Skip to content

Commit aa92586

Browse files
konardclaude
andcommitted
Add top place number to VK bot info command
- Added get_user_top_position method to DataBuilder to calculate user's ranking position - Updated build_info_message to include top position when karma is enabled in group chats - Modified info_message method to pass required parameters for position calculation - Added experiments folder with test scripts to verify functionality - Position is only shown in group chats (peer_id > 2e9) when karma is enabled 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent f7f6524 commit aa92586

File tree

5 files changed

+206
-6
lines changed

5 files changed

+206
-6
lines changed

experiments/test_logic.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Simple test to verify the logic of top position calculation.
4+
This test mimics the logic without importing the actual modules.
5+
"""
6+
7+
def calculate_real_karma(user, POSITIVE_VOTES_PER_KARMA=3, NEGATIVE_VOTES_PER_KARMA=2):
8+
"""Mock implementation of calculate_real_karma"""
9+
base_karma = user["karma"]
10+
up_votes = len(user["supporters"]) / POSITIVE_VOTES_PER_KARMA
11+
down_votes = len(user["opponents"]) / NEGATIVE_VOTES_PER_KARMA
12+
return base_karma + up_votes - down_votes
13+
14+
def get_users_sorted_by_karma(mock_users):
15+
"""Mock implementation of get_users_sorted_by_karma"""
16+
# Filter to only chat members (all in our mock)
17+
users = mock_users.copy()
18+
19+
# Sort by real karma
20+
users.sort(key=lambda u: calculate_real_karma(u), reverse=True)
21+
return users
22+
23+
def get_user_top_position(user, mock_users):
24+
"""Mock implementation of get_user_top_position"""
25+
users = get_users_sorted_by_karma(mock_users)
26+
users = [u for u in users if
27+
(u["karma"] != 0) or
28+
("programming_languages" in u and len(u["programming_languages"]) > 0)]
29+
30+
user_uid = user["uid"]
31+
for index, ranked_user in enumerate(users):
32+
if ranked_user["uid"] == user_uid:
33+
return index + 1 # 1-based position
34+
return 0 # User not found in ranking
35+
36+
def test_top_position():
37+
"""Test the top position logic"""
38+
# Mock users with different karma values
39+
mock_users = [
40+
{"uid": 1, "karma": 100, "supporters": [], "opponents": [], "name": "User1", "programming_languages": ["Python"]},
41+
{"uid": 2, "karma": 80, "supporters": [101, 102], "opponents": [], "name": "User2", "programming_languages": ["JavaScript"]},
42+
{"uid": 3, "karma": 60, "supporters": [], "opponents": [201], "name": "User3", "programming_languages": ["Java"]},
43+
{"uid": 4, "karma": 40, "supporters": [301], "opponents": [401, 402, 403], "name": "User4", "programming_languages": ["C++"]},
44+
{"uid": 5, "karma": 20, "supporters": [], "opponents": [], "name": "User5", "programming_languages": ["Go"]},
45+
{"uid": 6, "karma": 0, "supporters": [], "opponents": [], "name": "User6", "programming_languages": []}, # Should be excluded
46+
]
47+
48+
print("Mock users with calculated real karma:")
49+
sorted_users = get_users_sorted_by_karma(mock_users)
50+
for i, user in enumerate(sorted_users):
51+
real_karma = calculate_real_karma(user)
52+
print(f" {i+1}. {user['name']} (uid: {user['uid']}) - karma: {user['karma']}, real karma: {real_karma:.1f}")
53+
54+
print("\nFiltered users for ranking (excluding users with 0 karma and no languages):")
55+
filtered_users = [u for u in sorted_users if
56+
(u["karma"] != 0) or
57+
("programming_languages" in u and len(u["programming_languages"]) > 0)]
58+
for i, user in enumerate(filtered_users):
59+
print(f" {i+1}. {user['name']} (uid: {user['uid']})")
60+
61+
print("\nTesting position calculation:")
62+
for user in mock_users:
63+
position = get_user_top_position(user, mock_users)
64+
print(f" {user['name']} (uid: {user['uid']}) -> position: {position}")
65+
66+
if __name__ == "__main__":
67+
test_top_position()

experiments/test_top_position.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test script to verify the top position functionality works correctly.
4+
"""
5+
import sys
6+
import os
7+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
8+
9+
# Mock the necessary dependencies for testing
10+
class MockBetterUser(dict):
11+
pass
12+
13+
class MockVk:
14+
def get_members_ids(self, peer_id):
15+
return [1, 2, 3, 4, 5] # Mock member IDs
16+
17+
class MockDataService:
18+
def get_user_property(self, user, prop):
19+
return user[prop]
20+
21+
def get_user_sorted_programming_languages(self, user):
22+
return user.get("programming_languages", [])
23+
24+
def get_users(self, other_keys=None, sort_key=None, reverse_sort=True):
25+
# Mock users data with different karma values
26+
mock_users = [
27+
{"uid": 1, "karma": 100, "supporters": [], "opponents": [], "name": "User1", "programming_languages": ["Python"], "github_profile": "user1"},
28+
{"uid": 2, "karma": 80, "supporters": [], "opponents": [], "name": "User2", "programming_languages": ["JavaScript"], "github_profile": "user2"},
29+
{"uid": 3, "karma": 60, "supporters": [], "opponents": [], "name": "User3", "programming_languages": ["Java"], "github_profile": "user3"},
30+
{"uid": 4, "karma": 40, "supporters": [], "opponents": [], "name": "User4", "programming_languages": ["C++"], "github_profile": "user4"},
31+
{"uid": 5, "karma": 20, "supporters": [], "opponents": [], "name": "User5", "programming_languages": ["Go"], "github_profile": "user5"},
32+
]
33+
34+
if sort_key:
35+
mock_users.sort(key=sort_key, reverse=reverse_sort)
36+
37+
return mock_users
38+
39+
# Import the modules after setting up mocks
40+
from modules.data_builder import DataBuilder
41+
from modules.commands_builder import CommandsBuilder
42+
43+
def test_get_user_top_position():
44+
"""Test that the get_user_top_position function works correctly"""
45+
vk_instance = MockVk()
46+
data_service = MockDataService()
47+
48+
# Test user with highest karma (should be position 1)
49+
user1 = MockBetterUser({"uid": 1, "karma": 100, "supporters": [], "opponents": [], "name": "User1", "programming_languages": ["Python"], "github_profile": "user1"})
50+
position1 = DataBuilder.get_user_top_position(user1, vk_instance, data_service, 2000000001)
51+
print(f"User1 (karma 100) position: {position1}")
52+
53+
# Test user with middle karma (should be position 3)
54+
user3 = MockBetterUser({"uid": 3, "karma": 60, "supporters": [], "opponents": [], "name": "User3", "programming_languages": ["Java"], "github_profile": "user3"})
55+
position3 = DataBuilder.get_user_top_position(user3, vk_instance, data_service, 2000000001)
56+
print(f"User3 (karma 60) position: {position3}")
57+
58+
# Test user with lowest karma (should be position 5)
59+
user5 = MockBetterUser({"uid": 5, "karma": 20, "supporters": [], "opponents": [], "name": "User5", "programming_languages": ["Go"], "github_profile": "user5"})
60+
position5 = DataBuilder.get_user_top_position(user5, vk_instance, data_service, 2000000001)
61+
print(f"User5 (karma 20) position: {position5}")
62+
63+
# Test non-existent user (should be position 0)
64+
user_fake = MockBetterUser({"uid": 999, "karma": 50, "supporters": [], "opponents": [], "name": "FakeUser", "programming_languages": ["Python"], "github_profile": "fake"})
65+
position_fake = DataBuilder.get_user_top_position(user_fake, vk_instance, data_service, 2000000001)
66+
print(f"Fake user position: {position_fake}")
67+
68+
def test_build_info_message():
69+
"""Test that the build_info_message includes top position"""
70+
vk_instance = MockVk()
71+
data_service = MockDataService()
72+
73+
user2 = MockBetterUser({"uid": 2, "karma": 80, "supporters": [], "opponents": [], "name": "User2", "programming_languages": ["JavaScript"], "github_profile": "user2"})
74+
75+
# Test with karma enabled and group chat
76+
message = CommandsBuilder.build_info_message(
77+
user2, data_service, 2, True, vk_instance, 2000000001
78+
)
79+
print("Info message with top position:")
80+
print(message)
81+
print()
82+
83+
# Test without karma
84+
message_no_karma = CommandsBuilder.build_info_message(
85+
user2, data_service, 2, False, vk_instance, 2000000001
86+
)
87+
print("Info message without karma:")
88+
print(message_no_karma)
89+
90+
if __name__ == "__main__":
91+
print("Testing top position functionality...")
92+
test_get_user_top_position()
93+
print()
94+
test_build_info_message()
95+
print("All tests completed!")

