Merge pull request 'Added user activity score #451' (#452) from dev into staging
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m18s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m18s
				
			Reviewed-on: #452
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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)}"; | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -18,6 +18,7 @@ export interface User extends DataWithHistory { | ||||
|   ontime?: number; | ||||
|   gameOntime?: number; | ||||
|   level?: Level; | ||||
|   activityScore?: number; | ||||
|   server?: Server; | ||||
|   leftServer?: boolean; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -74,6 +74,13 @@ | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th hideable-th="activity" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.activity' | translate}}</div> | ||||
|               <p-sortIcon field="activityScore" class="table-header-icon"></p-sortIcon> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th hideable-th="left_server" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.left_server' | translate}}</div> | ||||
| @@ -127,6 +134,7 @@ | ||||
|           <th hideable-th="xp" [parent]="this"></th> | ||||
|           <th hideable-th="ontime" [parent]="this"></th> | ||||
|           <th hideable-th="game_ontime" [parent]="this"></th> | ||||
|           <th hideable-th="activity" [parent]="this"></th> | ||||
|           <th hideable-th="left_server" [parent]="this" class="table-header-small-dropdown"> | ||||
|             <form [formGroup]="filterForm"> | ||||
|               <p-dropdown formControlName="leftServer" [options]="leftServerOptions" | ||||
| @@ -213,6 +221,17 @@ | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|           <td hideable-th="activity" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.activity' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 {{member.activityScore}} | ||||
|               </ng-template> | ||||
|               <ng-template pTemplate="output"> | ||||
|                 {{member.activityScore}} | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|           <td hideable-th="left_server" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.left_server' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -75,6 +75,13 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="content-row"> | ||||
|       <div class="content-column"> | ||||
|         <div class="content-data-name">{{'common.activity' | translate}}:</div> | ||||
|         <div class="content-data-value">{{user.activityScore}}</div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="content-row"> | ||||
|       <div class="content-column"> | ||||
|         <div class="content-data-name">{{'view.server.profile.level' | translate}}:</div> | ||||
|   | ||||
| @@ -127,6 +127,7 @@ | ||||
|     "abort": "Abbrechen", | ||||
|     "actions": "Aktionen", | ||||
|     "active": "Aktiv", | ||||
|     "activity": "Aktivität", | ||||
|     "add": "Hinzufügen", | ||||
|     "attribute": "Attribut", | ||||
|     "auth_role": "Rolle", | ||||
|   | ||||
| @@ -127,6 +127,7 @@ | ||||
|     "abort": "Abort", | ||||
|     "actions": "Actions", | ||||
|     "active": "Active", | ||||
|     "activity": "Activity", | ||||
|     "add": "Add", | ||||
|     "attribute": "Attribute", | ||||
|     "auth_role": "Role", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user