Add song & search history (#165)
This commit is contained in:
4
.envrc
Normal file
4
.envrc
Normal file
@@ -0,0 +1,4 @@
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
|
||||
fi
|
||||
use flake
|
||||
78
back/package-lock.json
generated
78
back/package-lock.json
generated
@@ -17,7 +17,7 @@
|
||||
"@nestjs/passport": "^8.2.2",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"@nestjs/swagger": "^5.2.1",
|
||||
"@prisma/client": "^3.14.0",
|
||||
"@prisma/client": "^4.4.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/passport": "^1.0.9",
|
||||
@@ -46,7 +46,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"prettier": "^2.3.2",
|
||||
"prisma": "^3.13.0",
|
||||
"prisma": "^4.4.0",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
@@ -1683,15 +1683,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.14.0.tgz",
|
||||
"integrity": "sha512-atb41UpgTR1MCst0VIbiHTMw8lmXnwUvE1KyUCAkq08+wJyjRE78Due+nSf+7uwqQn+fBFYVmoojtinhlLOSaA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
|
||||
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.6"
|
||||
"node": ">=14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
@@ -1703,16 +1703,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b.tgz",
|
||||
"integrity": "sha512-Ip9CcCeUocH61eXu4BUGpvl5KleQyhcUVLpWCv+0ZmDv44bFaDpREqjGHHdRupvPN/ugB6gTlD9b9ewdj02yVA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a.tgz",
|
||||
"integrity": "sha512-D+yHzq4a2r2Rrd0ZOW/mTZbgDIkUkD8ofKgusEI1xPiZz60Daks+UM7Me2ty5FzH3p/TgyhBpRrfIHx+ha20RQ=="
|
||||
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||
},
|
||||
"node_modules/@sinonjs/commons": {
|
||||
"version": "1.8.3",
|
||||
@@ -7304,21 +7304,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-3.13.0.tgz",
|
||||
"integrity": "sha512-oO1auBnBtieGdiN+57IgsA9Vr7Sy4HkILi1KSaUG4mpKfEbnkTGnLOxAqjLed+K2nsG/GtE1tJBtB7JxN1a78Q==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b",
|
||||
"ts-pattern": "^4.0.1"
|
||||
"@prisma/engines": "4.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
"prisma2": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.6"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
@@ -8618,12 +8617,6 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-pattern": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.0.2.tgz",
|
||||
"integrity": "sha512-eHqR/7A6fcw05vCOfnL6RwgGJbVi9G/YHTdYdjYmElhDdJ1SMn7pWs+6+YuxygaFwQS/g+cIDlu+UD8IVpur1A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/tsconfig-paths": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
|
||||
@@ -10435,23 +10428,23 @@
|
||||
}
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.14.0.tgz",
|
||||
"integrity": "sha512-atb41UpgTR1MCst0VIbiHTMw8lmXnwUvE1KyUCAkq08+wJyjRE78Due+nSf+7uwqQn+fBFYVmoojtinhlLOSaA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
|
||||
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b.tgz",
|
||||
"integrity": "sha512-Ip9CcCeUocH61eXu4BUGpvl5KleQyhcUVLpWCv+0ZmDv44bFaDpREqjGHHdRupvPN/ugB6gTlD9b9ewdj02yVA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a.tgz",
|
||||
"integrity": "sha512-D+yHzq4a2r2Rrd0ZOW/mTZbgDIkUkD8ofKgusEI1xPiZz60Daks+UM7Me2ty5FzH3p/TgyhBpRrfIHx+ha20RQ=="
|
||||
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.3",
|
||||
@@ -14767,13 +14760,12 @@
|
||||
}
|
||||
},
|
||||
"prisma": {
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-3.13.0.tgz",
|
||||
"integrity": "sha512-oO1auBnBtieGdiN+57IgsA9Vr7Sy4HkILi1KSaUG4mpKfEbnkTGnLOxAqjLed+K2nsG/GtE1tJBtB7JxN1a78Q==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b",
|
||||
"ts-pattern": "^4.0.1"
|
||||
"@prisma/engines": "4.4.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
@@ -15715,12 +15707,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ts-pattern": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.0.2.tgz",
|
||||
"integrity": "sha512-eHqR/7A6fcw05vCOfnL6RwgGJbVi9G/YHTdYdjYmElhDdJ1SMn7pWs+6+YuxygaFwQS/g+cIDlu+UD8IVpur1A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"tsconfig-paths": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@nestjs/passport": "^8.2.2",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"@nestjs/swagger": "^5.2.1",
|
||||
"@prisma/client": "^3.14.0",
|
||||
"@prisma/client": "^4.4.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/passport": "^1.0.9",
|
||||
@@ -58,7 +58,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"prettier": "^2.3.2",
|
||||
"prisma": "^3.13.0",
|
||||
"prisma": "^4.4.0",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "SongHistory" (
|
||||
"songID" INTEGER NOT NULL,
|
||||
"userID" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "SongHistory_pkey" PRIMARY KEY ("songID","userID")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SongHistory" ADD CONSTRAINT "SongHistory_songID_fkey" FOREIGN KEY ("songID") REFERENCES "Song"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SongHistory" ADD CONSTRAINT "SongHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `score` to the `SongHistory` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "SongHistory" ADD COLUMN "score" INTEGER NOT NULL;
|
||||
14
back/prisma/migrations/20230227022300_/migration.sql
Normal file
14
back/prisma/migrations/20230227022300_/migration.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `difficulties` to the `SongHistory` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "SongHistory" DROP CONSTRAINT "SongHistory_userID_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "SongHistory" ADD COLUMN "difficulties" JSONB NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SongHistory" ADD CONSTRAINT "SongHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
11
back/prisma/migrations/20230228020324_/migration.sql
Normal file
11
back/prisma/migrations/20230228020324_/migration.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "SearchHistory" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"query" TEXT NOT NULL,
|
||||
"userId" INTEGER,
|
||||
|
||||
CONSTRAINT "SearchHistory_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SearchHistory" ADD CONSTRAINT "SearchHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
8
back/prisma/migrations/20230228022322_/migration.sql
Normal file
8
back/prisma/migrations/20230228022322_/migration.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `type` to the `SearchHistory` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "SearchHistory" ADD COLUMN "type" TEXT NOT NULL;
|
||||
5
back/prisma/migrations/20230301021027_/migration.sql
Normal file
5
back/prisma/migrations/20230301021027_/migration.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "SongHistory" DROP CONSTRAINT "SongHistory_songID_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SongHistory" ADD CONSTRAINT "SongHistory_songID_fkey" FOREIGN KEY ("songID") REFERENCES "Song"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -15,20 +15,42 @@ model User {
|
||||
password String
|
||||
email String
|
||||
LessonHistory LessonHistory[]
|
||||
SongHistory SongHistory[]
|
||||
searchHistory SearchHistory[]
|
||||
}
|
||||
|
||||
model SearchHistory {
|
||||
id Int @id @default(autoincrement())
|
||||
query String
|
||||
type String
|
||||
userId Int?
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
}
|
||||
|
||||
model Song {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
midiPath String
|
||||
musicXmlPath String
|
||||
artistId Int?
|
||||
artist Artist? @relation(fields: [artistId], references: [id])
|
||||
artist Artist? @relation(fields: [artistId], references: [id])
|
||||
albumId Int?
|
||||
album Album? @relation(fields: [albumId], references: [id])
|
||||
album Album? @relation(fields: [albumId], references: [id])
|
||||
genreId Int?
|
||||
genre Genre? @relation(fields: [genreId], references: [id])
|
||||
genre Genre? @relation(fields: [genreId], references: [id])
|
||||
difficulties Json
|
||||
SongHistory SongHistory[]
|
||||
}
|
||||
|
||||
model SongHistory {
|
||||
song Song @relation(fields: [songID], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
songID Int
|
||||
user User @relation(fields: [userID], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
userID Int
|
||||
score Int
|
||||
difficulties Json
|
||||
|
||||
@@id([songID, userID])
|
||||
}
|
||||
|
||||
model Genre {
|
||||
@@ -42,16 +64,16 @@ model Artist {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
|
||||
Song Song[]
|
||||
Song Song[]
|
||||
Album Album[]
|
||||
}
|
||||
|
||||
model Album {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
artistId Int?
|
||||
artist Artist? @relation(fields: [artistId], references: [id])
|
||||
Song Song[]
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
artistId Int?
|
||||
artist Artist? @relation(fields: [artistId], references: [id])
|
||||
Song Song[]
|
||||
}
|
||||
|
||||
model Lesson {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { AlbumModule } from './album/album.module';
|
||||
import { SearchController } from './search/search.controller';
|
||||
import { SearchService } from './search/search.service';
|
||||
import { SearchModule } from './search/search.module';
|
||||
import { HistoryModule } from './history/history.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -27,6 +28,7 @@ import { SearchModule } from './search/search.module';
|
||||
ArtistModule,
|
||||
AlbumModule,
|
||||
SearchModule,
|
||||
HistoryModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, PrismaService, ArtistService],
|
||||
|
||||
14
back/src/history/dto/SearchHistoryDto.ts
Normal file
14
back/src/history/dto/SearchHistoryDto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNumber } from "class-validator";
|
||||
|
||||
export class SearchHistoryDto {
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
userID: number;
|
||||
|
||||
@ApiProperty()
|
||||
query: string;
|
||||
|
||||
@ApiProperty()
|
||||
type: "song" | "artist" | "album";
|
||||
}
|
||||
19
back/src/history/dto/SongHistoryDto.ts
Normal file
19
back/src/history/dto/SongHistoryDto.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNumber } from "class-validator";
|
||||
|
||||
export class SongHistoryDto {
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
songID: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
userID: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
score: number;
|
||||
|
||||
@ApiProperty()
|
||||
difficulties: Record<string, number>
|
||||
}
|
||||
18
back/src/history/history.controller.spec.ts
Normal file
18
back/src/history/history.controller.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HistoryController } from './history.controller';
|
||||
|
||||
describe('HistoryController', () => {
|
||||
let controller: HistoryController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [HistoryController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<HistoryController>(HistoryController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
42
back/src/history/history.controller.ts
Normal file
42
back/src/history/history.controller.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Body, Controller, DefaultValuePipe, Get, HttpCode, ParseIntPipe, Post, Query, Request, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger';
|
||||
import { SearchHistory, SongHistory } from '@prisma/client';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { SongHistoryDto } from './dto/SongHistoryDto';
|
||||
import { HistoryService } from './history.service';
|
||||
|
||||
@Controller('history')
|
||||
@ApiTags("history")
|
||||
export class HistoryController {
|
||||
constructor(private readonly historyService: HistoryService) {}
|
||||
|
||||
@Get()
|
||||
@HttpCode(200)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
async getHistory(
|
||||
@Request() req: any,
|
||||
@Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
): Promise<SongHistory[]> {
|
||||
return this.historyService.getHistory(req.user.id, { skip, take });
|
||||
}
|
||||
|
||||
@Get("search")
|
||||
@HttpCode(200)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
async getSearchHistory(
|
||||
@Request() req: any,
|
||||
@Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
): Promise<SearchHistory[]> {
|
||||
return this.historyService.getSearchHistory(req.user.id, { skip, take });
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HttpCode(201)
|
||||
async create(@Body() record: SongHistoryDto): Promise<SongHistory> {
|
||||
return this.historyService.createSongHistoryRecord(record);
|
||||
}
|
||||
}
|
||||
12
back/src/history/history.module.ts
Normal file
12
back/src/history/history.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { HistoryService } from './history.service';
|
||||
import { HistoryController } from './history.controller';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [HistoryService],
|
||||
controllers: [HistoryController],
|
||||
exports: [HistoryService],
|
||||
})
|
||||
export class HistoryModule {}
|
||||
18
back/src/history/history.service.spec.ts
Normal file
18
back/src/history/history.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HistoryService } from './history.service';
|
||||
|
||||
describe('HistoryService', () => {
|
||||
let service: HistoryService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [HistoryService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<HistoryService>(HistoryService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
59
back/src/history/history.service.ts
Normal file
59
back/src/history/history.service.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { SearchHistory, SongHistory } from '@prisma/client';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { SearchHistoryDto } from './dto/SearchHistoryDto';
|
||||
import { SongHistoryDto } from './dto/SongHistoryDto';
|
||||
|
||||
@Injectable()
|
||||
export class HistoryService {
|
||||
constructor(private prisma: PrismaService) { }
|
||||
|
||||
async createSongHistoryRecord({ songID, userID, score, difficulties }: SongHistoryDto): Promise<SongHistory> {
|
||||
return this.prisma.songHistory.create({
|
||||
data: {
|
||||
score,
|
||||
difficulties,
|
||||
song: {
|
||||
connect: {
|
||||
id: songID,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: userID,
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getHistory(playerId: number, { skip, take }: { skip?: number, take?: number }): Promise<SongHistory[]> {
|
||||
return this.prisma.songHistory.findMany({
|
||||
where: { user: { id: playerId } },
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
}
|
||||
|
||||
async createSearchHistoryRecord({ userID, query, type }: SearchHistoryDto): Promise<SearchHistory> {
|
||||
return this.prisma.searchHistory.create({
|
||||
data: {
|
||||
query,
|
||||
type,
|
||||
user: {
|
||||
connect: {
|
||||
id: userID,
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getSearchHistory(playerId: number, { skip, take }: { skip?: number, take?: number }): Promise<SearchHistory[]> {
|
||||
return this.prisma.searchHistory.findMany({
|
||||
where: { user: { id: playerId } },
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,35 @@ import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
DefaultValuePipe,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
Request,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
|
||||
import { Song } from '@prisma/client';
|
||||
import { SongService } from 'src/song/song.service';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { SearchSongDto } from './dto/search-song.dto';
|
||||
import { SearchService } from './search.service';
|
||||
|
||||
@ApiTags('search')
|
||||
@Controller('search')
|
||||
export class SearchController {
|
||||
constructor(
|
||||
private readonly searchService: SearchService,
|
||||
private readonly songService: SongService,
|
||||
) {}
|
||||
constructor(private readonly searchService: SearchService) { }
|
||||
|
||||
@ApiOperation({
|
||||
summary: 'Get a song details by song name',
|
||||
description: 'Get a song details by song name',
|
||||
})
|
||||
@Get('song/:name')
|
||||
async findByName(@Param('name') name: string): Promise<Song | null> {
|
||||
const ret = await this.searchService.songByTitle({ name });
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async findByName(@Request() req: any, @Param('name') name: string): Promise<Song | null> {
|
||||
const ret = await this.searchService.songByTitle({ name }, req.user?.id);
|
||||
if (!ret) throw new NotFoundException();
|
||||
return ret;
|
||||
}
|
||||
@@ -113,20 +109,22 @@ export class SearchController {
|
||||
example: 'Yoko Shimomura',
|
||||
})
|
||||
@ApiParam({ name: 'type', type: 'string', required: true, example: 'artist' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async guess(
|
||||
@Request() req: any,
|
||||
@Param() params: { type: string; word: string },
|
||||
): Promise<any[] | null> {
|
||||
try {
|
||||
let ret: any[];
|
||||
switch (params.type) {
|
||||
case 'artist':
|
||||
ret = await this.searchService.guessArtist(params.word);
|
||||
ret = await this.searchService.guessArtist(params.word, req.user?.id);
|
||||
break;
|
||||
case 'album':
|
||||
ret = await this.searchService.guessAlbum(params.word);
|
||||
ret = await this.searchService.guessAlbum(params.word, req.user?.id);
|
||||
break;
|
||||
case 'song':
|
||||
ret = await this.searchService.guessSong(params.word);
|
||||
ret = await this.searchService.guessSong(params.word, req.user?.id);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SearchService } from './search.service';
|
||||
import { SearchController } from './search.controller';
|
||||
import { HistoryModule } from 'src/history/history.module';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { SongService } from 'src/song/song.service';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, HistoryModule],
|
||||
controllers: [SearchController],
|
||||
providers: [SearchService, SongService],
|
||||
exports: [SearchService],
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import {
|
||||
DefaultValuePipe,
|
||||
Injectable,
|
||||
ParseIntPipe,
|
||||
Query,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Album, Artist, Prisma, Song } from '@prisma/client';
|
||||
import { HistoryService } from 'src/history/history.service';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private prisma: PrismaService, private history: HistoryService) { }
|
||||
|
||||
async songByTitle(
|
||||
songWhereUniqueInput: Prisma.SongWhereUniqueInput,
|
||||
userID: number
|
||||
): Promise<Song | null> {
|
||||
if (songWhereUniqueInput.name)
|
||||
await this.history.createSearchHistoryRecord({ query: songWhereUniqueInput.name, userID, type: "song" });
|
||||
return this.prisma.song.findUnique({
|
||||
where: songWhereUniqueInput,
|
||||
});
|
||||
@@ -53,7 +51,8 @@ export class SearchService {
|
||||
});
|
||||
}
|
||||
|
||||
async guessSong(word: string): Promise<Song[]> {
|
||||
async guessSong(word: string, userID: number): Promise<Song[]> {
|
||||
await this.history.createSearchHistoryRecord({ query: word, type: "song", userID });
|
||||
return this.prisma.song.findMany({
|
||||
where: {
|
||||
name: { contains: word },
|
||||
@@ -61,7 +60,8 @@ export class SearchService {
|
||||
});
|
||||
}
|
||||
|
||||
async guessArtist(word: string): Promise<Artist[]> {
|
||||
async guessArtist(word: string, userID: number): Promise<Artist[]> {
|
||||
await this.history.createSearchHistoryRecord({ query: word, type: "artist", userID });
|
||||
return this.prisma.artist.findMany({
|
||||
where: {
|
||||
name: { contains: word },
|
||||
@@ -69,7 +69,8 @@ export class SearchService {
|
||||
});
|
||||
}
|
||||
|
||||
async guessAlbum(word: string): Promise<Album[]> {
|
||||
async guessAlbum(word: string, userID: number): Promise<Album[]> {
|
||||
await this.history.createSearchHistoryRecord({ query: word, type: "album", userID });
|
||||
return this.prisma.album.findMany({
|
||||
where: {
|
||||
name: { contains: word },
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { User, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
|
||||
47
back/test/robot/auth/auth.resource
Normal file
47
back/test/robot/auth/auth.resource
Normal file
@@ -0,0 +1,47 @@
|
||||
*** Settings ***
|
||||
Documentation Methods to login/register.
|
||||
|
||||
Resource ../rest.resource
|
||||
|
||||
|
||||
*** Keywords ***
|
||||
Login
|
||||
[Documentation] Shortcut to login with the given username for future requests
|
||||
[Arguments] ${username}
|
||||
&{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"}
|
||||
Output
|
||||
Integer response status 200
|
||||
String response body access_token
|
||||
Set Headers {"Authorization": "Bearer ${res.body.access_token}"}
|
||||
|
||||
Register
|
||||
[Documentation] Shortcut to register with the given username for future requests
|
||||
[Arguments] ${username}
|
||||
&{res}= POST
|
||||
... /auth/register
|
||||
... {"username": "${username}", "password": "password-${username}", "email": "${username}@chromacase.moe"}
|
||||
Output
|
||||
Integer response status 201
|
||||
|
||||
RegisterLogin
|
||||
[Documentation] Shortcut to register with the given username for future requests
|
||||
[Arguments] ${username}
|
||||
POST
|
||||
... /auth/register
|
||||
... {"username": "${username}", "password": "password-${username}", "email": "${username}@chromacase.moe"}
|
||||
Output
|
||||
Integer response status 201
|
||||
&{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"}
|
||||
Output
|
||||
Integer response status 200
|
||||
String response body access_token
|
||||
Set Headers {"Authorization": "Bearer ${res.body.access_token}"}
|
||||
|
||||
&{me}= GET /auth/me
|
||||
Output
|
||||
Integer response status 200
|
||||
RETURN ${me.body.id}
|
||||
|
||||
Logout
|
||||
[Documentation] Logout the current user, only the local client is affected.
|
||||
Set Headers {"Authorization": ""}
|
||||
@@ -1,31 +1,9 @@
|
||||
*** Settings ***
|
||||
Documentation Tests of the /auth route.
|
||||
... Ensures that the user can authenticate on kyoo.
|
||||
|
||||
Resource ../rest.resource
|
||||
|
||||
|
||||
*** Keywords ***
|
||||
Login
|
||||
[Documentation] Shortcut to login with the given username for future requests
|
||||
[Arguments] ${username}
|
||||
&{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"}
|
||||
Output
|
||||
Integer response status 200
|
||||
String response body access_token
|
||||
Set Headers {"Authorization": "Bearer ${res.body.access_token}"}
|
||||
|
||||
Register
|
||||
[Documentation] Shortcut to register with the given username for future requests
|
||||
[Arguments] ${username}
|
||||
&{res}= POST
|
||||
... /auth/register
|
||||
... {"username": "${username}", "password": "password-${username}", "email": "${username}@chromacase.moe"}
|
||||
Output
|
||||
Integer response status 201
|
||||
|
||||
Logout
|
||||
[Documentation] Logout the current user, only the local client is affected.
|
||||
Set Headers {"Authorization": ""}
|
||||
Resource ./auth.resource
|
||||
|
||||
|
||||
*** Test Cases ***
|
||||
@@ -43,7 +21,7 @@ Bad Account
|
||||
RegisterAndLogin
|
||||
[Documentation] Create a new user and login in it
|
||||
Register user-1
|
||||
Login user-1
|
||||
Login user-1
|
||||
[Teardown] DELETE /auth/me
|
||||
|
||||
Register Duplicates
|
||||
@@ -53,13 +31,13 @@ Register Duplicates
|
||||
POST /auth/register {"username": "user-duplicate", "password": "pass", "email": "mail@kyoo.moe"}
|
||||
Output
|
||||
Integer response status 400
|
||||
Login user-duplicate
|
||||
Login user-duplicate
|
||||
[Teardown] DELETE /auth/me
|
||||
|
||||
Delete Account
|
||||
[Documentation] Check if a user can delete it's account
|
||||
Register I-should-be-deleted
|
||||
Login I-should-be-deleted
|
||||
Login I-should-be-deleted
|
||||
DELETE /auth/me
|
||||
Output
|
||||
Integer response status 200
|
||||
@@ -67,7 +45,7 @@ Delete Account
|
||||
Login
|
||||
[Documentation] Create a new user and login in it
|
||||
Register login-user
|
||||
Login login-user
|
||||
Login login-user
|
||||
${res}= GET /auth/me
|
||||
Output
|
||||
Integer response status 200
|
||||
|
||||
55
back/test/robot/history/history.robot
Normal file
55
back/test/robot/history/history.robot
Normal file
@@ -0,0 +1,55 @@
|
||||
*** Settings ***
|
||||
Documentation Tests of the /history route.
|
||||
... Ensures that the history CRUD works corectly.
|
||||
|
||||
Resource ../rest.resource
|
||||
Resource ../auth/auth.resource
|
||||
|
||||
|
||||
*** Test Cases ***
|
||||
Get history without behing connected
|
||||
&{history}= GET /history
|
||||
Output
|
||||
Integer response status 401
|
||||
|
||||
Create and get an history record
|
||||
[Documentation] Create an history item
|
||||
&{song}= POST
|
||||
... /song
|
||||
... {"name": "Mama mia", "difficulties": {}, "midiPath": "/musics/Beethoven-125-4.midi", "musicXmlPath": "/musics/Beethoven-125-4.mxl"}
|
||||
Output
|
||||
${userID}= RegisterLogin wowuser
|
||||
|
||||
&{history}= POST
|
||||
... /history
|
||||
... { "userID": ${userID}, "songID": ${song.body.id}, "score": 55, "difficulties": {} }
|
||||
Output
|
||||
Integer response status 201
|
||||
|
||||
&{res}= GET /history
|
||||
Output
|
||||
Integer response status 200
|
||||
Array response body
|
||||
Integer $[0].userID ${userID}
|
||||
Integer $[0].songID ${song.body.id}
|
||||
Integer $[0].score 55
|
||||
|
||||
[Teardown] Run Keywords DELETE /users/${userID}
|
||||
... AND DELETE /song/${song.body.id}
|
||||
|
||||
Create and get a search history record
|
||||
[Documentation] Create a search history item
|
||||
${userID}= RegisterLogin historyqueryuser
|
||||
|
||||
GET /search/song/toto
|
||||
Output
|
||||
Integer response status 404
|
||||
|
||||
&{res}= GET /history/search
|
||||
Output
|
||||
Integer response status 200
|
||||
Array response body
|
||||
String $[0].type "song"
|
||||
String $[0].query "toto"
|
||||
|
||||
[Teardown] DELETE /users/${userID}
|
||||
@@ -25,6 +25,8 @@ services:
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
@@ -32,7 +34,7 @@ services:
|
||||
retries: 5
|
||||
|
||||
front:
|
||||
build:
|
||||
build:
|
||||
context: ./front
|
||||
args:
|
||||
- API_URL=${API_URL}
|
||||
@@ -43,3 +45,6 @@ services:
|
||||
- "back"
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
volumes:
|
||||
db:
|
||||
|
||||
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1665573177,
|
||||
"narHash": "sha256-Arkrf3zmi3lXYpbSe9H+HQxswQ6jxsAmeQVq5Sr/OZc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d2afb051ffd904af5a825f58abee3c63b148c5f2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "master",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
28
flake.nix
Normal file
28
flake.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
description = "A prisma test project";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/master";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
devShell = pkgs.mkShell {
|
||||
nativeBuildInputs = [ pkgs.bashInteractive ];
|
||||
buildInputs = with pkgs; [
|
||||
nodePackages.prisma
|
||||
nodePackages."@nestjs/cli"
|
||||
nodePackages.npm
|
||||
nodejs-slim
|
||||
];
|
||||
shellHook = with pkgs; ''
|
||||
export PRISMA_MIGRATION_ENGINE_BINARY="${prisma-engines}/bin/migration-engine"
|
||||
export PRISMA_QUERY_ENGINE_BINARY="${prisma-engines}/bin/query-engine"
|
||||
export PRISMA_QUERY_ENGINE_LIBRARY="${prisma-engines}/lib/libquery_engine.node"
|
||||
export PRISMA_INTROSPECTION_ENGINE_BINARY="${prisma-engines}/bin/introspection-engine"
|
||||
export PRISMA_FMT_BINARY="${prisma-engines}/bin/prisma-fmt"
|
||||
export DATABASE_URL=postgresql://user:eip@localhost:5432/chromacase
|
||||
'';
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -17,9 +17,15 @@ channels:
|
||||
type:
|
||||
type: "string"
|
||||
enum: ["start"]
|
||||
name:
|
||||
id:
|
||||
type: "number"
|
||||
description: "The id of the song"
|
||||
mode:
|
||||
type: "string"
|
||||
description: "The name of the song"
|
||||
enum: ["practice", "normal"]
|
||||
user_id:
|
||||
type: "number"
|
||||
description: "The ID of the user playing"
|
||||
operationId: "startSong"
|
||||
/midi:
|
||||
publish:
|
||||
|
||||
@@ -133,7 +133,7 @@ class Scorometer():
|
||||
|
||||
def getTimingScore(self, key: Key, to_play: Key):
|
||||
tempo_percent = abs((key.duration / to_play.duration) - 1)
|
||||
if tempo_percent < .3 :
|
||||
if tempo_percent < .3:
|
||||
timingScore = "perfect"
|
||||
elif tempo_percent < .5:
|
||||
timingScore = f"great"
|
||||
@@ -161,9 +161,6 @@ class Scorometer():
|
||||
if obj["type"] == "pause":
|
||||
pass
|
||||
|
||||
def sendEnd(self, overall, difficulties):
|
||||
send({"overallScore": overall, "score": difficulties})
|
||||
|
||||
def sendScore(self, id, timingScore, timingInformation):
|
||||
send({"id": id, "timingScore": timingScore, "timingInformation": timingInformation})
|
||||
|
||||
@@ -177,7 +174,7 @@ class Scorometer():
|
||||
self.handleMessage(line.rstrip())
|
||||
else:
|
||||
pass
|
||||
self.sendEnd(self.score, {})
|
||||
return self.score, {}
|
||||
|
||||
def handleStartMessage(start_message):
|
||||
if "type" not in start_message.keys():
|
||||
@@ -188,19 +185,34 @@ def handleStartMessage(start_message):
|
||||
raise Exception("id of song not specified in start message")
|
||||
if "mode" not in start_message.keys():
|
||||
raise Exception("mode of song not specified in start message")
|
||||
if "user_id" not in start_message.keys():
|
||||
raise Exception("user_id not specified in start message")
|
||||
mode = PRACTICE if start_message["mode"] == "practice" else NORMAL
|
||||
# TODO get song path from the API
|
||||
song_id = start_message["id"]
|
||||
# TODO: use something secure here but I don't find sending a jwt something elegant.
|
||||
user_id = start_message["user_id"]
|
||||
song_path = requests.get(f"http://back:3000/song/{song_id}").json()["midiPath"]
|
||||
return mode, song_path
|
||||
return mode, song_path, song_id, user_id
|
||||
|
||||
|
||||
def sendScore(score, difficulties, song_id, user_id):
|
||||
send({"overallScore": score, "score": difficulties})
|
||||
requests.post(f"http://back:3000/history", json={
|
||||
"songID": song_id,
|
||||
"userID": user_id,
|
||||
"score": score,
|
||||
"difficulties": difficulties,
|
||||
})
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
start_message = json.loads(input())
|
||||
mode, song_path = handleStartMessage(start_message)
|
||||
mode, song_path, song_id, user_id = handleStartMessage(start_message)
|
||||
sc = Scorometer(mode, song_path)
|
||||
sc.gameLoop()
|
||||
score, difficulties = sc.gameLoop()
|
||||
sendScore(score, difficulties, song_id, user_id)
|
||||
except Exception as error:
|
||||
send({ "error": error })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user