diff --git a/bot/src/bot_data/abc/user_repository_abc.py b/bot/src/bot_data/abc/user_repository_abc.py index 0e3ced21..b5c7051d 100644 --- a/bot/src/bot_data/abc/user_repository_abc.py +++ b/bot/src/bot_data/abc/user_repository_abc.py @@ -31,6 +31,10 @@ class UserRepositoryABC(ABC): def get_users_by_server_id(self, server_id: int) -> List[User]: pass + @abstractmethod + def get_users_with_activity_by_server_id(self, server_id: int) -> List[User]: + pass + @abstractmethod def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: pass diff --git a/bot/src/bot_data/model/user.py b/bot/src/bot_data/model/user.py index dee1e355..bfb65ae6 100644 --- a/bot/src/bot_data/model/user.py +++ b/bot/src/bot_data/model/user.py @@ -32,6 +32,8 @@ class User(TableABC): self._birthday = birthday self._server = server + self._activity = 0 + TableABC.__init__(self) self._created_at = created_at if created_at is not None else self._created_at self._modified_at = modified_at if modified_at is not None else self._modified_at @@ -120,6 +122,14 @@ class User(TableABC): return 0 return float(result) + @property + def activity(self) -> int: + return self._activity + + @activity.setter + def activity(self, value: int): + self._activity = value + @property @ServiceProviderABC.inject def level(self, services: ServiceProviderABC) -> Level: diff --git a/bot/src/bot_data/service/user_repository_service.py b/bot/src/bot_data/service/user_repository_service.py index 90e501c7..6a5a00bc 100644 --- a/bot/src/bot_data/service/user_repository_service.py +++ b/bot/src/bot_data/service/user_repository_service.py @@ -1,6 +1,8 @@ +import datetime from typing import Optional from cpl_core.database.context import DatabaseContextABC +from cpl_core.type import T, R from cpl_query.extension import List from bot_core.logging.database_logger import DatabaseLogger @@ -38,7 +40,9 @@ class UserRepositoryService(UserRepositoryABC): def get_users(self) -> List[User]: self._logger.trace(__name__, f"Send SQL command: {User.get_select_all_string()}") - return List(User, [self._from_result(user) for user in self._context.select(User.get_select_all_string())]) + return List( + User, [self._from_result(user) for user in self._context.select(User.get_select_all_string())] + ).for_each(lambda x: self._set_user_activity(x)) def get_user_by_id(self, id: int) -> User: self._logger.trace(__name__, f"Send SQL command: {User.get_select_by_id_string(id)}") @@ -77,6 +81,16 @@ class UserRepositoryService(UserRepositoryABC): [self._from_result(user) for user in self._context.select(User.get_select_by_server_id_string(server_id))], ) + def get_users_with_activity_by_server_id(self, server_id: int) -> List[User]: + self._logger.trace( + __name__, + f"Send SQL command: {User.get_select_by_server_id_string(server_id)}", + ) + return List( + User, + [self._from_result(user) for user in self._context.select(User.get_select_by_server_id_string(server_id))], + ).for_each(lambda x: self._set_user_activity(x)) + def get_user_by_discord_id_and_server_id(self, discord_id: int, server_id: int) -> User: self._logger.trace( __name__, @@ -109,3 +123,91 @@ class UserRepositoryService(UserRepositoryABC): def delete_user(self, user: User): self._logger.trace(__name__, f"Send SQL command: {user.delete_string}") self._context.cursor.execute(user.delete_string) + + def _set_user_activity(self, user): + days = (datetime.date.today() - (datetime.date.today() - datetime.timedelta(days=7))).days + query = f""" + SELECT ( + ( + SELECT Count(UserGotAchievements.CreatedAt) + FROM UserGotAchievements + INNER JOIN Achievements ON UserGotAchievements.AchievementId = Achievements.Id + INNER JOIN Users ON UserGotAchievements.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserGotAchievements.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + + ( + SELECT SUM( + UserMessageCountPerHour.XPCount / ( + SELECT XpPerMessage + FROM CFG_Server + WHERE ServerId = {user.server.id} + ) + ) + FROM UserMessageCountPerHour + INNER JOIN Users ON UserMessageCountPerHour.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserMessageCountPerHour.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + + ( + SELECT Count(UserJoinedVoiceChannel.CreatedAt) + FROM UserJoinedVoiceChannel + INNER JOIN Users ON UserJoinedVoiceChannel.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserJoinedVoiceChannel.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + + ( + SELECT IFNULL(ROUND(SUM(TIME_TO_SEC( + TIMEDIFF(UserJoinedVoiceChannel.LeavedOn, UserJoinedVoiceChannel.JoinedOn) + ) / 3600), 2), 0) + FROM UserJoinedVoiceChannel + INNER JOIN Users ON UserJoinedVoiceChannel.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserJoinedVoiceChannel.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + + ( + SELECT Count(UserJoinedGameServer.CreatedAt) + FROM UserJoinedGameServer + INNER JOIN Users ON UserJoinedGameServer.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserJoinedGameServer.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + + ( + SELECT IFNULL(ROUND(SUM(TIME_TO_SEC( + TIMEDIFF(UserJoinedGameServer.LeavedOn, UserJoinedGameServer.JoinedOn) + ) / 3600), 2), 0) + FROM UserJoinedGameServer + INNER JOIN Users ON UserJoinedGameServer.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserJoinedGameServer.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) - + ( + SELECT COUNT(UserWarnings.CreatedAt) + FROM UserWarnings + INNER JOIN Users ON UserWarnings.UserId = Users.UserId + WHERE Users.ServerId = {user.server.id} + AND Users.UserId = {user.id} + AND UserWarnings.CreatedAt >= "{datetime.date.today() - datetime.timedelta(days=7)}" + ) + ) / {days} * 1000 as count; + """ + user.activity = self._cast_query_result(query, int) + + def _cast_query_result(self, query: str, r_type: T) -> Optional[R]: + results = self._context.select(query) + if len(results) == 0 or len(results[0]) == 0: + return None + result = results[0][0] + default = None + if r_type is int or r_type is float: + default = 0 + elif r_type is str: + default = "" + + return r_type(result) if result is not None else default diff --git a/bot/src/bot_graphql/abc/query_abc.py b/bot/src/bot_graphql/abc/query_abc.py index d66fd516..cfd18ac6 100644 --- a/bot/src/bot_graphql/abc/query_abc.py +++ b/bot/src/bot_graphql/abc/query_abc.py @@ -64,11 +64,9 @@ class QueryABC(ObjectType): if user == "system" or user.auth_role == AuthRoleEnum.admin: return self._resolve_collection(collection, *args, **kwargs) - for x in collection.to_list(): - if not self._can_user_see_element(user, x): - collection.remove(x) - - return self._resolve_collection(collection, *args, **kwargs) + return self._resolve_collection( + collection.where(lambda x: self._can_user_see_element(user, x)), *args, **kwargs + ) self.set_field(f"{name}s", wrapper) self.set_field(f"{name}Count", lambda *args: wrapper(*args).count()) diff --git a/bot/src/bot_graphql/graphql/user.gql b/bot/src/bot_graphql/graphql/user.gql index f7d2b990..e4ed9ea1 100644 --- a/bot/src/bot_graphql/graphql/user.gql +++ b/bot/src/bot_graphql/graphql/user.gql @@ -9,6 +9,7 @@ type User implements TableWithHistoryQuery { ontime: Float gameOntime: Float level: Level + activityScore: Int joinedServers(filter: UserJoinedServerFilter, page: Page, sort: Sort): [UserJoinedServer] joinedServerCount: Int diff --git a/bot/src/bot_graphql/queries/server_query.py b/bot/src/bot_graphql/queries/server_query.py index eac863b9..9b8b9cbf 100644 --- a/bot/src/bot_graphql/queries/server_query.py +++ b/bot/src/bot_graphql/queries/server_query.py @@ -86,7 +86,7 @@ class ServerQuery(DataQueryWithHistoryABC): ) self.add_collection( "user", - lambda server, *_: self._users.get_users_by_server_id(server.id), + lambda server, *_: self._users.get_users_with_activity_by_server_id(server.id), UserFilter, ) self.add_collection( diff --git a/bot/src/bot_graphql/queries/server_statistic_query.py b/bot/src/bot_graphql/queries/server_statistic_query.py index e511dfdb..1dd82b41 100644 --- a/bot/src/bot_graphql/queries/server_statistic_query.py +++ b/bot/src/bot_graphql/queries/server_statistic_query.py @@ -114,7 +114,9 @@ class ServerStatisticQuery(QueryABC): def _resolve_voice_channel_ontime(self, server, *_): query = f""" - SELECT ROUND(SUM(TIME_TO_SEC(TIMEDIFF(UserJoinedVoiceChannel.LeavedOn, UserJoinedVoiceChannel.JoinedOn)) / 3600),{server.server.id}) FROM UserJoinedVoiceChannel + SELECT IFNULL(ROUND(SUM( + TIME_TO_SEC(TIMEDIFF(UserJoinedVoiceChannel.LeavedOn, UserJoinedVoiceChannel.JoinedOn)) / 3600 + ), 2), 0) FROM UserJoinedVoiceChannel INNER JOIN Users ON UserJoinedVoiceChannel.UserId = Users.UserId WHERE Users.ServerId = {server.server.id} AND UserJoinedVoiceChannel.CreatedAt >= "{self._get_date(**server.kwargs)}"; @@ -132,7 +134,10 @@ class ServerStatisticQuery(QueryABC): def _resolve_game_server_ontime(self, server, *_): query = f""" - SELECT ROUND(SUM(TIME_TO_SEC(TIMEDIFF(UserJoinedGameServer.LeavedOn, UserJoinedGameServer.JoinedOn)) / 3600),2) FROM UserJoinedGameServer + SELECT IFNULL(ROUND(SUM( + TIME_TO_SEC(TIMEDIFF(UserJoinedGameServer.LeavedOn, UserJoinedGameServer.JoinedOn)) / 3600 + ), 2), 0) + FROM UserJoinedGameServer INNER JOIN Users ON UserJoinedGameServer.UserId = Users.UserId WHERE Users.ServerId = {server.server.id} AND UserJoinedGameServer.CreatedAt >= "{self._get_date(**server.kwargs)}"; diff --git a/bot/src/bot_graphql/queries/user_query.py b/bot/src/bot_graphql/queries/user_query.py index 219a1526..1d31312f 100644 --- a/bot/src/bot_graphql/queries/user_query.py +++ b/bot/src/bot_graphql/queries/user_query.py @@ -59,6 +59,7 @@ class UserQuery(DataQueryWithHistoryABC): self.set_field("ontime", lambda user, *_: user.ontime) self.set_field("gameOntime", lambda user, *_: user.game_ontime) self.set_field("level", lambda user, *_: user.level) + self.set_field("activityScore", lambda user, *_: user.activity) self.add_collection( "joinedServer", lambda user, *_: self._ujs.get_user_joined_servers_by_user_id(user.id), diff --git a/web/src/app/models/data/user.model.ts b/web/src/app/models/data/user.model.ts index 37c00950..f7ae3454 100644 --- a/web/src/app/models/data/user.model.ts +++ b/web/src/app/models/data/user.model.ts @@ -18,6 +18,7 @@ export interface User extends DataWithHistory { ontime?: number; gameOntime?: number; level?: Level; + activityScore?: number; server?: Server; leftServer?: boolean; diff --git a/web/src/app/models/graphql/queries.model.ts b/web/src/app/models/graphql/queries.model.ts index bf803943..2db56afa 100644 --- a/web/src/app/models/graphql/queries.model.ts +++ b/web/src/app/models/graphql/queries.model.ts @@ -362,6 +362,7 @@ export class Queries { xp ontime gameOntime + activityScore level { id name @@ -388,6 +389,7 @@ export class Queries { birthday ontime gameOntime + activityScore level { id name diff --git a/web/src/app/modules/view/server/members/members.component.html b/web/src/app/modules/view/server/members/members.component.html index b633ae05..da87353e 100644 --- a/web/src/app/modules/view/server/members/members.component.html +++ b/web/src/app/modules/view/server/members/members.component.html @@ -74,6 +74,13 @@ + +
+
{{'common.activity' | translate}}
+ +
+ +
{{'common.left_server' | translate}}
@@ -127,6 +134,7 @@ +
+ + {{'common.activity' | translate}}: + + + {{member.activityScore}} + + + {{member.activityScore}} + + + {{'common.left_server' | translate}}: diff --git a/web/src/app/modules/view/server/members/members.component.ts b/web/src/app/modules/view/server/members/members.component.ts index c9d23f1d..ded4f2a4 100644 --- a/web/src/app/modules/view/server/members/members.component.ts +++ b/web/src/app/modules/view/server/members/members.component.ts @@ -89,7 +89,7 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe private data: DataService, private route: ActivatedRoute ) { - super("member", ["id", "discord_id", "name", "xp", "ontime", "game_ontime", "left_server", "level"]); + super("member", ["id", "discord_id", "name", "xp", "ontime", "game_ontime", "activity", "left_server", "level"]); } ngOnInit(): void { diff --git a/web/src/app/modules/view/server/profile/profile.component.html b/web/src/app/modules/view/server/profile/profile.component.html index 80e27d0b..ea14ad09 100644 --- a/web/src/app/modules/view/server/profile/profile.component.html +++ b/web/src/app/modules/view/server/profile/profile.component.html @@ -75,6 +75,13 @@
+
+
+
{{'common.activity' | translate}}:
+
{{user.activityScore}}
+
+
+
{{'view.server.profile.level' | translate}}:
diff --git a/web/src/assets/i18n/de.json b/web/src/assets/i18n/de.json index 9c5fec08..e31ade5d 100644 --- a/web/src/assets/i18n/de.json +++ b/web/src/assets/i18n/de.json @@ -127,6 +127,7 @@ "abort": "Abbrechen", "actions": "Aktionen", "active": "Aktiv", + "activity": "Aktivität", "add": "Hinzufügen", "attribute": "Attribut", "auth_role": "Rolle", diff --git a/web/src/assets/i18n/en.json b/web/src/assets/i18n/en.json index dbfd4101..49b1b59d 100644 --- a/web/src/assets/i18n/en.json +++ b/web/src/assets/i18n/en.json @@ -127,6 +127,7 @@ "abort": "Abort", "actions": "Actions", "active": "Active", + "activity": "Activity", "add": "Add", "attribute": "Attribute", "auth_role": "Role",