Big update
This commit is contained in:
@@ -27,7 +27,7 @@ export const changeLang: RequestHandler = async (req, res) => {
|
||||
export const addGuest: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as KadiUser);
|
||||
if (req.body.guestName) {
|
||||
const newGuest: Player = await KadiUserCollection.addGuestForUser(user, req.body.guestName);
|
||||
const newGuest: Player = await KadiUserCollection().addGuestForUser(user, req.body.guestName);
|
||||
res.send({
|
||||
username: user.getUsername(),
|
||||
userId: user.getId(),
|
||||
@@ -47,9 +47,9 @@ export const updateGuest: RequestHandler = async (req, res) => {
|
||||
const {id: guestId} = req.params;
|
||||
if (req.body.newName) {
|
||||
const {newName} = req.body;
|
||||
const guest = await PlayerCollection.read(guestId);
|
||||
const guest = await PlayerCollection().read(guestId);
|
||||
guest.setNick(newName);
|
||||
const updatedGuest = await PlayerCollection.save(guest);
|
||||
const updatedGuest = await PlayerCollection().save(guest);
|
||||
res.status(200).send({
|
||||
userId: user.getId(),
|
||||
username: user.getUsername(),
|
||||
@@ -67,7 +67,7 @@ export const updateGuest: RequestHandler = async (req, res) => {
|
||||
export const getGuest: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as KadiUser);
|
||||
const {id: guestId} = req.params;
|
||||
const guest = await PlayerCollection.read(guestId);
|
||||
const guest = await PlayerCollection().read(guestId);
|
||||
res.status(200).send({
|
||||
userId: user.getId(),
|
||||
username: user.getUsername(),
|
||||
@@ -78,7 +78,7 @@ export const getGuest: RequestHandler = async (req, res) => {
|
||||
export const deleteGuest: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as KadiUser);
|
||||
const {id: guestId} = req.params;
|
||||
const deletedGuest = await KadiUserCollection.deleteGuestFromUser(user, guestId);
|
||||
const deletedGuest = await KadiUserCollection().deleteGuestFromUser(user, guestId);
|
||||
res.status(200).send({
|
||||
userId: user.getId(),
|
||||
username: user.getUsername(),
|
||||
@@ -88,7 +88,7 @@ export const deleteGuest: RequestHandler = async (req, res) => {
|
||||
|
||||
export const getGuests: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as KadiUser);
|
||||
const guests = await KadiUserCollection.getAllGuestsForUser(user);
|
||||
const guests = await KadiUserCollection().getAllGuestsForUser(user);
|
||||
res.status(200).send({
|
||||
userId: user.getId(),
|
||||
username: user.getUsername(),
|
||||
@@ -98,7 +98,7 @@ export const getGuests: RequestHandler = async (req, res) => {
|
||||
|
||||
export const getAllPlayersAssociatedWithAccount: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as KadiUser);
|
||||
const guests = await KadiUserCollection.getAllGuestsForUser(user);
|
||||
const mainPlayer = await KadiUserCollection.getMainPlayerForUser(user);
|
||||
const guests = await KadiUserCollection().getAllGuestsForUser(user);
|
||||
const mainPlayer = await KadiUserCollection().getMainPlayerForUser(user);
|
||||
res.status(200).send({guests, mainPlayer});
|
||||
};
|
||||
7
src/Controllers/rulesetController.ts
Normal file
7
src/Controllers/rulesetController.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {RequestHandler} from "express";
|
||||
import RulesetCollection from "../ObjectCollections/RulesetCollection";
|
||||
|
||||
export const getRuleset: RequestHandler = async (req, res) => {
|
||||
const ruleset = await RulesetCollection().read(req.params.id);
|
||||
res.json(ruleset.getSchemaJSON());
|
||||
};
|
||||
@@ -24,7 +24,7 @@ export const showRegistrationPage: RequestHandler = (req, res) => {
|
||||
export const registerNewUser: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const loginDetails: LoginDetails = req.body as LoginDetails;
|
||||
const newUser = await KadiUserCollection.registerUser(loginDetails);
|
||||
const newUser = await KadiUserCollection().registerUser(loginDetails);
|
||||
req.login(newUser, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
@@ -54,7 +54,7 @@ export const logoutUser: RequestHandler = (req, res) => {
|
||||
};
|
||||
|
||||
export const authenticateKadiUser: VerifyFunction = async (email, password, done) => {
|
||||
const user = await KadiUserCollection.findByEmail(email);
|
||||
const user = await KadiUserCollection().findByEmail(email);
|
||||
if (!user) {
|
||||
return done(null, false, { message: "A user with that email does not exist."} );
|
||||
}
|
||||
@@ -75,6 +75,6 @@ export async function serializeKadiUser(user: KadiUser, done: (err: any, id?: un
|
||||
}
|
||||
|
||||
export async function deserializeKadiUser(id: string, done: (err: any, id?: unknown) => void): Promise<void> {
|
||||
const user: KadiUser | null = await KadiUserCollection.getSerializedAuthUser(id);
|
||||
const user: KadiUser | null = await KadiUserCollection().read(id);
|
||||
done(null, user);
|
||||
}
|
||||
@@ -9,12 +9,18 @@ import RulesetCollection from "../ObjectCollections/RulesetCollection";
|
||||
import Ruleset from "../Objects/Ruleset";
|
||||
import {OutcomeType} from "../Objects/DefaultStatsMongoData";
|
||||
|
||||
export interface GameSubmission {
|
||||
rulesetId: string;
|
||||
interface GameSubmission {
|
||||
ruleset: string;
|
||||
players: { id: string; nick: string }[];
|
||||
results: PlayerGameResult[];
|
||||
}
|
||||
|
||||
export interface ProcessedGameSubmission {
|
||||
ruleset: string;
|
||||
players: {id: string, nick: string}[];
|
||||
results: ScoredResultsWithOutcome[];
|
||||
}
|
||||
|
||||
type PlayerGameResult = { playerId: string; blocks: Record<string, Block> };
|
||||
type Block = { cells: Record<string, Cell> };
|
||||
type Cell = { value: CellValue };
|
||||
@@ -27,11 +33,11 @@ enum ResultType {
|
||||
}
|
||||
|
||||
type ScoredResults = {score: number, results: PlayerGameResult};
|
||||
type ScoredResultsWithOutcome = {score: number, results: PlayerGameResult & {outcome: OutcomeType}};
|
||||
export type ScoredResultsWithOutcome = {score: number, results: PlayerGameResult & {outcome: OutcomeType}};
|
||||
|
||||
export const listGames: RequestHandler = async (req, res) => {
|
||||
const user = req.user as KadiUser;
|
||||
const gamesList = await KadiUserCollection.getSavedGamesForUser(user);
|
||||
const gamesList = await KadiUserCollection().getSavedGamesForUser(user);
|
||||
if (gamesList) {
|
||||
res.json({ games: gamesList });
|
||||
}
|
||||
@@ -47,17 +53,32 @@ export const saveGame: RequestHandler = async (req, res) => {
|
||||
if (newGuests.length > 0) {
|
||||
fillOutSubmissionWithNewIds(submission, newGuests);
|
||||
}
|
||||
const newGame = await KadiUserCollection.addGameForUser(user, submission);
|
||||
processStats(await RulesetCollection.read(submission.rulesetId), submission.results, user);
|
||||
const rulesetUsed = await RulesetCollection().read(submission.ruleset);
|
||||
const scoredResultsWithOutcomes = await processStats(rulesetUsed, submission.results, user);
|
||||
const newGame = await KadiUserCollection().addGameForUser(user, {...submission, results: scoredResultsWithOutcomes});
|
||||
res.send({ message: "Game submitted successfully!", newGame: newGame });
|
||||
};
|
||||
|
||||
export const getStats: RequestHandler = async (req, res) => {
|
||||
const user = req.user as KadiUser;
|
||||
const stats = await KadiUserCollection().getAllStatsForUser(user);
|
||||
if (stats) {
|
||||
res.json({
|
||||
pStats: stats.pStats.map(pStats => ({...pStats, stats: pStats.stats.getData()})),
|
||||
accStats: stats.accStats.getData()
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
};
|
||||
|
||||
async function addNewGuests(submission: GameSubmission, user: KadiUser): Promise<Player[]> {
|
||||
const newGuestIds: Player[] = [];
|
||||
for (const playerDetails of submission.players) {
|
||||
const isNewPlayer = playerDetails.id === playerDetails.nick;
|
||||
if (isNewPlayer) {
|
||||
const newGuest: Player = await KadiUserCollection.addGuestForUser(user, playerDetails.nick);
|
||||
const newGuest: Player = await KadiUserCollection().addGuestForUser(user, playerDetails.nick);
|
||||
newGuestIds.push(newGuest);
|
||||
}
|
||||
}
|
||||
@@ -68,17 +89,17 @@ function fillOutSubmissionWithNewIds(submission: GameSubmission, newGuestList: P
|
||||
for (const newGuest of newGuestList) {
|
||||
const gameResultsFromNewGuest = submission.results.find((result) => result.playerId === newGuest.getNick());
|
||||
if (gameResultsFromNewGuest) {
|
||||
gameResultsFromNewGuest.playerId = newGuest.getId();
|
||||
gameResultsFromNewGuest.playerId = newGuest.getId().toString();
|
||||
}
|
||||
const playerEntryForNewGuest = submission.players.find((player) => player.id === newGuest.getNick());
|
||||
if (playerEntryForNewGuest) {
|
||||
playerEntryForNewGuest.id = newGuest.getId();
|
||||
playerEntryForNewGuest.id = newGuest.getId().toString();
|
||||
}
|
||||
}
|
||||
return submission;
|
||||
}
|
||||
|
||||
async function processStats(ruleset: Ruleset, results: PlayerGameResult[], account: KadiUser) {
|
||||
async function processStats(ruleset: Ruleset, results: PlayerGameResult[], account: KadiUser): Promise<ScoredResultsWithOutcome[]> {
|
||||
const calc = new ScoreCalculator(ruleset);
|
||||
let playerScoreList: ScoredResults[] = [];
|
||||
for (const result of results) {
|
||||
@@ -88,59 +109,60 @@ async function processStats(ruleset: Ruleset, results: PlayerGameResult[], accou
|
||||
results: result
|
||||
});
|
||||
}
|
||||
const playerScoreListWithOutcomes = updateScoreListWithOutcomes(playerScoreList, ruleset);
|
||||
updateStatsForIndividualPlayers(playerScoreListWithOutcomes, ruleset);
|
||||
const playerScoreListWithOutcomes = updateScoreListWithOutcomes(playerScoreList);
|
||||
await updateStatsForIndividualPlayers(playerScoreListWithOutcomes, ruleset);
|
||||
const gameResults = playerScoreListWithOutcomes.map(scoredResults => scoredResults.results);
|
||||
await KadiUserCollection.updateAccountStats(account.getId(), gameResults, ruleset);
|
||||
await KadiUserCollection().updateAccountStats(account.getId(), gameResults, ruleset);
|
||||
return playerScoreListWithOutcomes;
|
||||
}
|
||||
|
||||
function updateScoreListWithOutcomes(playerScoreList: ScoredResults[], rulesetUsed: Ruleset): ScoredResultsWithOutcome[] {
|
||||
playerScoreList = sortDescendingByScore(playerScoreList);
|
||||
const playerScoreListWithOutcomes: ScoredResultsWithOutcome[] = playerScoreList.map(scoredResults => {
|
||||
function updateScoreListWithOutcomes(scoreResultsList: ScoredResults[]): ScoredResultsWithOutcome[] {
|
||||
scoreResultsList = sortDescendingByScore(scoreResultsList);
|
||||
const playerScoreListWithOutcomes: ScoredResultsWithOutcome[] = scoreResultsList.map(scoredResults => {
|
||||
const newResults = {...scoredResults.results, outcome: OutcomeType.loss};
|
||||
return {...scoredResults, results: newResults};
|
||||
});
|
||||
let runnerUpsStart: number;
|
||||
if (playerScoreListWithOutcomes[0].score !== playerScoreListWithOutcomes[1].score) {
|
||||
playerScoreListWithOutcomes[0].results.outcome = OutcomeType.win;
|
||||
|
||||
runnerUpsStart = 1;
|
||||
}
|
||||
else {
|
||||
runnerUpsStart = updateScoreListWithDraws(playerScoreListWithOutcomes, rulesetUsed);
|
||||
runnerUpsStart = updateScoreListWithDraws(playerScoreListWithOutcomes);
|
||||
}
|
||||
const losersStart = updateScoreListWithRunnerUps(playerScoreListWithOutcomes.slice(runnerUpsStart), rulesetUsed);
|
||||
updateScoreListWithLosses(playerScoreListWithOutcomes.slice(losersStart), rulesetUsed);
|
||||
const losersStart = updateScoreListWithRunnerUps(playerScoreListWithOutcomes, runnerUpsStart);
|
||||
updateScoreListWithLosses(playerScoreListWithOutcomes, losersStart);
|
||||
return playerScoreListWithOutcomes;
|
||||
}
|
||||
|
||||
function updateScoreListWithDraws(playerScoreList: ScoredResultsWithOutcome[], rulesetUsed: Ruleset): number {
|
||||
for (let i = 0; i < playerScoreList.length; i++) {
|
||||
if (playerScoreList[i].score === playerScoreList[0].score) {
|
||||
playerScoreList[i].results.outcome = OutcomeType.draw;
|
||||
function updateScoreListWithDraws(scoreResultsList: ScoredResultsWithOutcome[]): number {
|
||||
for (let i = 0; i < scoreResultsList.length; i++) {
|
||||
if (scoreResultsList[i].score === scoreResultsList[0].score) {
|
||||
scoreResultsList[i].results.outcome = OutcomeType.draw;
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return playerScoreList.length;
|
||||
return scoreResultsList.length;
|
||||
}
|
||||
|
||||
function updateScoreListWithRunnerUps(playerScoreList: ScoredResultsWithOutcome[], rulesetUsed: Ruleset): number {
|
||||
for (let i = 0; i < playerScoreList.length; i++) {
|
||||
if (playerScoreList[i].score === playerScoreList[0].score) {
|
||||
playerScoreList[i].results.outcome = OutcomeType.runnerUp;
|
||||
function updateScoreListWithRunnerUps(scoreResultsList: ScoredResultsWithOutcome[], runnerUpsStartIndex: number): number {
|
||||
scoreResultsList[runnerUpsStartIndex].results.outcome = OutcomeType.runnerUp;
|
||||
for (let i = runnerUpsStartIndex + 1; i < scoreResultsList.length; i++) {
|
||||
if (scoreResultsList[i].score === scoreResultsList[i - 1].score) {
|
||||
scoreResultsList[i].results.outcome = OutcomeType.runnerUp;
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return playerScoreList.length;
|
||||
return scoreResultsList.length;
|
||||
}
|
||||
|
||||
function updateScoreListWithLosses(scoreResultsList: ScoredResultsWithOutcome[], rulesetUsed: Ruleset) {
|
||||
for (const lostPlayerResults of scoreResultsList) {
|
||||
lostPlayerResults.results.outcome = OutcomeType.loss;
|
||||
function updateScoreListWithLosses(scoreResultsList: ScoredResultsWithOutcome[], losersStartIndex: number) {
|
||||
for (let i = losersStartIndex; i < scoreResultsList.length; i++) {
|
||||
scoreResultsList[i].results.outcome = OutcomeType.loss;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +170,9 @@ function sortDescendingByScore(playerScoreList: ScoredResults[]) {
|
||||
return playerScoreList.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
|
||||
function updateStatsForIndividualPlayers(playerScoreListWithOutcomes: ScoredResultsWithOutcome[], rulesetUsed: Ruleset): void {
|
||||
async function updateStatsForIndividualPlayers(playerScoreListWithOutcomes: ScoredResultsWithOutcome[], rulesetUsed: Ruleset): Promise<void> {
|
||||
for (const scoredResults of playerScoreListWithOutcomes) {
|
||||
PlayerCollection.updateStatsForPlayer(
|
||||
await PlayerCollection().updateStatsForPlayer(
|
||||
scoredResults.results.playerId,
|
||||
{...scoredResults.results, outcome: scoredResults.results.outcome},
|
||||
rulesetUsed);
|
||||
1
src/ObjectCollections/CollectionRegistry.ts
Normal file
1
src/ObjectCollections/CollectionRegistry.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {CredentialsTakenError, GenericPersistenceError} from "../errors";
|
||||
import mongo from "mongodb";
|
||||
import {CredentialsTakenError} from "../errors";
|
||||
import bcrypt from "bcrypt";
|
||||
import {SupportedLang} from "../enums";
|
||||
import {AccountStatsMongoData, defaultAccountStatsMongoData, OutcomeType, PlayerGameResults} from "../Objects/DefaultStatsMongoData";
|
||||
@@ -11,13 +10,14 @@ import SavedGameCollection from "./SavedGameCollection";
|
||||
import SavedGame from "../Objects/SavedGame";
|
||||
import PlayerCollection from "../ObjectCollections/PlayerCollection";
|
||||
import Player from "../Objects/Player";
|
||||
import {GameSubmission} from "../controllers/statsController";
|
||||
import {ProcessedGameSubmission} from "../Controllers/statsController";
|
||||
import Ruleset from "../Objects/Ruleset";
|
||||
import RulesetCollection from "./RulesetCollection";
|
||||
import AccountStats from "../Objects/AccountStats";
|
||||
import PlayerStats from "../Objects/PlayerStats";
|
||||
|
||||
export interface KadiUserMongoData {
|
||||
id: string;
|
||||
id: ActiveRecordId;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
@@ -30,8 +30,20 @@ export interface KadiUserMongoData {
|
||||
}
|
||||
|
||||
class KadiUserCollection extends MongoStoredObjectCollection<KadiUserMongoData> {
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient);
|
||||
private static instance?: KadiUserCollection;
|
||||
private constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static getInstance(): KadiUserCollection {
|
||||
if (KadiUserCollection.instance === undefined) {
|
||||
KadiUserCollection.instance = new KadiUserCollection();
|
||||
}
|
||||
return KadiUserCollection.instance;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.mongoDbClientCollection = getMongoObjectCollection("users")
|
||||
}
|
||||
|
||||
private kadiUserFrom(data: KadiUserMongoData): KadiUser {
|
||||
@@ -70,7 +82,7 @@ class KadiUserCollection extends MongoStoredObjectCollection<KadiUserMongoData>
|
||||
}
|
||||
|
||||
private async addNewUser(loginDetails: LoginDetails): Promise<KadiUser> {
|
||||
const newPlayer = await PlayerCollection.create(loginDetails.username);
|
||||
const newPlayer = await PlayerCollection().create(loginDetails.username);
|
||||
const securePassword = await this.makePasswordSecure(loginDetails.password);
|
||||
const newUser = await this.mongoCreate({
|
||||
username: loginDetails.username,
|
||||
@@ -96,58 +108,52 @@ class KadiUserCollection extends MongoStoredObjectCollection<KadiUserMongoData>
|
||||
return object !== null;
|
||||
}
|
||||
|
||||
async getSerializedAuthUser(id: string): Promise<KadiUser> {
|
||||
const foundUser = await this.mongoRead(id);
|
||||
if (foundUser) {
|
||||
return this.kadiUserFrom(foundUser);
|
||||
}
|
||||
else {
|
||||
throw new GenericPersistenceError("User not found!");
|
||||
}
|
||||
}
|
||||
|
||||
async makePasswordSecure(password: string): Promise<string> {
|
||||
return bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
async addGuestForUser(userOrId: OrId<KadiUser>, newGuestNick: string): Promise<Player> {
|
||||
const newGuest = await PlayerCollection.create(newGuestNick);
|
||||
await this.mongoDbClientCollection.findOneAndUpdate(
|
||||
const newGuest = await PlayerCollection().create(newGuestNick);
|
||||
await this.mongoDbClientCollection!.findOneAndUpdate(
|
||||
{_id: this.idFromRecordOrId(userOrId)},
|
||||
{$push: {guests: newGuest.getId()}});
|
||||
return newGuest;
|
||||
}
|
||||
|
||||
async deleteGuestFromUser(userOrId: OrId<KadiUser>, guestOrGuestId: OrId<Player>): Promise<Player> {
|
||||
const deletedGuest = await PlayerCollection.delete(this.idFromRecordOrId(guestOrGuestId));
|
||||
await this.mongoDbClientCollection.findOneAndUpdate(
|
||||
const deletedGuest = await PlayerCollection().delete(this.idFromRecordOrId(guestOrGuestId));
|
||||
await this.mongoDbClientCollection!.findOneAndUpdate(
|
||||
{_id: this.idFromRecordOrId(userOrId)},
|
||||
{$pull: {guests: this.idFromRecordOrId(guestOrGuestId)}});
|
||||
{$pull: {guests: this.idFromRecordOrId(deletedGuest)}});
|
||||
return deletedGuest;
|
||||
}
|
||||
|
||||
async getAllGuestsForUser(userOrId: OrId<KadiUser>): Promise<Promise<Player>[]> {
|
||||
async getAllGuestsForUser(userOrId: OrId<KadiUser>): Promise<Player[]> {
|
||||
const guestIdList = (await this.mongoRead(this.idFromRecordOrId(userOrId)))?.guests;
|
||||
return guestIdList.map(async (guestId) => {
|
||||
return await PlayerCollection.read(guestId);
|
||||
});
|
||||
return Promise.all<Player>(
|
||||
guestIdList.map(async (guestId) => {
|
||||
return await PlayerCollection().read(guestId);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
async getMainPlayerForUser(userOrId: OrId<KadiUser>): Promise<Player> {
|
||||
const userData = await this.mongoRead(this.idFromRecordOrId(userOrId));
|
||||
return PlayerCollection.read(userData?.player);
|
||||
return PlayerCollection().read(userData?.player);
|
||||
}
|
||||
|
||||
async getSavedGamesForUser(userOrId: OrId<KadiUser>): Promise<Promise<SavedGame>[]> {
|
||||
async getSavedGamesForUser(userOrId: OrId<KadiUser>): Promise<SavedGame[]> {
|
||||
const savedGameIds = (await this.mongoRead(this.idFromRecordOrId(userOrId)))?.savedGames;
|
||||
return savedGameIds.map(async (savedGameId) => {
|
||||
return await SavedGameCollection.read(savedGameId);
|
||||
});
|
||||
return Promise.all<SavedGame>(
|
||||
savedGameIds.map(async (savedGameId) => {
|
||||
return await SavedGameCollection().read(savedGameId);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
async addGameForUser(userOrId: OrId<KadiUser>, gameSubmission: GameSubmission): Promise<SavedGame> {
|
||||
const newGame = await SavedGameCollection.create(gameSubmission);
|
||||
await this.mongoDbClientCollection.findOneAndUpdate(
|
||||
async addGameForUser(userOrId: OrId<KadiUser>, submission: ProcessedGameSubmission): Promise<SavedGame> {
|
||||
const newGame = await SavedGameCollection().create(submission);
|
||||
await this.mongoDbClientCollection!.findOneAndUpdate(
|
||||
{_id: this.idFromRecordOrId(userOrId)},
|
||||
{$push: {savedGames: newGame.getId()}});
|
||||
return newGame;
|
||||
@@ -155,16 +161,36 @@ class KadiUserCollection extends MongoStoredObjectCollection<KadiUserMongoData>
|
||||
|
||||
async updateAccountStats(userOrId: OrId<KadiUser>, gameResults: (PlayerGameResults & {outcome: OutcomeType})[], rulesetUsedOrId: OrId<Ruleset>): Promise<void> {
|
||||
const userId = this.idFromRecordOrId(userOrId);
|
||||
const rulesetUsed = rulesetUsedOrId instanceof Ruleset ? rulesetUsedOrId : await RulesetCollection.read(rulesetUsedOrId);
|
||||
const accountStatsMongoData = await this.mongoRead(userId);
|
||||
const accountStatsObject = new AccountStats(accountStatsMongoData.accountStats);
|
||||
const rulesetUsed = rulesetUsedOrId instanceof Ruleset ? rulesetUsedOrId : await RulesetCollection().read(rulesetUsedOrId);
|
||||
const accountStatsMongoData = (await this.mongoRead(userId)).accountStats;
|
||||
const accountStatsObject = new AccountStats(accountStatsMongoData);
|
||||
accountStatsObject.updateStats(gameResults, rulesetUsed);
|
||||
this.mongoUpdate({
|
||||
id: this.idFromRecordOrId(userId),
|
||||
id: userId,
|
||||
accountStats: accountStatsObject.getData()
|
||||
});
|
||||
}
|
||||
|
||||
async getAllStatsForUser(userOrId: OrId<KadiUser>): Promise<StatsListing> {
|
||||
const players = [...(await this.getAllGuestsForUser(userOrId)), await this.getMainPlayerForUser(userOrId)];
|
||||
const playerStats = players.map(player => ({
|
||||
nick: player.getNick(),
|
||||
playerId: player.getId(),
|
||||
stats: player.getStats()
|
||||
}));
|
||||
const accountStatsMongoData = (await this.mongoRead(this.idFromRecordOrId(userOrId))).accountStats;
|
||||
const accountStats = new AccountStats(accountStatsMongoData);
|
||||
return {pStats: playerStats, accStats: accountStats};
|
||||
}
|
||||
}
|
||||
|
||||
const KadiUserCollectionSingleton = new KadiUserCollection(getMongoObjectCollection("users"));
|
||||
export default KadiUserCollectionSingleton;
|
||||
export interface StatsListing {
|
||||
accStats: AccountStats;
|
||||
pStats: {
|
||||
nick: string,
|
||||
playerId: ActiveRecordId,
|
||||
stats: PlayerStats
|
||||
}[];
|
||||
}
|
||||
|
||||
export default KadiUserCollection.getInstance;
|
||||
@@ -5,35 +5,47 @@ import ActiveRecord, {ActiveRecordId} from "../Objects/ActiveRecord";
|
||||
|
||||
|
||||
abstract class MongoStoredObjectCollection<IRawData extends {id: ActiveRecordId}> {
|
||||
protected mongoDbClientCollection?: mongo.Collection;
|
||||
protected constructor() {}
|
||||
|
||||
protected constructor(protected mongoDbClientCollection: mongo.Collection) {}
|
||||
abstract init(): Promise<void>;
|
||||
|
||||
protected async mongoCreate(objectData: Omit<IRawData, "id">): Promise<IRawData> {
|
||||
return tryQuery(async () => {
|
||||
const insertOneWriteOpResult = await this.mongoDbClientCollection.insertOne(objectData);
|
||||
const insertOneWriteOpResult = await this.mongoDbClientCollection!.insertOne(objectData);
|
||||
if (insertOneWriteOpResult.result.ok === 1) {
|
||||
return insertOneWriteOpResult.ops[0]
|
||||
const newObject = insertOneWriteOpResult.ops[0];
|
||||
newObject.id = newObject._id;
|
||||
newObject._id = undefined;
|
||||
return insertOneWriteOpResult.ops[0];
|
||||
} else {
|
||||
throw new MongoError(`Error creating the object: ${JSON.stringify(objectData)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected async mongoRead(id: string): Promise<IRawData> {
|
||||
protected async mongoRead(id: ActiveRecordId): Promise<IRawData> {
|
||||
return tryQuery(async () => {
|
||||
const result = await this.mongoDbClientCollection.findOne({_id: id});
|
||||
const result = await this.mongoDbClientCollection!.findOne({_id: new mongo.ObjectID(id)});
|
||||
if (result) {
|
||||
result.id = result._id;
|
||||
result._id = undefined;
|
||||
return result;
|
||||
} else {
|
||||
throw new InvalidIdError(`Object in collection "${typeof this}" with id ${JSON.stringify(id)} not found!`);
|
||||
throw new InvalidIdError(`Object in collection "${this.constructor.name}" with id ${JSON.stringify(id)} not found!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected async mongoFindByAttribute(attribute: string, value: any): Promise<IRawData | null> {
|
||||
return tryQuery(async () =>
|
||||
await this.mongoDbClientCollection.findOne({[attribute]: value})
|
||||
);
|
||||
return tryQuery(async () => {
|
||||
const result = await this.mongoDbClientCollection!.findOne({[attribute]: value});
|
||||
if (result) {
|
||||
result.id = result._id;
|
||||
result._id = undefined;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
protected async mongoDelete(objectId: ActiveRecordId, returnObject?: boolean): Promise<IRawData | void> {
|
||||
@@ -41,7 +53,7 @@ abstract class MongoStoredObjectCollection<IRawData extends {id: ActiveRecordId}
|
||||
if (returnObject ?? true) {
|
||||
deletedObject = await this.mongoRead(objectId);
|
||||
}
|
||||
const deleteWriteOpResult = await this.mongoDbClientCollection.deleteOne({_id: objectId});
|
||||
const deleteWriteOpResult = await this.mongoDbClientCollection!.deleteOne({_id: objectId});
|
||||
if (deleteWriteOpResult.result.ok === 1) {
|
||||
return deletedObject;
|
||||
} else {
|
||||
@@ -49,16 +61,17 @@ abstract class MongoStoredObjectCollection<IRawData extends {id: ActiveRecordId}
|
||||
}
|
||||
}
|
||||
|
||||
protected async mongoUpdate(object: Partial<IRawData> & {id: ActiveRecordId}) {
|
||||
protected async mongoUpdate(object: Partial<IRawData>) {
|
||||
await tryQuery(() =>
|
||||
this.mongoDbClientCollection.findOneAndUpdate({_id: object.id}, {$set: {...object, id: undefined}})
|
||||
this.mongoDbClientCollection!.findOneAndUpdate({_id: object.id}, {$set: {...object, id: undefined}})
|
||||
);
|
||||
}
|
||||
|
||||
protected idFromRecordOrId<T extends ActiveRecord>(recordOrRecordId: T | ActiveRecordId): ActiveRecordId {
|
||||
return typeof recordOrRecordId === "string" ? recordOrRecordId : recordOrRecordId.getId();
|
||||
return recordOrRecordId instanceof mongo.ObjectId || typeof recordOrRecordId === "string" ?
|
||||
recordOrRecordId :
|
||||
recordOrRecordId.getId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default MongoStoredObjectCollection;
|
||||
@@ -1,5 +1,4 @@
|
||||
import MongoStoredObjectCollection from "./MongoStoredObjectCollection";
|
||||
import mongo from "mongodb";
|
||||
import {
|
||||
defaultPlayerStatsMongoData,
|
||||
OutcomeType,
|
||||
@@ -14,18 +13,30 @@ import Ruleset from "../Objects/Ruleset";
|
||||
import RulesetCollection from "./RulesetCollection";
|
||||
|
||||
export interface PlayerMongoData {
|
||||
id: string;
|
||||
id: ActiveRecordId;
|
||||
nick: string;
|
||||
stats?: PlayerStatsMongoData;
|
||||
stats: PlayerStatsMongoData;
|
||||
}
|
||||
|
||||
class PlayerCollection extends MongoStoredObjectCollection<PlayerMongoData> {
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient);
|
||||
private static instance?: PlayerCollection;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static getInstance(): PlayerCollection {
|
||||
if (PlayerCollection.instance === undefined) {
|
||||
PlayerCollection.instance = new PlayerCollection();
|
||||
}
|
||||
return PlayerCollection.instance;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.mongoDbClientCollection = getMongoObjectCollection("players");
|
||||
}
|
||||
|
||||
private playerFrom(data: PlayerMongoData): Player {
|
||||
return new Player(data.id, data.nick, data.stats ? new PlayerStats(data.stats) : undefined);
|
||||
return new Player(data.id, data.nick, new PlayerStats(data.stats));
|
||||
}
|
||||
|
||||
async create(nick: string): Promise<Player> {
|
||||
@@ -47,16 +58,15 @@ class PlayerCollection extends MongoStoredObjectCollection<PlayerMongoData> {
|
||||
}
|
||||
|
||||
async updateStatsForPlayer(playerOrId: OrId<Player>, gameResults: PlayerGameResults & {outcome: OutcomeType}, rulesetUsedOrId: OrId<Ruleset>): Promise<void> {
|
||||
playerOrId = playerOrId instanceof Player ? playerOrId : await this.read(playerOrId);
|
||||
rulesetUsedOrId = rulesetUsedOrId instanceof Ruleset ? rulesetUsedOrId : await RulesetCollection.read(rulesetUsedOrId);
|
||||
playerOrId.updateStats(gameResults, rulesetUsedOrId);
|
||||
const player = playerOrId instanceof Player ? playerOrId : await this.read(playerOrId);
|
||||
const ruleset = rulesetUsedOrId instanceof Ruleset ? rulesetUsedOrId : await RulesetCollection().read(rulesetUsedOrId);
|
||||
player.updateStats(gameResults, ruleset);
|
||||
this.mongoUpdate({
|
||||
id: this.idFromRecordOrId(playerOrId),
|
||||
stats: playerOrId.getStats()?.getData()
|
||||
id: this.idFromRecordOrId(player),
|
||||
stats: player.getStats()?.getData()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const PlayerCollectionSingleton = new PlayerCollection(getMongoObjectCollection("players"));
|
||||
export default PlayerCollectionSingleton;
|
||||
export default PlayerCollection.getInstance;
|
||||
@@ -1,21 +1,33 @@
|
||||
import MongoStoredObjectCollection from "./MongoStoredObjectCollection";
|
||||
import mongo from "mongodb";
|
||||
import {DEFAULT_RULESET, DEFAULT_RULESET_NAME, RulesetSchema} from "../rulesets";
|
||||
import {getMongoObjectCollection} from "../database";
|
||||
import Ruleset from "../Objects/Ruleset";
|
||||
import {ActiveRecordId} from "../Objects/ActiveRecord";
|
||||
|
||||
type RulesetMongoData = RulesetSchema;
|
||||
type RulesetMongoData = RulesetSchema & {id: ActiveRecordId};
|
||||
|
||||
class RulesetCollection extends MongoStoredObjectCollection<RulesetMongoData> {
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient);
|
||||
private static instance?: RulesetCollection;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static getInstance(): RulesetCollection {
|
||||
if (RulesetCollection.instance === undefined) {
|
||||
RulesetCollection.instance = new RulesetCollection();
|
||||
}
|
||||
return RulesetCollection.instance;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.mongoDbClientCollection = getMongoObjectCollection("rulesets");
|
||||
}
|
||||
|
||||
private async rulesetFrom(data: RulesetMongoData): Promise<Ruleset> {
|
||||
return new Ruleset(data.id, data);
|
||||
}
|
||||
|
||||
async read(id: string): Promise<Ruleset> {
|
||||
async read(id: ActiveRecordId): Promise<Ruleset> {
|
||||
if (id === DEFAULT_RULESET_NAME) {
|
||||
return new Ruleset(DEFAULT_RULESET_NAME, DEFAULT_RULESET);
|
||||
}
|
||||
@@ -26,5 +38,4 @@ class RulesetCollection extends MongoStoredObjectCollection<RulesetMongoData> {
|
||||
}
|
||||
}
|
||||
|
||||
const RulesetCollectionSingleton = new RulesetCollection(getMongoObjectCollection("users"));
|
||||
export default RulesetCollectionSingleton;
|
||||
export default RulesetCollection.getInstance;
|
||||
@@ -1,54 +1,63 @@
|
||||
import MongoStoredObjectCollection from "./MongoStoredObjectCollection";
|
||||
import mongo from "mongodb";
|
||||
import {ActiveRecordId} from "../Objects/ActiveRecord";
|
||||
import PlayerCollection from "./PlayerCollection";
|
||||
import SavedGame from "../Objects/SavedGame";
|
||||
import {getMongoObjectCollection} from "../database";
|
||||
import {PlayerGameResults} from "../Objects/DefaultStatsMongoData";
|
||||
import RulesetCollection from "./RulesetCollection";
|
||||
import {GameSubmission} from "../controllers/statsController";
|
||||
import {ProcessedGameSubmission, ScoredResultsWithOutcome} from "../Controllers/statsController";
|
||||
|
||||
export interface SavedGameMongoData {
|
||||
id: string;
|
||||
rulesetUsed: ActiveRecordId;
|
||||
ruleset: ActiveRecordId;
|
||||
players: ActiveRecordId[];
|
||||
results: PlayerGameResults[];
|
||||
results: ScoredResultsWithOutcome[];
|
||||
}
|
||||
|
||||
class SavedGameCollection extends MongoStoredObjectCollection<SavedGameMongoData> {
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient);
|
||||
private static instance?: SavedGameCollection;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static getInstance(): SavedGameCollection {
|
||||
if (SavedGameCollection.instance === undefined) {
|
||||
SavedGameCollection.instance = new SavedGameCollection();
|
||||
}
|
||||
return SavedGameCollection.instance;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.mongoDbClientCollection = getMongoObjectCollection("savedGames");
|
||||
}
|
||||
|
||||
private async savedGameFrom(data: SavedGameMongoData): Promise<SavedGame> {
|
||||
const playerList: {name: string, id: ActiveRecordId}[] = [];
|
||||
for (const playerId of data.players) {
|
||||
const player = await PlayerCollection.read(playerId);
|
||||
const player = await PlayerCollection().read(playerId);
|
||||
playerList.push({name: player.getNick(), id: playerId})
|
||||
}
|
||||
const rulesetUsed = await RulesetCollection.read(data.rulesetUsed);
|
||||
const rulesetUsed = await RulesetCollection().read(data.ruleset);
|
||||
return new SavedGame(
|
||||
data.id,
|
||||
{name: rulesetUsed.getName(), id: data.rulesetUsed},
|
||||
{name: rulesetUsed.getName(), id: data.ruleset},
|
||||
playerList,
|
||||
data.results);
|
||||
}
|
||||
|
||||
async read(id: string): Promise<SavedGame> {
|
||||
async read(id: ActiveRecordId): Promise<SavedGame> {
|
||||
const foundGame = await this.mongoRead(id);
|
||||
return this.savedGameFrom(foundGame);
|
||||
}
|
||||
|
||||
async create(gameSubmission: GameSubmission): Promise<SavedGame> {
|
||||
const pids = gameSubmission.players.map(playerIdAndNick => playerIdAndNick.id);
|
||||
async create(submission: ProcessedGameSubmission): Promise<SavedGame> {
|
||||
const pids = submission.players.map(playerIdAndNick => playerIdAndNick.id);
|
||||
return this.savedGameFrom(
|
||||
await this.mongoCreate({
|
||||
rulesetUsed: gameSubmission.rulesetId,
|
||||
ruleset: submission.ruleset,
|
||||
players: pids,
|
||||
results: gameSubmission.results})
|
||||
results: submission.results})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SavedGameCollectionSingleton = new SavedGameCollection(getMongoObjectCollection("users"));
|
||||
export default SavedGameCollectionSingleton;
|
||||
export default SavedGameCollection.getInstance;
|
||||
@@ -10,6 +10,7 @@ class AccountStats {
|
||||
constructor(data: AccountStatsMongoData) {
|
||||
this.data = data;
|
||||
this.updater = new StatsUpdater();
|
||||
this.updater.use(data);
|
||||
}
|
||||
|
||||
use(data: AccountStatsMongoData) {
|
||||
@@ -21,8 +22,8 @@ class AccountStats {
|
||||
if (this.data) {
|
||||
for (const playerGameResult of playerGameResults) {
|
||||
this.updater.updateStats(playerGameResult, ruleset);
|
||||
this.data.gamesPlayed += 1;
|
||||
}
|
||||
this.data.gamesPlayed += 1;
|
||||
}
|
||||
else {
|
||||
throw new UpdateError(`Cannot update without data! Call the use() method to hydrate the updater with data
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export type ActiveRecordId = string;
|
||||
import mongo from "mongodb";
|
||||
|
||||
export type ActiveRecordId = mongo.ObjectId | string;
|
||||
export type OrId<T extends ActiveRecord> = T | ActiveRecordId;
|
||||
|
||||
interface ActiveRecord {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {CellDef, DEFAULT_RULESET, DEFAULT_RULESET_NAME, RulesetSchema} from "../rulesets";
|
||||
import {FieldType} from "../enums";
|
||||
|
||||
|
||||
export enum OutcomeType {
|
||||
win,
|
||||
loss,
|
||||
runnerUp,
|
||||
draw,
|
||||
win = "win",
|
||||
loss = "loss",
|
||||
runnerUp = "runnerUp",
|
||||
draw = "draw",
|
||||
}
|
||||
|
||||
export interface PlayerStatsMongoData extends BaseStatsMongoData {}
|
||||
|
||||
@@ -5,7 +5,7 @@ export type LoginDetails = { username: string, email: string, password: string }
|
||||
|
||||
class KadiUser implements ActiveRecord {
|
||||
constructor(
|
||||
private id: string,
|
||||
private id: ActiveRecordId,
|
||||
private username: string,
|
||||
private email: string,
|
||||
private password: string,
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
import {CellValue} from "../controllers/statsController";
|
||||
import {RulesetSchema} from "../rulesets";
|
||||
import ActiveRecord, {ActiveRecordId} from "./ActiveRecord";
|
||||
import {UpdateError} from "../errors";
|
||||
import {OutcomeType, PlayerGameResults} from "./DefaultStatsMongoData";
|
||||
import PlayerStats from "./PlayerStats";
|
||||
import Ruleset from "./Ruleset";
|
||||
|
||||
export interface CellDetails {
|
||||
id: string;
|
||||
value: CellValue;
|
||||
}
|
||||
|
||||
export class Player implements ActiveRecord {
|
||||
constructor(
|
||||
private id: ActiveRecordId,
|
||||
private nick: string,
|
||||
private stats?: PlayerStats
|
||||
private stats: PlayerStats
|
||||
) {}
|
||||
|
||||
getId(): ActiveRecordId {
|
||||
@@ -30,7 +23,7 @@ export class Player implements ActiveRecord {
|
||||
this.nick = newNick;
|
||||
}
|
||||
|
||||
getStats(): PlayerStats | undefined {
|
||||
getStats(): PlayerStats {
|
||||
return this.stats;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ class PlayerStats {
|
||||
constructor(data: PlayerStatsMongoData) {
|
||||
this.data = data;
|
||||
this.updater = new StatsUpdater();
|
||||
this.updater.use(data);
|
||||
}
|
||||
|
||||
use(data: PlayerStatsMongoData) {
|
||||
@@ -18,6 +19,7 @@ class PlayerStats {
|
||||
|
||||
updateStats(playerGameResults: PlayerGameResults & {outcome: OutcomeType}, ruleset: Ruleset): void {
|
||||
this.updater.updateStats(playerGameResults, ruleset);
|
||||
this.data.gamesPlayed += 1;
|
||||
}
|
||||
|
||||
getData(): PlayerStatsMongoData {
|
||||
|
||||
@@ -26,6 +26,10 @@ export class Ruleset implements ActiveRecord {
|
||||
getCellsInBlock(blockId: string): Record<string, CellDef> {
|
||||
return Object.assign({}, this.schema.blocks[blockId].cells);
|
||||
}
|
||||
|
||||
getSchemaJSON(): RulesetSchema {
|
||||
return Object.assign({}, this.schema);
|
||||
}
|
||||
}
|
||||
|
||||
export default Ruleset;
|
||||
@@ -1,12 +1,12 @@
|
||||
import ActiveRecord, {ActiveRecordId} from "./ActiveRecord";
|
||||
import {PlayerGameResults} from "./DefaultStatsMongoData";
|
||||
import {ScoredResultsWithOutcome} from "../Controllers/statsController";
|
||||
|
||||
class SavedGame implements ActiveRecord {
|
||||
constructor(
|
||||
private id: string,
|
||||
private rulesetUsed: {name: string, id: ActiveRecordId},
|
||||
private players: {name: string, id: ActiveRecordId}[],
|
||||
private results: PlayerGameResults[],
|
||||
private results: ScoredResultsWithOutcome[],
|
||||
) {}
|
||||
|
||||
getId() {
|
||||
|
||||
@@ -60,6 +60,7 @@ abstract class ScoreCellCalculator {
|
||||
}
|
||||
|
||||
hydrateWithJSON(jsonRep: ScoreCellJSONRepresentation): void {
|
||||
this.reset();
|
||||
if (jsonRep.value === CellFlag.strike) {
|
||||
this.struck = true;
|
||||
}
|
||||
@@ -67,6 +68,11 @@ abstract class ScoreCellCalculator {
|
||||
this.value = jsonRep.value;
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.struck = false;
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class NumberScoreCell extends ScoreCellCalculator {
|
||||
@@ -78,8 +84,13 @@ class NumberScoreCell extends ScoreCellCalculator {
|
||||
}
|
||||
|
||||
getScore(): number {
|
||||
if (this.isStruck()) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return this.value as number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BoolScoreCell extends ScoreCellCalculator {
|
||||
@@ -101,6 +112,11 @@ class BoolScoreCell extends ScoreCellCalculator {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
super.reset();
|
||||
this.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
class SuperkadiScoreCell extends ScoreCellCalculator {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {RulesetSchema} from "../rulesets";
|
||||
import ScoreCalculator, {ScoreCardJSONRepresentation} from "./ScoreCalculator";
|
||||
import {UpdateError} from "../errors";
|
||||
import {FieldType} from "../enums";
|
||||
@@ -8,7 +7,8 @@ import {
|
||||
BoolFieldStatsMongoData,
|
||||
CellStatsMongoData,
|
||||
OutcomeType,
|
||||
PlayerGameResults, RulesetStatsMongoData,
|
||||
PlayerGameResults,
|
||||
RulesetStatsMongoData,
|
||||
TotalFieldStatsMongoData
|
||||
} from "./DefaultStatsMongoData";
|
||||
import Ruleset from "./Ruleset";
|
||||
@@ -30,15 +30,15 @@ class StatsUpdater {
|
||||
this.validationRuleset = ruleset;
|
||||
this.calculator = new ScoreCalculator(ruleset);
|
||||
this.calculator.hydrateWithJSON(playerGameResults as ScoreCardJSONRepresentation);
|
||||
this.currentStatsObject = this.data.statsByRuleset[ruleset.getId()];
|
||||
this.currentStatsObject = this.data.statsByRuleset[ruleset.getId().toString()];
|
||||
for (const blockId in ruleset.getBlocks()) {
|
||||
this.updateBlockStats(blockId);
|
||||
}
|
||||
this.updateTotalFieldStats(this.currentStatsObject.grandTotal, this.calculator.getTotal());
|
||||
this.currentStatsObject.wins += Number(playerGameResults.outcome === "win");
|
||||
this.currentStatsObject.draws += Number(playerGameResults.outcome === "draw");
|
||||
this.currentStatsObject.runnerUps += Number(playerGameResults.outcome === "runnerUp");
|
||||
this.currentStatsObject.losses += Number(playerGameResults.outcome === "loss");
|
||||
this.currentStatsObject.wins += Number(playerGameResults.outcome === OutcomeType.win);
|
||||
this.currentStatsObject.draws += Number(playerGameResults.outcome === OutcomeType.draw);
|
||||
this.currentStatsObject.runnerUps += Number(playerGameResults.outcome === OutcomeType.runnerUp);
|
||||
this.currentStatsObject.losses += Number(playerGameResults.outcome === OutcomeType.loss);
|
||||
}
|
||||
else {
|
||||
throw new UpdateError(`Cannot update without data! Call the use() method to hydrate the updater with data
|
||||
@@ -66,9 +66,10 @@ class StatsUpdater {
|
||||
}
|
||||
|
||||
private updateCellStatsByIds(ids: {cellId: string, blockId: string}) {
|
||||
const cellStats = this.getCellStatsByIds({...ids, rulesetId: this.validationRuleset!.getId()});
|
||||
const cellStats = this.getCellStatsByIds({...ids, rulesetId: this.validationRuleset!.getId().toString()});
|
||||
const cellFieldType = this.validationRuleset?.getBlocks()[ids.blockId].cells[ids.cellId].fieldType;
|
||||
const cellScore = this.calculator!.getCellScoreByLocation({...ids});
|
||||
cellStats.runningTotal += cellScore;
|
||||
if (cellScore > 0 && cellFieldType === FieldType.bool) {
|
||||
(cellStats as BoolFieldStatsMongoData).total += 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import express from "express";
|
||||
import * as statsController from "../controllers/statsController";
|
||||
import * as KadiUserController from "../controllers/kadiUserController"
|
||||
import * as statsController from "../Controllers/statsController";
|
||||
import * as KadiUserController from "../Controllers/kadiUserController"
|
||||
import {requireAuthenticated} from "./routerMiddleware";
|
||||
import * as rulesetController from "../Controllers/rulesetController";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -21,4 +22,8 @@ router.delete("/guest/:id", requireAuthenticated, KadiUserController.deleteGuest
|
||||
router.get("/games", requireAuthenticated, statsController.listGames);
|
||||
router.post("/games", requireAuthenticated, statsController.saveGame);
|
||||
|
||||
//Stats
|
||||
router.get("/stats", requireAuthenticated, statsController.getStats);
|
||||
router.get("/ruleset/:id", rulesetController.getRuleset);
|
||||
|
||||
export default router;
|
||||
@@ -1,4 +1,4 @@
|
||||
import express, {NextFunction} from "express";
|
||||
import express from "express";
|
||||
import routers from "./routers";
|
||||
import {LoginDetails} from "../Objects/KadiUser";
|
||||
import {requireAuthenticated} from "./routerMiddleware";
|
||||
@@ -22,6 +22,7 @@ router.get("/**", requireAuthenticated, (req, res) => {
|
||||
});
|
||||
|
||||
const topLevelErrorHandler: express.ErrorRequestHandler = (err, req, res, next) => {
|
||||
console.log(err.message);
|
||||
if (err instanceof GenericPersistenceError) {
|
||||
res.status(500).send({message: "An internal error occurred accessing the database."});
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
import express from "express";
|
||||
import {requireAuthenticated, requireNotAuthenticated} from "../passport-config";
|
||||
import * as signup from "../controllers/signupController";
|
||||
import * as signup from "../Controllers/signupController";
|
||||
import {requireAuthenticated, requireNotAuthenticated} from "./routerMiddleware";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/login", requireNotAuthenticated, signup.showLoginPage);
|
||||
|
||||
router.post("/login", requireNotAuthenticated, signup.loginUser);
|
||||
|
||||
router.get("/register", requireNotAuthenticated, signup.showRegistrationPage);
|
||||
|
||||
router.post("/register", requireNotAuthenticated, signup.registerNewUser);
|
||||
|
||||
router.get("/logout", requireAuthenticated, signup.logoutUser);
|
||||
|
||||
export default router;
|
||||
@@ -1,6 +1,10 @@
|
||||
import {MongoClient, Db} from "mongodb";
|
||||
import Settings from "./server-config.json";
|
||||
import {GenericPersistenceError, MongoError} from "./errors";
|
||||
import KadiUserCollection from "./ObjectCollections/KadiUserCollection";
|
||||
import PlayerCollection from "./ObjectCollections/PlayerCollection";
|
||||
import RulesetCollection from "./ObjectCollections/RulesetCollection";
|
||||
import SavedGameCollection from "./ObjectCollections/SavedGameCollection";
|
||||
|
||||
let SessionDbClient: Db;
|
||||
|
||||
@@ -21,6 +25,13 @@ export function getMongoObjectCollection(collectionName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function initCollections() {
|
||||
KadiUserCollection().init();
|
||||
PlayerCollection().init();
|
||||
SavedGameCollection().init();
|
||||
RulesetCollection().init();
|
||||
}
|
||||
|
||||
type CallbackWrapper = <T>(query: () => T) => Promise<T>;
|
||||
export const tryQuery: CallbackWrapper = async (cb) => {
|
||||
try {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import express, {NextFunction, Request, Response} from "express";
|
||||
import express from "express";
|
||||
import Settings from "./server-config.json";
|
||||
import flash from "express-flash";
|
||||
import passport from "passport";
|
||||
import session from "express-session";
|
||||
import MainRouter from "./routers/mainRouter";
|
||||
import {initMongoSessionClient} from "./database";
|
||||
import MainRouter from "./Routers/mainRouter";
|
||||
import {initCollections, initMongoSessionClient} from "./database";
|
||||
import {Strategy as LocalStrategy} from "passport-local";
|
||||
import {authenticateKadiUser, deserializeKadiUser, serializeKadiUser} from "./controllers/signupController";
|
||||
import {authenticateKadiUser, deserializeKadiUser, serializeKadiUser} from "./Controllers/signupController";
|
||||
|
||||
async function startApp() {
|
||||
await initMongoSessionClient();
|
||||
await initCollections();
|
||||
passport.use(new LocalStrategy({ usernameField: "email" }, authenticateKadiUser));
|
||||
passport.serializeUser(serializeKadiUser);
|
||||
passport.deserializeUser(deserializeKadiUser);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||
|
||||
Reference in New Issue
Block a user