Added logic to make achievement config more generic #268_achievements

This commit is contained in:
Sven Heidemann 2023-07-15 12:52:54 +02:00
parent c8a2ed290b
commit cd5b3b6523
12 changed files with 138 additions and 18 deletions

View File

@ -1,3 +1,8 @@
type AchievementAttribute {
name: String
type: String
}
type Achievement implements TableWithHistoryQuery { type Achievement implements TableWithHistoryQuery {
id: ID id: ID
name: String name: String

View File

@ -31,6 +31,8 @@ type Query {
achievementCount: Int achievementCount: Int
achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement]
AchievementAttributes: [AchievementAttribute]
AchievementOperators: [String]
guilds(filter: GuildFilter): [Guild] guilds(filter: GuildFilter): [Guild]
} }

View File

@ -0,0 +1,11 @@
from bot_graphql.abc.data_query_abc import DataQueryABC
class AchievementQuery(DataQueryABC):
def __init__(
self,
):
DataQueryABC.__init__(self, "AchievementAttribute")
self.set_field("name", lambda x, *_: x.name)
self.set_field("type", lambda x, *_: x.type)

View File

@ -21,6 +21,7 @@ from bot_graphql.filter.user_filter import UserFilter
from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter
from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter
from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter
from modules.achievements.achievement_service import AchievementService
class Query(QueryABC): class Query(QueryABC):
@ -37,6 +38,7 @@ class Query(QueryABC):
user_joined_game_server: UserJoinedGameServerRepositoryABC, user_joined_game_server: UserJoinedGameServerRepositoryABC,
users: UserRepositoryABC, users: UserRepositoryABC,
achievements: AchievementRepositoryABC, achievements: AchievementRepositoryABC,
achievement_service: AchievementService,
): ):
QueryABC.__init__(self, "Query") QueryABC.__init__(self, "Query")
@ -65,6 +67,8 @@ class Query(QueryABC):
self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter) self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter)
self.set_field("guilds", self._resolve_guilds) self.set_field("guilds", self._resolve_guilds)
self.set_field("AchievementAttributes", lambda x, *_: achievement_service.get_attributes())
self.set_field("AchievementOperators", lambda x, *_: ["==", "!=", "<=", ">=", "<", ">"])
def _resolve_guilds(self, *_, filter=None): def _resolve_guilds(self, *_, filter=None):
if filter is None or "id" not in filter: if filter is None or "id" not in filter:

View File

@ -2,6 +2,7 @@ from cpl_core.configuration import ConfigurationABC
from cpl_core.database.context import DatabaseContextABC from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC from cpl_core.logging import LoggerABC
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from cpl_translation import TranslatePipe from cpl_translation import TranslatePipe
from bot_core.configuration.server_settings import ServerSettings from bot_core.configuration.server_settings import ServerSettings
@ -10,6 +11,7 @@ from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
from bot_data.model.achievement import Achievement from bot_data.model.achievement import Achievement
from bot_data.model.user import User from bot_data.model.user import User
from bot_data.model.user_got_achievement import UserGotAchievement from bot_data.model.user_got_achievement import UserGotAchievement
from modules.achievements.model.achievement_attribute import AchievementAttribute
class AchievementService: class AchievementService:
@ -31,6 +33,17 @@ class AchievementService:
self._message_service = message_service self._message_service = message_service
self._t = t self._t = t
def get_attributes(self) -> List[AchievementAttribute]:
attributes = List(AchievementAttribute)
attributes.add(AchievementAttribute("xp", lambda user: user.xp, "number"))
attributes.add(AchievementAttribute("message_count", lambda user: user.message_count, "number"))
attributes.add(AchievementAttribute("reaction_count", lambda user: user.reaction_count, "number"))
attributes.add(AchievementAttribute("ontime", lambda user: user.ontime, "number"))
attributes.add(AchievementAttribute("level", lambda user: user.level, "Level"))
return attributes
def _match(self, value: str, operator: str, expected_value: str) -> bool: def _match(self, value: str, operator: str, expected_value: str) -> bool:
match operator: match operator:
case "==": case "==":

View File

@ -0,0 +1 @@
# imports

View File

@ -0,0 +1,20 @@
from typing import Callable
class AchievementAttribute:
# frontend type = TypeScript types
def __init__(self, name: str, resolver: Callable, frontend_type: str):
self._name = name
self._resolver = resolver
self._frontend_type = frontend_type
@property
def name(self) -> str:
return self._name
@property
def type(self) -> str:
return self._frontend_type
def resolve(self, *args, **kwargs):
return self._resolver(*args, **kwargs)

