diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 810a43d..8171f7c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -45,6 +45,7 @@ model user { RefreshToken refreshToken[] resetPassword resetPassword? favoriteDecks favoriteDeck[] + attempts cardAttempt[] @@index([email]) @@index([id]) @@ -83,40 +84,41 @@ model resetPassword { } model card { - id String @id @default(cuid()) + id String @id @default(cuid()) deckId String userId String - question String @db.Text - answer String @db.Text - shots Int @default(0) - questionImg String? @db.VarChar(500) - answerImg String? @db.VarChar(500) - answerVideo String? @db.VarChar(500) - questionVideo String? @db.VarChar(500) + question String @db.Text + answer String @db.Text + shots Int @default(0) + questionImg String? @db.VarChar(500) + answerImg String? @db.VarChar(500) + answerVideo String? @db.VarChar(500) + questionVideo String? @db.VarChar(500) comments String? type String? moreId String? - created DateTime @default(now()) - updated DateTime @updatedAt - author user @relation(fields: [userId], references: [id], onDelete: Cascade) - decks deck @relation(fields: [deckId], references: [id], onDelete: Cascade) + created DateTime @default(now()) + updated DateTime @updatedAt + author user @relation(fields: [userId], references: [id], onDelete: Cascade) + decks deck @relation(fields: [deckId], references: [id], onDelete: Cascade) grades grade[] + attempts cardAttempt[] @@index([userId]) @@index([deckId]) } model deck { - id String @id @default(cuid()) - userId String - name String - isPrivate Boolean @default(false) - cover String? @db.VarChar(500) - created DateTime @default(now()) - updated DateTime @updatedAt - author user @relation(fields: [userId], references: [id], onDelete: Cascade) - card card[] - grades grade[] + id String @id @default(cuid()) + userId String + name String + isPrivate Boolean @default(false) + cover String? @db.VarChar(500) + created DateTime @default(now()) + updated DateTime @updatedAt + author user @relation(fields: [userId], references: [id], onDelete: Cascade) + card card[] + grades grade[] favoritedBy favoriteDeck[] @@index([userId]) @@ -174,4 +176,18 @@ model favoriteDeck { @@unique([userId, deckId]) @@index([userId]) @@index([deckId]) -} \ No newline at end of file +} + +model cardAttempt { + id String @id @default(cuid()) + userId String + cardId String + attemptCount Int @default(1) + lastAttempt DateTime @default(now()) + user user @relation(fields: [userId], references: [id], onDelete: Cascade) + card card @relation(fields: [cardId], references: [id], onDelete: Cascade) + + @@unique([userId, cardId]) + @@index([userId]) + @@index([cardId]) +} diff --git a/src/modules/cards/infrastructure/cards.repository.ts b/src/modules/cards/infrastructure/cards.repository.ts index a5e3778..510681a 100644 --- a/src/modules/cards/infrastructure/cards.repository.ts +++ b/src/modules/cards/infrastructure/cards.repository.ts @@ -97,13 +97,14 @@ export class CardsRepository { const whereClause = whereParts.join(' OR ') const sqlQuery = ` - SELECT c.*, g.grade as "userGrade" - FROM flashcards.card AS c - LEFT JOIN flashcards.grade AS g ON c.id = g."cardId" AND g."userId" = $1 - WHERE c."deckId" = $2 AND (${whereClause}) - ORDER BY g."grade" ${direction === 'asc' ? 'ASC NULLS FIRST' : 'DESC NULLS LAST'} - LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2} -` + SELECT c.*, g.grade as "userGrade", COALESCE(a."attemptCount", 0) as "totalAttempts" + FROM flashcards.card AS c + LEFT JOIN flashcards.grade AS g ON c.id = g."cardId" AND g."userId" = $1 + LEFT JOIN flashcards.cardAttempt AS a ON c.id = a."cardId" AND a."userId" = $1 + WHERE c."deckId" = $2 AND (${whereClause}) + ORDER BY g."grade" ${direction === 'asc' ? 'ASC NULLS FIRST' : 'DESC NULLS LAST'} + LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2} + ` // Add itemsPerPage and start to the queryParams queryParams.push(itemsPerPage, start) @@ -113,13 +114,14 @@ export class CardsRepository { ...queryParams )) satisfies Array - const cards: CardWithGrades[] = cardsRaw.map(({ userGrade, ...card }) => ({ + const cards: CardWithGrades[] = cardsRaw.map(({ userGrade, totalAttempts, ...card }) => ({ ...card, grades: [ { grade: userGrade, }, ], + attempts: totalAttempts, })) const totalCount = await this.prisma.card.count({ where }) @@ -143,13 +145,33 @@ export class CardsRepository { grade: true, }, }, + attempts: { + where: { + userId, + }, + select: { + attemptCount: true, + }, + }, }, skip: (currentPage - 1) * itemsPerPage, take: itemsPerPage, }), ]) - return Pagination.transformPaginationData(result, { currentPage, itemsPerPage }) + const [totalCount, cardsRaw] = result + + const cards = cardsRaw.map(card => ({ + ...card, + shots: card.attempts.reduce((acc, attempt) => acc + attempt.attemptCount, 0), + })) + + console.log(cards) + + return Pagination.transformPaginationData([totalCount, cards], { + currentPage, + itemsPerPage, + }) } } catch (e) { this.logger.error(e?.message) @@ -159,7 +181,7 @@ export class CardsRepository { async findCardsByDeckIdWithGrade(userId: string, deckId: string) { try { - return this.prisma.card.findMany({ + const cards = await this.prisma.card.findMany({ where: { deckId, }, @@ -169,8 +191,18 @@ export class CardsRepository { userId, }, }, + attempts: { + where: { + userId, + }, + }, }, }) + + return cards.map(card => ({ + ...card, + shots: card.attempts.reduce((acc, attempt) => acc + attempt.attemptCount, 0), + })) } catch (e) { this.logger.error(e?.message) throw new InternalServerErrorException(e?.message) diff --git a/src/modules/decks/infrastructure/grades.repository.ts b/src/modules/decks/infrastructure/grades.repository.ts index a5b575a..071f38a 100644 --- a/src/modules/decks/infrastructure/grades.repository.ts +++ b/src/modules/decks/infrastructure/grades.repository.ts @@ -20,7 +20,7 @@ export class GradesRepository { grade: number }) { try { - return await this.prisma.grade.upsert({ + const gradeResult = await this.prisma.grade.upsert({ where: { cardId_userId: { cardId, @@ -57,6 +57,37 @@ export class GradesRepository { }, }, }) + + const attemptResult = await this.prisma.cardAttempt.upsert({ + where: { + userId_cardId: { + userId, + cardId, + }, + }, + update: { + attemptCount: { + increment: 1, + }, + lastAttempt: new Date(), + }, + create: { + attemptCount: 1, + lastAttempt: new Date(), + user: { + connect: { + id: userId, + }, + }, + card: { + connect: { + id: cardId, + }, + }, + }, + }) + + return { gradeResult, attemptResult } } catch (e) { this.logger.error(e?.message) throw new InternalServerErrorException(e?.message)