Merge pull request 'dev' (#422) from dev into staging
All checks were successful
Deploy staging on push / on-push-deploy_sh-edraft (push) Successful in 3m22s

Reviewed-on: #422
This commit is contained in:
Sven Heidemann 2023-11-05 17:07:56 +01:00
commit 0a5f23f1af
18 changed files with 329 additions and 7 deletions

View File

@ -38,6 +38,8 @@ type Server implements TableWithHistoryQuery {
config: ServerConfig
hasFeatureFlag(flag: String): FeatureFlag
statistic(date: String): ServerStatistic
createdAt: String
modifiedAt: String

View File

@ -0,0 +1,17 @@
type ServerStatistic {
achievementsAchieved: Int
messageCount: Int
userCount: Int
activeUserCount: Int
userJoinedVoiceChannelCount: Int
userJoinedVoiceChannelOntime: Int
userJoinedGameServerCount: Int
userJoinedGameServerOntime: Int
userWarningCount: Int
activityScore: Int
}

View File

@ -57,6 +57,7 @@ from bot_graphql.queries.level_query import LevelQuery
from bot_graphql.queries.server_config_query import ServerConfigQuery
from bot_graphql.queries.server_history_query import ServerHistoryQuery
from bot_graphql.queries.server_query import ServerQuery
from bot_graphql.queries.server_statistic_query import ServerStatisticQuery
from bot_graphql.queries.short_role_name_history_query import ShortRoleNameHistoryQuery
from bot_graphql.queries.short_role_name_query import ShortRoleNameQuery
from bot_graphql.queries.technician_config_history_query import (
@ -138,6 +139,7 @@ class GraphQLModule(ModuleABC):
services.add_transient(QueryABC, ShortRoleNameQuery)
services.add_transient(QueryABC, UserWarningHistoryQuery)
services.add_transient(QueryABC, UserWarningQuery)
services.add_transient(QueryABC, ServerStatisticQuery)
services.add_transient(QueryABC, DiscordQuery)
services.add_transient(QueryABC, GuildQuery)

View File

@ -0,0 +1,7 @@
from bot_data.model.server import Server
class ServerStatistics:
def __init__(self, server: Server, kwargs: dict):
self.server = server
self.kwargs = kwargs

View File

@ -26,6 +26,7 @@ from bot_graphql.filter.client_filter import ClientFilter
from bot_graphql.filter.level_filter import LevelFilter
from bot_graphql.filter.short_role_name_filter import ShortRoleNameFilter
from bot_graphql.filter.user_filter import UserFilter
from bot_graphql.model.server_statistics import ServerStatistics
class ServerQuery(DataQueryWithHistoryABC):
@ -107,6 +108,7 @@ class ServerQuery(DataQueryWithHistoryABC):
"hasFeatureFlag",
lambda server, *_, **kwargs: self._resolve_has_feature_flag(server, *_, **kwargs),
)
self.set_field("statistic", lambda server, *_, **kwargs: ServerStatistics(server, kwargs))
@staticmethod
def resolve_id(server: Server, *_):

View File

@ -0,0 +1,157 @@
import datetime
from cpl_core.configuration import ConfigurationABC
from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_message_count_per_hour_repository_abc import UserMessageCountPerHourRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC
from bot_data.model.server_config import ServerConfig
from bot_graphql.abc.query_abc import QueryABC
class ServerStatisticQuery(QueryABC):
def __init__(
self,
config: ConfigurationABC,
users: UserRepositoryABC,
user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC,
user_joined_game_servers: UserJoinedGameServerRepositoryABC,
user_messages: UserMessageCountPerHourRepositoryABC,
user_warnings: UserWarningsRepositoryABC,
achievements: AchievementRepositoryABC,
):
QueryABC.__init__(self, "ServerStatistic")
self._config = config
self._users = users
self._user_joined_voice_channels = user_joined_voice_channels
self._user_joined_game_servers = user_joined_game_servers
self._user_messages = user_messages
self._user_warnings = user_warnings
self._achievements = achievements
self.set_field(
"achievementsAchieved",
self._resolve_achievements,
)
self.set_field(
"messageCount",
self._resolve_message_count,
)
self.set_field("userCount", lambda server, *_: self._users.get_users_by_server_id(server.server.id).count())
self.set_field("activeUserCount", self._resolve_active_count)
self.set_field("userJoinedVoiceChannelCount", self._resolve_voice_channel_count)
self.set_field("userJoinedVoiceChannelOntime", self._resolve_voice_channel_ontime)
self.set_field("userJoinedGameServerCount", self._resolve_game_server_count)
self.set_field("userJoinedGameServerOntime", self._resolve_game_server_ontime)
self.set_field("userWarningCount", self._resolve_user_warning_count)
self.set_field("activityScore", self._resolve_activity_score)
def _resolve_activity_score(self, server, *_):
days = (datetime.date.today() - self._get_date(**server.kwargs)).days
return int(
(
(
self._resolve_achievements(server, *_)
+ self._resolve_message_count(server, *_)
+ self._resolve_voice_channel_count(server, *_)
+ self._resolve_voice_channel_ontime(server, *_)
+ self._resolve_game_server_count(server, *_)
+ self._resolve_game_server_ontime(server, *_)
- self._resolve_user_warning_count(server, *_)
)
/ self._resolve_active_count(server, *_)
)
/ days
* 1000
)
def _resolve_message_count(self, server, *_):
settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.server.discord_id}")
return (
self._user_messages.get_user_message_count_per_hours()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.sum(lambda x: x.xp_count / settings.xp_per_message)
)
def _resolve_active_count(self, server, *_):
return self._users.get_users_by_server_id(server.server.id).where(lambda x: not x.left_server).count()
def _resolve_voice_channel_count(self, server, *_):
return (
self._user_joined_voice_channels.get_user_joined_voice_channels()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.count()
)
def _resolve_voice_channel_ontime(self, server, *_):
return (
self._user_joined_voice_channels.get_user_joined_voice_channels()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.sum(lambda x: x.time)
)
def _resolve_game_server_count(self, server, *_):
return (
self._user_joined_game_servers.get_user_joined_game_servers()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.count()
)
def _resolve_game_server_ontime(self, server, *_):
return (
self._user_joined_game_servers.get_user_joined_game_servers()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.sum(lambda x: x.time)
)
def _resolve_user_warning_count(self, server, *_):
return (
self._user_warnings.get_user_warnings()
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.count()
)
def _resolve_achievements(self, server, *_):
return (
self._achievements.get_achievements_by_server_id(server.server.id)
.select_many(lambda x: self._achievements.get_user_got_achievements_by_achievement_id(x.id))
.where(
lambda x: x.user.server.id == server.server.id
and x.created_at.date() >= self._get_date(**server.kwargs)
)
.count()
)
def _get_date(self, **kwargs) -> datetime.date:
if "date" not in kwargs:
return datetime.date.today() - datetime.timedelta(days=7)
return datetime.datetime.strptime(kwargs["date"], "%d.%m.%Y").date()

View File

@ -186,7 +186,7 @@ class SteamOfferWatcher(TaskABC):
self._logger.trace(__name__, "Finished watching")
return offers_for_notifications
@tasks.loop(hours=1)
@tasks.loop(hours=4)
async def watch(self):
self._logger.info(__name__, "Watching steam special offers")
try:

View File

@ -20,6 +20,7 @@ export interface Server extends Data {
autoRoles?: AutoRole[];
clientCount?: number;
clients?: Client[];
statistic?: ServerStatistic;
levelCount?: number;
levels?: Level[];
userCount?: number;
@ -35,3 +36,16 @@ export interface ServerFilter {
discordId?: String;
name?: String;
}
export interface ServerStatistic {
achievementsAchieved?: Number
messageCount?: Number
userCount?: Number
activeUserCount?: Number
userJoinedVoiceChannelCount?: Number
userJoinedVoiceChannelOntime?: Number
userJoinedGameServerCount?: Number
userJoinedGameServerOntime?: Number
userWarningCount?: Number
activityScore?: Number
}

View File

@ -79,6 +79,18 @@ export class Queries {
receivedCommandCount
movedUsersCount
}
statistic {
achievementsAchieved
messageCount
userCount
activeUserCount
userJoinedVoiceChannelCount
userJoinedVoiceChannelOntime
userJoinedGameServerCount
userJoinedGameServerOntime
userWarningCount
activityScore
}
}
}
`;

View File

@ -0,0 +1,35 @@
<div class="client">
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.activity_score' | translate}}</label>
<label class="client-info-value">{{stats?.activityScore}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.achievements_achieved' | translate}}</label>
<label class="client-info-value">{{stats?.achievementsAchieved}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.message_count' | translate}}</label>
<label class="client-info-value">{{stats?.messageCount}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.user_joined_voice_channel_count' | translate}}</label>
<label class="client-info-value">{{stats?.userJoinedVoiceChannelCount}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.user_joined_voice_channel_ontime' | translate}}</label>
<label class="client-info-value">{{stats?.userJoinedVoiceChannelOntime}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.user_joined_game_server_count' | translate}}</label>
<label class="client-info-value">{{stats?.userJoinedGameServerCount}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.user_joined_game_server_ontime' | translate}}</label>
<label class="client-info-value">{{stats?.userJoinedGameServerOntime}}</label>
</div>
<div class="client-info client-info-small">
<label class="client-info-header">{{'view.server.dashboard.user_warning_count' | translate}}</label>
<label class="client-info-value">{{stats?.userWarningCount}}</label>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ServerStatisticComponent } from './server-statistic.component';
describe('ServerStatisticComponent', () => {
let component: ServerStatisticComponent;
let fixture: ComponentFixture<ServerStatisticComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ServerStatisticComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ServerStatisticComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,22 @@
import { Component, Input } from "@angular/core";
import { ServerStatistic } from "../../../../../../models/data/server.model";
@Component({
selector: 'app-server-statistic',
templateUrl: './server-statistic.component.html',
styleUrls: ['./server-statistic.component.scss']
})
export class ServerStatisticComponent {
@Input() stats?: ServerStatistic = {
achievementsAchieved: 0,
messageCount: 0,
userCount: 0,
activeUserCount: 0,
userJoinedVoiceChannelCount: 0,
userJoinedVoiceChannelOntime: 0,
userJoinedGameServerCount: 0,
userJoinedGameServerOntime: 0,
userWarningCount: 0,
activityScore: 0,
};
}

View File

@ -28,6 +28,13 @@
{{'view.dashboard.server.active_members' | translate}} {{server ? server.activeUserCount : ''}}
</div>
<div class="content-divider"></div>
<div class="client-data">
<app-server-statistic class="client-component" [stats]="server.statistic"></app-server-statistic>
</div>
<div class="content-divider"></div>
<div class="client-data"
*ngFor="let client of server?.clients">
<app-client class="client-component" [client]="client"></app-client>

View File

@ -6,6 +6,7 @@ import { SharedModule } from "../../shared/shared.module";
import { ProfileComponent } from "./profile/profile.component";
import { MembersComponent } from "./members/members.component";
import { ClientComponent } from "./server-dashboard/components/client/client.component";
import { ServerStatisticComponent } from './server-dashboard/components/server-statistic/server-statistic.component';
@NgModule({
@ -14,6 +15,7 @@ import { ClientComponent } from "./server-dashboard/components/client/client.com
ProfileComponent,
MembersComponent,
ClientComponent,
ServerStatisticComponent,
],
imports: [
CommonModule,

View File

@ -454,13 +454,21 @@
}
},
"dashboard": {
"achievements_achieved": "Errungenschaften vergeben",
"activity_score": "Aktivitätsbewertung",
"deleted_message_count": "Gelöschte Nachrichten",
"header": "Server dashboard",
"message_count": "Anzahl Nachrichten",
"moved_users_count": "Verschobene Benutzer",
"name": "Name",
"received_command_count": "Empfangene Befehle",
"received_message_count": "Empfangene Nachrichten",
"sent_message_count": "Gesendete Nachrichten"
"sent_message_count": "Gesendete Nachrichten",
"user_joined_game_server_count": "Anzahl Gameserver beitritte",
"user_joined_game_server_ontime": "Gameserver Ontime",
"user_joined_voice_channel_count": "Anzahl Sprachkanal beitritte",
"user_joined_voice_channel_ontime": "Sprachkanal Ontime",
"user_warning_count": "Anzahl Verwarnungen"
},
"header": "Server",
"levels": {

View File

@ -454,13 +454,21 @@
}
},
"dashboard": {
"achievements_achieved": "Achievements achieved",
"activity_score": "Activity score",
"deleted_message_count": "Deleted messages",
"header": "Server dashboard",
"message_count": "Message count",
"moved_users_count": "Moved users",
"name": "Name",
"received_command_count": "Received commands",
"received_message_count": "Received messages",
"sent_message_count": "Sent messages"
"sent_message_count": "Sent messages",
"user_joined_game_server_count": "Joined Gameserver count",
"user_joined_game_server_ontime": "Gameserver ontime",
"user_joined_voice_channel_count": "Joined voice channel count",
"user_joined_voice_channel_ontime": "Joined voice channel ontime",
"user_warning_count": "User warning count"
},
"header": "Server",
"levels": {

View File

@ -163,6 +163,14 @@ header {
}
}
td,
th {
p-dropdown,
.p-dropdown {
width: 100%;
}
}
.content-row {
display: flex;
flex-direction: row;
@ -811,8 +819,4 @@ p-inputNumber {
gap: 5px;
}
}
p-dropdown {
width: 100%;
}
}