Add baby name cards
This commit is contained in:
@@ -2,14 +2,17 @@ import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Redirect,
|
||||
Render,
|
||||
Req,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { BabyNamesService } from './baby-names.service';
|
||||
import { BabyNamesService, Cards } from './baby-names.service';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
@Controller('baby-names')
|
||||
@@ -31,20 +34,44 @@ export class BabyNamesController {
|
||||
if (!key) {
|
||||
throw new Error("Missing 'key' field");
|
||||
}
|
||||
const currentIndex = await this.babyNamesService.getCurrentNumber(
|
||||
key as string,
|
||||
);
|
||||
const name = this.babyNamesService.nameList[currentIndex];
|
||||
const synonyms = this.babyNamesService.getSynonyms(name);
|
||||
return {
|
||||
key: key,
|
||||
name,
|
||||
index: currentIndex,
|
||||
...(await this.babyNamesService.getUserCurrentName(key as string)),
|
||||
message: request.query.message || null,
|
||||
synonyms: synonyms.join(', '),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('.json')
|
||||
@ApiConsumes('application/json')
|
||||
async submitJson(
|
||||
@Body()
|
||||
body: {
|
||||
key: string;
|
||||
name: string;
|
||||
nameindex: number;
|
||||
opinion: number;
|
||||
pronunciation: number;
|
||||
spelling: number;
|
||||
comment: string;
|
||||
},
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const { key, name, opinion, nameindex, pronunciation, spelling, comment } =
|
||||
body;
|
||||
if (!key) {
|
||||
res.status(400).json({ error: "Missing 'key' field" });
|
||||
return;
|
||||
}
|
||||
await this.babyNamesService.addUserScore(key, name, {
|
||||
opinion,
|
||||
pronunciation,
|
||||
spelling,
|
||||
comment: comment || '',
|
||||
});
|
||||
await this.babyNamesService.writeUserNumber(key, nameindex + 1);
|
||||
res.cookie('baby-names-key', key, { maxAge: 30 * 24 * 60 * 60 * 1000 }); // 30 days
|
||||
res.json({ status: 'ok', nextIndex: nameindex + 1 });
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@Redirect('/baby-names', 302)
|
||||
@@ -89,4 +116,62 @@ export class BabyNamesController {
|
||||
nameCount: this.babyNamesService.nameCountMap,
|
||||
};
|
||||
}
|
||||
|
||||
@Get('current-index.json')
|
||||
async getCurrentIndex(@Query('key') key: string, @Res() res: Response) {
|
||||
if (!key) {
|
||||
res.status(400).json({ error: "Missing 'key' field" });
|
||||
return;
|
||||
}
|
||||
const currentIndex = await this.babyNamesService.getCurrentNumber(key);
|
||||
res.json({ currentIndex });
|
||||
}
|
||||
|
||||
@Get('data/:index.json')
|
||||
async getNameByIndex(@Req() request: Request, @Res() res: Response) {
|
||||
const indexStr = request.params.index;
|
||||
const index = parseInt(indexStr, 10);
|
||||
if (
|
||||
isNaN(index) ||
|
||||
index < 0 ||
|
||||
index >= this.babyNamesService.nameList.length
|
||||
) {
|
||||
res.status(400).json({ error: 'Invalid index' });
|
||||
return;
|
||||
}
|
||||
const name = this.babyNamesService.nameList[index];
|
||||
const synonyms = this.babyNamesService.getSynonyms(name);
|
||||
res.json({
|
||||
index,
|
||||
name,
|
||||
synonyms,
|
||||
count: this.babyNamesService.nameCountMap[name] || 0,
|
||||
});
|
||||
}
|
||||
|
||||
@Get('cards/:key.json')
|
||||
async getUserCards(@Req() request: Request, @Res() res: Response) {
|
||||
const key = request.params.key;
|
||||
if (!key) {
|
||||
res.status(400).json({ error: "Missing 'key' field" });
|
||||
return;
|
||||
}
|
||||
const cards = await this.babyNamesService.getUserCardsOrDefault(key);
|
||||
res.json(cards);
|
||||
}
|
||||
|
||||
@Post('cards/:key.json')
|
||||
async updateUserCards(
|
||||
@Req() request: Request,
|
||||
@Res() res: Response,
|
||||
@Body() body: Cards,
|
||||
) {
|
||||
const key = request.params.key;
|
||||
if (!key) {
|
||||
res.status(400).json({ error: "Missing 'key' field" });
|
||||
return;
|
||||
}
|
||||
const cards = await this.babyNamesService.saveUserCards(key, body);
|
||||
res.json(cards);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import axios from 'axios';
|
||||
import { uniq } from 'ramda';
|
||||
import { KvService } from 'src/kv/kv.service';
|
||||
import { MinioService } from 'src/minio/minio.service';
|
||||
|
||||
@@ -10,6 +11,16 @@ export interface Name {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface Cards {
|
||||
green: string[];
|
||||
red: string[];
|
||||
wild: string[];
|
||||
}
|
||||
|
||||
export interface CardsWithHistory extends Cards {
|
||||
removed?: Cards;
|
||||
}
|
||||
|
||||
export interface NameMap {
|
||||
[key: string]: Name[];
|
||||
}
|
||||
@@ -68,7 +79,7 @@ export class BabyNamesService {
|
||||
const currentKey = await this.minioService
|
||||
.getBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-current`,
|
||||
`baby-names/${userKey}-current.json`,
|
||||
)
|
||||
.then((buffer) => buffer.toString())
|
||||
.catch(() => null);
|
||||
@@ -77,22 +88,39 @@ export class BabyNamesService {
|
||||
}
|
||||
const currentNumber = parseInt(currentKey || '0', 10);
|
||||
|
||||
return currentNumber + 1;
|
||||
return currentNumber;
|
||||
}
|
||||
|
||||
public async writeUserNumber(userKey: string, number: number): Promise<void> {
|
||||
await this.minioService.uploadBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-current`,
|
||||
`baby-names/${userKey}-current.json`,
|
||||
Buffer.from(number.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
public async getUserCurrentName(userKey: string): Promise<{
|
||||
key: string;
|
||||
name: string;
|
||||
index: number;
|
||||
synonyms: string[];
|
||||
}> {
|
||||
const currentIndex = await this.getCurrentNumber(userKey);
|
||||
const name = this.nameList[currentIndex];
|
||||
const synonyms = this.getSynonyms(name);
|
||||
return {
|
||||
key: userKey,
|
||||
name,
|
||||
index: currentIndex,
|
||||
synonyms: synonyms,
|
||||
};
|
||||
}
|
||||
|
||||
public async getUserScores(userKey: string): Promise<UserScoreMap> {
|
||||
const scoresKey = await this.minioService
|
||||
.getBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-scores`,
|
||||
`baby-names/${userKey}-scores.json`,
|
||||
)
|
||||
.then((buffer) => buffer.toString())
|
||||
.catch(() => null);
|
||||
@@ -102,14 +130,44 @@ export class BabyNamesService {
|
||||
return JSON.parse(scoresKey);
|
||||
}
|
||||
|
||||
public async getUserCardsOrDefault(
|
||||
userKey: string,
|
||||
): Promise<CardsWithHistory> {
|
||||
const cardsKey = await this.minioService
|
||||
.getBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-cards.json`,
|
||||
)
|
||||
.then((buffer) => buffer.toString())
|
||||
.catch(() => null);
|
||||
if (cardsKey === null) {
|
||||
return { green: [], red: [], wild: [] };
|
||||
}
|
||||
return JSON.parse(cardsKey);
|
||||
}
|
||||
|
||||
public async saveUserCards(
|
||||
userKey: string,
|
||||
cards: Cards,
|
||||
): Promise<CardsWithHistory> {
|
||||
const existingCards = await this.getUserCardsOrDefault(userKey);
|
||||
const updatedCards = resolveRemovedCards(existingCards, cards);
|
||||
await this.minioService.uploadBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-cards.json`,
|
||||
Buffer.from(JSON.stringify(updatedCards, null, 2)),
|
||||
);
|
||||
return updatedCards;
|
||||
}
|
||||
|
||||
public async saveUserScores(
|
||||
userKey: string,
|
||||
scores: UserScoreMap,
|
||||
): Promise<void> {
|
||||
await this.minioService.uploadBuffer(
|
||||
this.minioService.defaultBucketName,
|
||||
`baby-names/${userKey}-scores`,
|
||||
Buffer.from(JSON.stringify(scores)),
|
||||
`baby-names/${userKey}-scores.json`,
|
||||
Buffer.from(JSON.stringify(scores, null, 2)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,3 +189,26 @@ export class BabyNamesService {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const resolveRemovedCards = (
|
||||
existingCards: CardsWithHistory,
|
||||
newCards: Cards,
|
||||
): CardsWithHistory => {
|
||||
return {
|
||||
...newCards,
|
||||
removed: {
|
||||
green: uniq([
|
||||
...(existingCards.removed?.green || []),
|
||||
...existingCards.green.filter((name) => !newCards.green.includes(name)),
|
||||
]),
|
||||
red: uniq([
|
||||
...(existingCards.removed?.red || []),
|
||||
...existingCards.red.filter((name) => !newCards.red.includes(name)),
|
||||
]),
|
||||
wild: uniq([
|
||||
...(existingCards.removed?.wild || []),
|
||||
...existingCards.wild.filter((name) => !newCards.wild.includes(name)),
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user