mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-26 20:59:28 +00:00
fix: refactor two commands in saving grade to prevent race conditions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||
import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'
|
||||
import { pick } from 'remeda'
|
||||
|
||||
import {
|
||||
createPrismaOrderBy,
|
||||
@@ -224,4 +225,73 @@ export class CardsRepository {
|
||||
throw new InternalServerErrorException(e?.message)
|
||||
}
|
||||
}
|
||||
|
||||
private async getSmartRandomCard(cards: Array<CardWithGrade>): Promise<CardWithGrade> {
|
||||
const selectionPool: Array<CardWithGrade> = []
|
||||
|
||||
cards.forEach(card => {
|
||||
// Calculate the average grade for the card
|
||||
const averageGrade =
|
||||
card.grades.length === 0
|
||||
? 0
|
||||
: card.grades.reduce((acc, grade) => acc + grade.grade, 0) / card.grades.length
|
||||
// Calculate weight for the card, higher weight for lower grade card
|
||||
const weight = 6 - averageGrade
|
||||
|
||||
// Add the card to the selection pool `weight` times
|
||||
for (let i = 0; i < weight; i++) {
|
||||
selectionPool.push(card)
|
||||
}
|
||||
})
|
||||
|
||||
return selectionPool[Math.floor(Math.random() * selectionPool.length)]
|
||||
}
|
||||
|
||||
private async getNotDuplicateRandomCard(
|
||||
cards: Array<CardWithGrade>,
|
||||
previousCardId: string
|
||||
): Promise<CardWithGrade> {
|
||||
const randomCard = await this.getSmartRandomCard(cards)
|
||||
|
||||
if (!randomCard) {
|
||||
this.logger.error(`No cards found in deck}`, {
|
||||
previousCardId,
|
||||
randomCard,
|
||||
cards,
|
||||
})
|
||||
throw new NotFoundException(`No cards found in deck`)
|
||||
}
|
||||
if (randomCard.id === previousCardId && cards.length !== 1) {
|
||||
return this.getNotDuplicateRandomCard(cards, previousCardId)
|
||||
}
|
||||
|
||||
return randomCard
|
||||
}
|
||||
|
||||
async getRandomCardInDeck(deckId: string, userId: string, previousCardId: string) {
|
||||
const cards = await this.findCardsByDeckIdWithGrade(userId, deckId)
|
||||
|
||||
if (!cards.length) {
|
||||
throw new NotFoundException(`No cards found in deck with id ${deckId}`)
|
||||
}
|
||||
|
||||
const smartRandomCard = await this.getNotDuplicateRandomCard(cards, previousCardId)
|
||||
|
||||
return {
|
||||
...pick(smartRandomCard, [
|
||||
'id',
|
||||
'question',
|
||||
'answer',
|
||||
'deckId',
|
||||
'questionImg',
|
||||
'answerImg',
|
||||
'questionVideo',
|
||||
'answerVideo',
|
||||
'created',
|
||||
'updated',
|
||||
'shots',
|
||||
]),
|
||||
grade: smartRandomCard.grades[0]?.grade || 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user