Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions experiments/test_intermediate_votes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Simple test script to validate intermediate votes functionality
"""

# Mock user data structure
class MockUser(dict):
def __init__(self, uid, supporters=None, opponents=None, karma=0, name="Test User"):
super().__init__()
self['uid'] = uid
self['supporters'] = supporters or []
self['opponents'] = opponents or []
self['karma'] = karma
self['name'] = name
self['programming_languages'] = []

# Mock config values (from the actual config)
POSITIVE_VOTES_PER_KARMA = 2
NEGATIVE_VOTES_PER_KARMA = 3

def calculate_intermediate_votes(user):
"""Calculate only intermediate votes (pending supporters/opponents)"""
up_votes = len(user["supporters"]) / POSITIVE_VOTES_PER_KARMA
down_votes = len(user["opponents"]) / NEGATIVE_VOTES_PER_KARMA
return up_votes - down_votes

def test_intermediate_votes_calculation():
print("Testing intermediate votes calculation...")

# User with no pending votes
user1 = MockUser(1, supporters=[], opponents=[], karma=100)
intermediate1 = calculate_intermediate_votes(user1)
print(f"User 1 (no votes): {intermediate1} (expected: 0.0)")
assert intermediate1 == 0.0, f"Expected 0.0, got {intermediate1}"

# User with 2 supporters (should give +1.0 intermediate votes)
user2 = MockUser(2, supporters=[3, 4], opponents=[], karma=50)
intermediate2 = calculate_intermediate_votes(user2)
print(f"User 2 (2 supporters): {intermediate2} (expected: 1.0)")
assert intermediate2 == 1.0, f"Expected 1.0, got {intermediate2}"

# User with 3 opponents (should give -1.0 intermediate votes)
user3 = MockUser(3, supporters=[], opponents=[1, 2, 4], karma=25)
intermediate3 = calculate_intermediate_votes(user3)
print(f"User 3 (3 opponents): {intermediate3} (expected: -1.0)")
assert intermediate3 == -1.0, f"Expected -1.0, got {intermediate3}"

# User with mixed votes
user4 = MockUser(4, supporters=[1, 5], opponents=[2, 6, 7], karma=75)
intermediate4 = calculate_intermediate_votes(user4)
expected4 = 2/POSITIVE_VOTES_PER_KARMA - 3/NEGATIVE_VOTES_PER_KARMA # 1.0 - 1.0 = 0.0
print(f"User 4 (mixed votes): {intermediate4} (expected: {expected4})")
assert intermediate4 == expected4, f"Expected {expected4}, got {intermediate4}"

print("All intermediate votes calculations passed!")

def test_sorting_by_intermediate_votes():
print("\nTesting sorting by intermediate votes...")

users = [
MockUser(1, supporters=[], opponents=[]), # 0.0
MockUser(2, supporters=[3, 4], opponents=[]), # +1.0
MockUser(3, supporters=[], opponents=[1, 2, 5]), # -1.0
MockUser(4, supporters=[1, 2, 3, 4], opponents=[]), # +2.0
MockUser(5, supporters=[1], opponents=[2, 3]), # +0.5 - 0.67 = -0.17
]

# Sort by intermediate votes (descending)
users_sorted = sorted(users, key=calculate_intermediate_votes, reverse=True)

print("Users sorted by intermediate votes (highest to lowest):")
for user in users_sorted:
intermediate = calculate_intermediate_votes(user)
print(f" User {user['uid']}: {intermediate}")

# Verify order
expected_order = [4, 2, 1, 5, 3] # Based on intermediate votes
actual_order = [user['uid'] for user in users_sorted]
print(f"Expected order: {expected_order}")
print(f"Actual order: {actual_order}")

assert actual_order == expected_order, f"Expected {expected_order}, got {actual_order}"
print("Sorting test passed!")

if __name__ == "__main__":
test_intermediate_votes_calculation()
test_sorting_by_intermediate_votes()
print("\n✅ All tests passed! The intermediate votes functionality is working correctly.")
3 changes: 3 additions & 0 deletions python/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def __init__(
(patterns.PEOPLE_LANGUAGES, self.commands.top_langs),
(patterns.BOTTOM_LANGUAGES,
lambda: self.commands.top_langs(True)),
(patterns.TOP_VOTES, self.commands.top_votes),
(patterns.BOTTOM_VOTES,
lambda: self.commands.top_votes(True)),
(patterns.WHAT_IS, self.commands.what_is),
(patterns.WHAT_MEAN, self.commands.what_is),
(patterns.APPLY_KARMA, self.commands.apply_karma),
Expand Down
21 changes: 21 additions & 0 deletions python/modules/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ def top_langs(
self.vk_instance.send_msg(built, self.peer_id)
return

def top_votes(
self,
reverse: bool = False
) -> NoReturn:
"""Sends users top by intermediate votes."""
if self.peer_id < 2e9:
return
maximum_users = self.matched.group("maximum_users")
maximum_users = int(maximum_users) if maximum_users else -1
users = DataBuilder.get_users_sorted_by_intermediate_votes(
self.vk_instance, self.data_service, self.peer_id, not reverse)
users = [i for i in users if
(len(i["supporters"]) > 0 or len(i["opponents"]) > 0) or
("programming_languages" in i and len(i["programming_languages"]) > 0)
]
self.vk_instance.send_msg(
CommandsBuilder.build_top_users(
users, self.data_service, reverse,
self.karma_enabled, maximum_users),
self.peer_id)

def apply_karma(self) -> NoReturn:
"""Changes user karma."""
if self.peer_id < 2e9 or not self.karma_enabled or not self.matched or self.is_bot_selected:
Expand Down
29 changes: 29 additions & 0 deletions python/modules/data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,35 @@ def get_users_sorted_by_name(
users.reverse()
return users

@staticmethod
def calculate_intermediate_votes(
user: BetterUser,
data: BetterBotBaseDataService
) -> float:
"""Calculate only intermediate votes (pending supporters/opponents)"""
up_votes = len(user["supporters"])/config.POSITIVE_VOTES_PER_KARMA
down_votes = len(user["opponents"])/config.NEGATIVE_VOTES_PER_KARMA
return up_votes - down_votes

@staticmethod
def get_users_sorted_by_intermediate_votes(
vk_instance: Vk,
data: BetterBotBaseDataService,
peer_id: int,
reverse_sort: bool = True
) -> List[BetterUser]:
"""Get users sorted by intermediate votes only (pending supporters/opponents)"""
members = vk_instance.get_members_ids(peer_id)
users = data.get_users(
other_keys=[
"karma", "name", "programming_languages",
"supporters", "opponents", "github_profile", "uid"],
sort_key=lambda u: DataBuilder.calculate_intermediate_votes(u, data),
reverse_sort=reverse_sort)
if members:
users = [u for u in users if u["uid"] in members]
return users

@staticmethod
def calculate_real_karma(
user: BetterUser,
Expand Down
6 changes: 6 additions & 0 deletions python/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
r'\A\s*(люди|народ|people)\s*(?P<languages>(' + DEFAULT_LANGUAGES +
r')(\s+(' + DEFAULT_LANGUAGES + r'))*)\s*\Z', IGNORECASE)

TOP_VOTES = recompile(
r'\A\s*(топ голоса|топ голосов|top votes|votes top|голоса)\s*(?P<maximum_users>\d+)?\s*\Z', IGNORECASE)

BOTTOM_VOTES = recompile(
r'\A\s*(низ голоса|низ голосов|bottom votes|votes bottom)\s*(?P<maximum_users>\d+)?\s*\Z', IGNORECASE)

WHAT_IS = recompile(
r'\A\s*(what is|что такое|що таке)\s+(?P<question>[\S\s]+?)\??\s*\Z', IGNORECASE)

Expand Down
32 changes: 32 additions & 0 deletions python/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ def test_build_karma(
print(DataBuilder.build_karma(user_1, db))
print(DataBuilder.build_karma(user_2, db))

@ordered
def test_calculate_intermediate_votes(
self
) -> NoReturn:
user_1 = db.get_user(1)
user_2 = db.get_user(2)

# user_2 should have supporters from previous test
intermediate_votes_1 = DataBuilder.calculate_intermediate_votes(user_1, db)
intermediate_votes_2 = DataBuilder.calculate_intermediate_votes(user_2, db)

print()
print(f"User 1 intermediate votes: {intermediate_votes_1}")
print(f"User 2 intermediate votes: {intermediate_votes_2}")

assert intermediate_votes_1 >= 0 or intermediate_votes_1 < 0 # Just ensure it returns a number
assert intermediate_votes_2 >= 0 or intermediate_votes_2 < 0 # Just ensure it returns a number


class Test3Commands(TestCase):
commands = Commands(VkInstance(), BetterBotBaseDataService("test_db"))
Expand Down Expand Up @@ -222,6 +240,20 @@ def test_apply_karma_change(
self.commands.apply_karma_change('-', 6)
self.commands.karma_message()

@ordered
def test_top_votes(
self
) -> NoReturn:
self.commands.msg = 'top votes'
self.commands.match_command(patterns.TOP_VOTES)
self.commands.top_votes()
self.commands.top_votes(True)

self.commands.msg = 'bottom votes'
self.commands.match_command(patterns.BOTTOM_VOTES)
self.commands.top_votes()
self.commands.top_votes(True)


if __name__ == '__main__':
db = BetterBotBaseDataService("test_db")
Expand Down
Loading