Skip to content

Commit

Permalink
Merge pull request #42 from claustra01/feat/add_result
Browse files Browse the repository at this point in the history
結果入力コマンドを実装
  • Loading branch information
claustra01 authored Dec 17, 2023
2 parents 11a1125 + 2b8d7a2 commit cbd7cd9
Show file tree
Hide file tree
Showing 19 changed files with 603 additions and 13 deletions.
3 changes: 2 additions & 1 deletion application-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"commandPrefix": "./",
"permittedRole": "admin",
"initialRate": 1500,
"weightedCoefficient": 4,
"BaseWeightedCoefficient": 4,
"StackWeightedCoefficient": 1.5,
"calculatingGameCount": 3
}
3 changes: 2 additions & 1 deletion docs/dbdiagram
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ Table players {

Table games {
game_id INTEGER [pk, increment]
count INTEGER [not null]
entry_count INTEGER [not null]
stack INTEGER [not null]
date VARCHAR(10) [not null, note: "format: \"2022-01-23\""]
}

Table calculates {
calc_id INTEGER [pk, increment]
game_id INTEGER [not null, ref: > games.game_id]
stack INTEGER [not null]
winner_name VARCHAR(20) [not null, ref: > players.player_name]
winner_rate INTEGER [not null, note: "ref: players.current_rate"]
winner_is_excluded BOOLEAN [not null, note: "default: (players.game_count <= ${CALUCRATING_GAME_COUNT})"]
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"@types/pg": "^8.10.9",
"date-fns": "^2.30.0",
"discord.js": "^14.14.1",
"dotenv": "^16.3.1",
"express": "^4.18.2",
Expand Down
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

163 changes: 159 additions & 4 deletions src/adapter/commands/result.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,164 @@
import { isValid } from 'date-fns';
import { parse } from 'date-fns/fp';
import { config } from '../../config/config';
import { addMedal } from '../../usecase/functions/addMedal';
import { calculateRate } from '../../usecase/functions/calculateRate';
import { makePlayerObject } from '../../usecase/functions/makeObject';
import { Calculate, NewCalculate } from '../../usecase/types/calculate';
import { Game, NewGame } from '../../usecase/types/game';
import { Player } from '../../usecase/types/player';
import { Reply, ReplyType } from '../../usecase/types/reply';
import { calculateController } from '../queries/calculate';
import { gameController } from '../queries/game';
import { playerController } from '../queries/player';
import { transactionController } from '../queries/transaction';

const parseDate = parse(new Date(), 'yyyy-MM-dd');

const insertGame = async (newData: NewGame): Promise<Game> => {
return await gameController
.create(newData)
.then((game) => {
return game;
})
.catch((error) => {
throw error;
});
};

const insertCalc = async (newData: NewCalculate): Promise<Calculate> => {
return await calculateController
.create(newData)
.then((game) => {
return game;
})
.catch((error) => {
throw error;
});
};

const isExcluded = (gameId: number, playerGameCount: number): boolean => {
const calculatingGameCount = config.calculatingGameCount;
if (gameId <= 5) return false;
if (playerGameCount <= calculatingGameCount) return true;
return false;
};

export const commandResult = async (args: string[]): Promise<Reply> => {
if (args.length < 8) {
return {
type: ReplyType.Error,
errorText:
'Invalid Arguments: Date, EntryCount, Stack, Players...(4 or more)',
};
}

const date = args[1];
const parsedDate = parseDate(date);
const entryCount = parseInt(args[2]);
const stack = parseInt(args[3]);
const playerNameList = args.slice(4);
const playerList: Player[] = [];
const calcList: Calculate[] = [];
let gameId = -1;

// validation
if (
!isValid(parsedDate) ||
date.length !== 10 ||
parsedDate.getFullYear() < 2000
) {
return {
type: ReplyType.Error,
errorText: 'Error: Invalid date format: YYYY-MM-DD',
};
}
if (isNaN(entryCount) || isNaN(stack)) {
return {
type: ReplyType.Error,
errorText: 'Error: entry count and stack must be numbers',
};
}
if (entryCount != playerNameList.length) {
return {
type: ReplyType.Error,
errorText: 'Error: entry count and number of players do not match',
};
}
for (let i = 0; i < playerNameList.length; i++) {
try {
const player = await playerController.readByDiscordOrName(
playerNameList[i]
);
playerList.push(player);
} catch (error) {
return {
type: ReplyType.Error,
errorText: `${error}`,
};
}
}

// insert data
try {
await transactionController.begin();
const game = await insertGame({
date,
entryCount,
stack,
});
gameId = game.gameId;
for (let i = 0; i < playerList.length - 1; i++) {
for (let j = 1; j < playerList.length; j++) {
if (i < j) {
const calc = await insertCalc({
gameId: game.gameId,
stack: game.stack,
winnerName: playerList[i].playerName,
winnerRate: playerList[i].currentRate,
winnerIsExcluded: isExcluded(game.gameId, playerList[i].gameCount),
loserName: playerList[j].playerName,
loserRate: playerList[j].currentRate,
loserIsExcluded: isExcluded(game.gameId, playerList[j].gameCount),
});
calcList.push(calc);
}
}
}
await transactionController.commit();
} catch (error) {
await transactionController.rollback();
return {
type: ReplyType.Error,
errorText: `${error}`,
};
}

// calculate rate
try {
await transactionController.begin();
const playerObj = makePlayerObject(playerList);
const rateUpdatedPlayerObj = calculateRate(playerObj, calcList);
const medalUpdatedPlayerObj = addMedal(
rateUpdatedPlayerObj,
playerList[0].playerName,
playerList[1].playerName,
playerList[2].playerName
);
Object.values(medalUpdatedPlayerObj).forEach(async (player) => {
await playerController.update(player);
});
await transactionController.commit();
} catch (error) {
await transactionController.rollback();
return {
type: ReplyType.Error,
errorText: 'Error: Calculation Failed: use `recalculate` command',
};
}

export const commandResult = (args: string[]): Reply => {
console.log(args);
return {
type: ReplyType.Error,
errorText: 'Error: Not Implemented',
type: ReplyType.Text,
contentText: `Result Saved: ${stack}/${entryCount}entry, Id: ${gameId}`,
};
};
95 changes: 95 additions & 0 deletions src/adapter/queries/calculate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
parseCalculate,
parseCalculateList,
} from '../../usecase/functions/parseJson';
import { IController } from '../../usecase/interfaces/controller';
import { IDatabase } from '../../usecase/interfaces/db';
import { Calculate, NewCalculate } from '../../usecase/types/calculate';

export class CalculateController
implements IController<Calculate, NewCalculate>
{
private pool!: IDatabase;

setPool(pool: IDatabase): void {
this.pool = pool;
}

async create(newData: NewCalculate): Promise<Calculate> {
const result = await this.pool
.query(
`INSERT INTO calculates (game_id, stack, winner_name, winner_rate, winner_is_excluded, loser_name, loser_rate, loser_is_excluded) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,
[
newData.gameId.toString(),
newData.stack.toString(),
newData.winnerName,
newData.winnerRate.toString(),
newData.winnerIsExcluded.toString(),
newData.loserName,
newData.loserRate.toString(),
newData.loserIsExcluded.toString(),
]
)
.catch((error) => {
throw error;
});
return parseCalculate(result[0]);
}

async read(id: string): Promise<Calculate> {
const result = await this.pool
.query(`SELECT * FROM calculates WHERE calc_id = $1`, [id])
.catch((error) => {
throw error;
});
if (result.length === 0) {
throw new Error(`Calculate Not Found: ${id}`);
}
return parseCalculate(result[0]);
}

async readAll(): Promise<Calculate[]> {
const result = await this.pool
.query(`SELECT * FROM calculates`)
.catch((error) => {
throw error;
});
if (result.length === 0) {
throw new Error(`Calculate Not Found`);
}
return parseCalculateList(result);
}

async update(replaceData: Calculate): Promise<Calculate> {
const result = await this.pool
.query(
`UPDATE calculates SET game_id = $2, stack = $3, winner_name = $4, winner_rate = $5, winner_is_excluded = $6, loser_name = $7, loser_rate = $8, loser_is_excluded = $9 WHERE calc_id = $1 RETURNING *`,
[
replaceData.calcId.toString(),
replaceData.gameId.toString(),
replaceData.stack.toString(),
replaceData.winnerName,
replaceData.winnerRate.toString(),
replaceData.winnerIsExcluded.toString(),
replaceData.loserName,
replaceData.loserRate.toString(),
replaceData.loserIsExcluded.toString(),
]
)
.catch((error) => {
throw error;
});
return parseCalculate(result[0]);
}

async delete(id: string): Promise<Calculate> {
const result = await this.pool
.query(`DELETE FROM calculates WHERE calc_id = $1 RETURNING *`, [id])
.catch((error) => {
throw error;
});
return parseCalculate(result[0]);
}
}

export const calculateController = new CalculateController();
Loading

0 comments on commit cbd7cd9

Please sign in to comment.