python/modules/commands.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def info_message(self) -> NoReturn:
5656
"""Sends user info"""
5757
self.vk_instance.send_msg(
5858
CommandsBuilder.build_info_message(
59-
self.user, self.data_service, self.from_id, self.karma_enabled),
59+
self.user, self.data_service, self.from_id, self.karma_enabled,
60+
self.vk_instance, self.peer_id),
6061
self.peer_id)
6162

6263
def update_command(self) -> NoReturn:

python/modules/commands_builder.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,38 @@ def build_info_message(
3636
user: BetterUser,
3737
data: BetterBotBaseDataService,
3838
from_id: int,
39-
karma: bool
39+
karma: bool,
40+
vk_instance=None,
41+
peer_id: int = 0
4042
) -> str:
4143
"""Builds info message.
4244
4345
Arguments:
4446
- {user} - selected user;
4547
- {data} - data service;
46-
- {peer_id} - chat ID;
47-
- {karma} - is karma enabled in chat.
48+
- {from_id} - user ID requesting info;
49+
- {karma} - is karma enabled in chat;
50+
- {vk_instance} - VK instance for getting top position;
51+
- {peer_id} - chat ID for getting top position.
4852
"""
4953
programming_languages_string = DataBuilder.build_programming_languages(user, data)
5054
profile = DataBuilder.build_github_profile(user, data, default="отсутствует")
5155
mention = f"[id{data.get_user_property(user, 'uid')}|{data.get_user_property(user, 'name')}]"
5256
is_self = data.get_user_property(user, 'uid') == from_id
5357
karma_str: str = ""
58+
59+
# Get top position if in group chat and karma enabled
60+
top_position_str = ""
61+
if karma and peer_id > 2e9 and vk_instance:
62+
position = DataBuilder.get_user_top_position(user, vk_instance, data, peer_id)
63+
if position > 0:
64+
top_position_str = f" (место в топе: {position})"
65+
5466
if karma:
5567
if is_self:
56-
karma_str = f"{mention}, Ваша карма - {DataBuilder.build_karma(user, data)}.\n"
68+
karma_str = f"{mention}, Ваша карма - {DataBuilder.build_karma(user, data)}{top_position_str}.\n"
5769
else:
58-
karma_str = f"Карма {mention} - {DataBuilder.build_karma(user, data)}.\n"
70+
karma_str = f"Карма {mention} - {DataBuilder.build_karma(user, data)}{top_position_str}.\n"
5971
else:
6072
karma_str = f"{mention}.\n"
6173
return (f"{karma_str}"

python/modules/data_builder.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,31 @@ def get_users_sorted_by_name(
8888
users.reverse()
8989
return users
9090

91+
@staticmethod
92+
def get_user_top_position(
93+
user: BetterUser,
94+
vk_instance: Vk,
95+
data: BetterBotBaseDataService,
96+
peer_id: int
97+
) -> int:
98+
"""Gets user's position in the top ranking (1-based index).
99+
100+
Returns:
101+
- Position number (1 for first place, 2 for second, etc.)
102+
- 0 if user is not found in ranking
103+
"""
104+
users = DataBuilder.get_users_sorted_by_karma(vk_instance, data, peer_id)
105+
users = [u for u in users if
106+
(u["karma"] != 0) or
107+
("programming_languages" in u and len(u["programming_languages"]) > 0)
108+
]
109+
110+
user_uid = user["uid"]
111+
for index, ranked_user in enumerate(users):
112+
if ranked_user["uid"] == user_uid:
113+
return index + 1 # 1-based position
114+
return 0 # User not found in ranking
115+
91116
@staticmethod
92117
def calculate_real_karma(
93118
user: BetterUser,

0 commit comments

Comments
 (0)