View File

@ -1,10 +1,15 @@
import { DataWithHistory } from "./data.model"; import { DataWithHistory } from "./data.model";
import { Server, ServerFilter } from "./server.model"; import { Server, ServerFilter } from "./server.model";
export interface AchievementAttribute {
name?: string;
type?: string;
}
export interface Achievement extends DataWithHistory { export interface Achievement extends DataWithHistory {
id?: number; id?: number;
name?: string; name?: string;
attribute?: string; attribute?: string | AchievementAttribute;
operator?: string; operator?: string;
value?: string; value?: string;
server?: Server; server?: Server;

View File

@ -90,6 +90,16 @@ export class Queries {
} }
`; `;
static achievementTypeQuery = `
query AchievementType {
AchievementOperators
AchievementAttributes {
name
type
}
}
`;
static achievementQuery = ` static achievementQuery = `
query AchievementList($serverId: ID, $filter: AchievementFilter, $page: Page, $sort: Sort) { query AchievementList($serverId: ID, $filter: AchievementFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) { servers(filter: {id: $serverId}) {

View File

@ -3,7 +3,7 @@ import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Guild } from "../data/discord.model"; import { Guild } from "../data/discord.model";
import { Level } from "../data/level.model"; import { Level } from "../data/level.model";
import { Achievement } from "../data/achievement.model"; import { Achievement, AchievementAttribute } from "../data/achievement.model";
export interface Query { export interface Query {
serverCount: number; serverCount: number;
@ -24,6 +24,11 @@ export interface LevelListQuery {
levels: Level[]; levels: Level[];
} }
export interface AchievementTypeQuery {
AchievementAttributes: AchievementAttribute[];
AchievementOperators: string[];
}
export interface AchievementListQuery { export interface AchievementListQuery {
achievementCount: number; achievementCount: number;
achievements: Achievement[]; achievements: Achievement[];

View File

@ -132,7 +132,8 @@
<td> <td>
<p-cellEditor> <p-cellEditor>
<ng-template pTemplate="input"> <ng-template pTemplate="input">
<p-dropdown [options]="attributes" [(ngModel)]="achievement.attribute" placeholder="{{'view.server.achievements.headers.attribute' | translate}}"></p-dropdown> <p-dropdown [options]="attributes" [(ngModel)]="achievement.attribute"
placeholder="{{'view.server.achievements.headers.attribute' | translate}}"></p-dropdown>
</ng-template> </ng-template>
<ng-template pTemplate="output"> <ng-template pTemplate="output">
{{achievement.attribute}} {{achievement.attribute}}
@ -152,9 +153,18 @@
</td> </td>
<td> <td>
<p-cellEditor> <p-cellEditor *ngIf="getAchievementAttributeByName(achievement.attribute).type === 'number'">
<ng-template pTemplate="input"> <ng-template pTemplate="input">
<input class="table-edit-input" pInputText min="0" type="text" [(ngModel)]="achievement.value"> <input class="table-edit-input" pInputText min="0" type="number" [(ngModel)]="achievement.value">
</ng-template>
<ng-template pTemplate="output">
{{achievement.value}}
</ng-template>
</p-cellEditor>
<p-cellEditor *ngIf="getAchievementAttributeByName(achievement.attribute).type === 'Level'">
<ng-template pTemplate="input">
<p-dropdown [options]="levels" [(ngModel)]="achievement.value" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown>
</ng-template> </ng-template>
<ng-template pTemplate="output"> <ng-template pTemplate="output">
{{achievement.value}} {{achievement.value}}

View File

@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { Achievement, AchievementFilter } from "../../../../../../models/data/achievement.model"; import { Achievement, AchievementAttribute, AchievementFilter } from "../../../../../../models/data/achievement.model";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
import { Page } from "../../../../../../models/graphql/filter/page.model"; import { Page } from "../../../../../../models/graphql/filter/page.model";
import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model"; import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model";
@ -15,12 +15,12 @@ import { TranslateService } from "@ngx-translate/core";
import { DataService } from "../../../../../../services/data/data.service"; import { DataService } from "../../../../../../services/data/data.service";
import { SidebarService } from "../../../../../../services/sidebar/sidebar.service"; import { SidebarService } from "../../../../../../services/sidebar/sidebar.service";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { AchievementListQuery, Query } from "../../../../../../models/graphql/query.model"; import { AchievementListQuery, AchievementTypeQuery, LevelListQuery, Query } from "../../../../../../models/graphql/query.model";
import { catchError, debounceTime, takeUntil } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { LazyLoadEvent } from "primeng/api"; import { LazyLoadEvent, MenuItem } from "primeng/api";
import { Table } from "primeng/table"; import { Table } from "primeng/table";
import { User } from "../../../../../../models/data/user.model"; import { User } from "../../../../../../models/data/user.model";
import { AchievementMutationResult, UpdateUserMutationResult } from "../../../../../../models/graphql/result.model"; import { AchievementMutationResult } from "../../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../../models/graphql/mutations.model"; import { Mutations } from "../../../../../../models/graphql/mutations.model";
@Component({ @Component({
@ -60,8 +60,10 @@ export class AchievementComponent implements OnInit, OnDestroy {
private server: Server = {}; private server: Server = {};
public user: UserDTO | null = null; public user: UserDTO | null = null;
public operators = ["==", "!=", "<=", ">=", "<", ">"]; public operators: string[] = [];
public attributes = ["xp", "message_count", "reaction_count", "ontime", "level"] public attributes: MenuItem[] = [];
private achievementsAttributes: AchievementAttribute[] = [];
levels!: MenuItem[];
query: string = Queries.achievementWithHistoryQuery; query: string = Queries.achievementWithHistoryQuery;
@ -78,10 +80,10 @@ export class AchievementComponent implements OnInit, OnDestroy {
} }
public ngOnInit(): void { public ngOnInit(): void {
this.loading = true;
this.setFilterForm(); this.setFilterForm();
this.data.getServerFromRoute(this.route).then(async server => { this.data.getServerFromRoute(this.route).then(async server => {
this.server = server; this.server = server;
this.loadNextPage();
let authUser = await this.authService.getLoggedInUser(); let authUser = await this.authService.getLoggedInUser();
this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null; this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null;
}); });
@ -92,8 +94,22 @@ export class AchievementComponent implements OnInit, OnDestroy {
this.unsubscriber.complete(); this.unsubscriber.complete();
} }
public loadNextPage(): void {
this.loading = true; private loadLevels() {
this.data.query<LevelListQuery>(Queries.levelQuery, {
serverId: this.server.id
},
(data: Query) => {
return data.servers[0];
}
).subscribe(data => {
this.levels = data.levels.map(level => {
return { label: level.name, value: level.name };
});
});
}
private loadNextData() {
this.data.query<AchievementListQuery>(Queries.achievementQuery, { this.data.query<AchievementListQuery>(Queries.achievementQuery, {
serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort
}, },
@ -108,6 +124,19 @@ export class AchievementComponent implements OnInit, OnDestroy {
}); });
} }
public loadNextPage(): void {
this.data.query<AchievementTypeQuery>(Queries.achievementTypeQuery
).subscribe(data => {
this.operators = data.AchievementOperators;
this.achievementsAttributes = data.AchievementAttributes;
this.attributes = data.AchievementAttributes.map(attribute => {
return { label: attribute.name, value: attribute.name };
});
this.loadLevels();
this.loadNextData();
});
}
public setFilterForm(): void { public setFilterForm(): void {
this.filterForm = this.fb.group({ this.filterForm = this.fb.group({
id: new FormControl<number | null>(null), id: new FormControl<number | null>(null),
@ -184,7 +213,7 @@ export class AchievementComponent implements OnInit, OnDestroy {
name: newAchievement.name, name: newAchievement.name,
attribute: newAchievement.attribute, attribute: newAchievement.attribute,
operator: newAchievement.operator, operator: newAchievement.operator,
value: newAchievement.value, value: newAchievement.value + "",
serverId: this.server.id serverId: this.server.id
} }
).pipe(catchError(err => { ).pipe(catchError(err => {
@ -207,7 +236,7 @@ export class AchievementComponent implements OnInit, OnDestroy {
name: newAchievement.name, name: newAchievement.name,
attribute: newAchievement.attribute, attribute: newAchievement.attribute,
operator: newAchievement.operator, operator: newAchievement.operator,
value: newAchievement.value value: newAchievement.value + ""
} }
).pipe(catchError(err => { ).pipe(catchError(err => {
this.spinner.hideSpinner(); this.spinner.hideSpinner();
@ -268,4 +297,9 @@ export class AchievementComponent implements OnInit, OnDestroy {
this.isEditingNew = true; this.isEditingNew = true;
} }
public getAchievementAttributeByName(name: string): AchievementAttribute {
const [found] = this.achievementsAttributes.filter(x => x.name === name);
return found;
}
} }