Skip to content

Commit 8e06b9c

Browse files
authored
Merge pull request #167 from topcoder-platform/pm-2177
feat(PM-2177): modified schema to support vote tracking
2 parents 7d15dfa + efc9371 commit 8e06b9c

File tree

4 files changed

+204
-29
lines changed

4 files changed

+204
-29
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `downVotes` on the `aiWorkflowRunItem` table. All the data in the column will be lost.
5+
- You are about to drop the column `upVotes` on the `aiWorkflowRunItem` table. All the data in the column will be lost.
6+
- You are about to drop the column `downVotes` on the `aiWorkflowRunItemComment` table. All the data in the column will be lost.
7+
- You are about to drop the column `upVotes` on the `aiWorkflowRunItemComment` table. All the data in the column will be lost.
8+
9+
*/
10+
-- CreateEnum
11+
CREATE TYPE "VoteType" AS ENUM ('UPVOTE', 'DOWNVOTE');
12+
13+
-- AlterTable
14+
ALTER TABLE "aiWorkflowRunItem" DROP COLUMN "downVotes",
15+
DROP COLUMN "upVotes";
16+
17+
-- AlterTable
18+
ALTER TABLE "aiWorkflowRunItemComment" DROP COLUMN "downVotes",
19+
DROP COLUMN "upVotes";
20+
21+
-- CreateTable
22+
CREATE TABLE "aiWorkflowRunItemVote" (
23+
"id" VARCHAR(14) NOT NULL DEFAULT nanoid(),
24+
"workflowRunItemId" VARCHAR(14) NOT NULL,
25+
"voteType" "VoteType" NOT NULL,
26+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
27+
"createdBy" TEXT NOT NULL,
28+
29+
CONSTRAINT "aiWorkflowRunItemVote_pkey" PRIMARY KEY ("id")
30+
);
31+
32+
-- CreateTable
33+
CREATE TABLE "aiWorkflowRunItemCommentVote" (
34+
"id" VARCHAR(14) NOT NULL DEFAULT nanoid(),
35+
"workflowRunItemCommentId" VARCHAR(14) NOT NULL,
36+
"voteType" "VoteType" NOT NULL,
37+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
38+
"createdBy" TEXT NOT NULL,
39+
40+
CONSTRAINT "aiWorkflowRunItemCommentVote_pkey" PRIMARY KEY ("id")
41+
);
42+
43+
-- CreateIndex
44+
CREATE INDEX "aiWorkflowRunItemVote_workflowRunItemId_idx" ON "aiWorkflowRunItemVote"("workflowRunItemId");
45+
46+
-- CreateIndex
47+
CREATE UNIQUE INDEX "aiWorkflowRunItemVote_workflowRunItemId_createdBy_key" ON "aiWorkflowRunItemVote"("workflowRunItemId", "createdBy");
48+
49+
-- CreateIndex
50+
CREATE INDEX "aiWorkflowRunItemCommentVote_workflowRunItemCommentId_idx" ON "aiWorkflowRunItemCommentVote"("workflowRunItemCommentId");
51+
52+
-- CreateIndex
53+
CREATE UNIQUE INDEX "aiWorkflowRunItemCommentVote_workflowRunItemCommentId_creat_key" ON "aiWorkflowRunItemCommentVote"("workflowRunItemCommentId", "createdBy");
54+
55+
-- AddForeignKey
56+
ALTER TABLE "aiWorkflowRunItemVote" ADD CONSTRAINT "aiWorkflowRunItemVote_workflowRunItemId_fkey" FOREIGN KEY ("workflowRunItemId") REFERENCES "aiWorkflowRunItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
57+
58+
-- AddForeignKey
59+
ALTER TABLE "aiWorkflowRunItemCommentVote" ADD CONSTRAINT "aiWorkflowRunItemCommentVote_workflowRunItemCommentId_fkey" FOREIGN KEY ("workflowRunItemCommentId") REFERENCES "aiWorkflowRunItemComment"("id") ON DELETE CASCADE ON UPDATE CASCADE;

prisma/schema.prisma

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,37 @@ model llmModel {
643643
workflows aiWorkflow[]
644644
}
645645

646+
model aiWorkflowRunItemVote {
647+
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
648+
workflowRunItemId String @db.VarChar(14)
649+
voteType VoteType
650+
createdAt DateTime @default(now()) @db.Timestamp(3)
651+
createdBy String @db.Text
652+
653+
item aiWorkflowRunItem @relation(fields: [workflowRunItemId], references: [id], onDelete: Cascade)
654+
655+
@@index([workflowRunItemId])
656+
@@unique([workflowRunItemId, createdBy])
657+
}
658+
659+
model aiWorkflowRunItemCommentVote {
660+
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
661+
workflowRunItemCommentId String @db.VarChar(14)
662+
voteType VoteType
663+
createdAt DateTime @default(now()) @db.Timestamp(3)
664+
createdBy String @db.Text
665+
666+
comment aiWorkflowRunItemComment @relation(fields: [workflowRunItemCommentId], references: [id], onDelete: Cascade)
667+
668+
@@index([workflowRunItemCommentId])
669+
@@unique([workflowRunItemCommentId, createdBy])
670+
}
671+
672+
enum VoteType {
673+
UPVOTE
674+
DOWNVOTE
675+
}
676+
646677
model aiWorkflow {
647678
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
648679
name String @unique @db.VarChar
@@ -690,24 +721,22 @@ model aiWorkflowRunItem {
690721
workflowRunId String @db.VarChar(14)
691722
scorecardQuestionId String @db.VarChar(14)
692723
content String @db.Text
693-
upVotes Int @default(0)
694-
downVotes Int @default(0)
695724
questionScore Float? @db.DoublePrecision
696725
createdAt DateTime @db.Timestamp(3)
697726
createdBy String? @db.Text
698727
699728
run aiWorkflowRun @relation(fields: [workflowRunId], references: [id])
700729
question scorecardQuestion @relation(fields: [scorecardQuestionId], references: [id])
701730
comments aiWorkflowRunItemComment[]
731+
732+
votes aiWorkflowRunItemVote[]
702733
}
703734

704735
model aiWorkflowRunItemComment {
705736
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
706737
workflowRunItemId String @db.VarChar(14)
707738
userId String @db.Text
708739
content String @db.Text
709-
upVotes Int @default(0)
710-
downVotes Int @default(0)
711740
parentId String? @db.VarChar(14)
712741
createdAt DateTime @default(now()) @db.Timestamp(3)
713742
createdBy String? @db.Text
@@ -717,4 +746,6 @@ model aiWorkflowRunItemComment {
717746
item aiWorkflowRunItem @relation(fields: [workflowRunItemId], references: [id])
718747
parent aiWorkflowRunItemComment? @relation("CommentHierarchy", fields: [parentId], references: [id])
719748
replies aiWorkflowRunItemComment[] @relation("CommentHierarchy")
749+
750+
votes aiWorkflowRunItemCommentVote[]
720751
}

src/api/ai-workflow/ai-workflow.service.ts

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { UserRole } from 'src/shared/enums/userRole.enum';
2727
import { ChallengeStatus } from 'src/shared/enums/challengeStatus.enum';
2828
import { LoggerService } from 'src/shared/modules/global/logger.service';
2929
import { GiteaService } from 'src/shared/modules/global/gitea.service';
30+
import { VoteType } from '@prisma/client';
3031

3132
@Injectable()
3233
export class AiWorkflowService {
@@ -96,7 +97,49 @@ export class AiWorkflowService {
9697
);
9798
}
9899

99-
const allowedFields = ['content', 'upVotes', 'downVotes'];
100+
// Handle vote updates
101+
if (patchData.upVote !== undefined || patchData.downVote !== undefined) {
102+
if (!user.userId) {
103+
throw new BadRequestException('User id is not available');
104+
}
105+
106+
// Remove existing votes by this user for this comment
107+
await this.prisma.aiWorkflowRunItemCommentVote.deleteMany({
108+
where: {
109+
workflowRunItemCommentId: commentId,
110+
createdBy: user.userId.toString(),
111+
},
112+
});
113+
114+
// Add new vote if upVote or downVote is true
115+
if (patchData.upVote) {
116+
await this.prisma.aiWorkflowRunItemCommentVote.create({
117+
data: {
118+
workflowRunItemCommentId: commentId,
119+
voteType: VoteType.UPVOTE,
120+
createdBy: user.userId.toString(),
121+
},
122+
});
123+
} else if (patchData.downVote) {
124+
await this.prisma.aiWorkflowRunItemCommentVote.create({
125+
data: {
126+
workflowRunItemCommentId: commentId,
127+
voteType: VoteType.DOWNVOTE,
128+
createdBy: user.userId.toString(),
129+
},
130+
});
131+
}
132+
133+
delete patchData.downVote;
134+
delete patchData.upVote;
135+
}
136+
137+
// No other fields to update apart from likes
138+
if (Object.keys(patchData).length === 0) {
139+
return;
140+
}
141+
142+
const allowedFields = ['content'];
100143
const updateData: any = {};
101144
for (const key of allowedFields) {
102145
if (key in patchData) {
@@ -388,8 +431,6 @@ export class AiWorkflowService {
388431
workflowRunId: runId,
389432
scorecardQuestionId: item.scorecardQuestionId,
390433
content: item.content,
391-
upVotes: item.upVotes ?? 0,
392-
downVotes: item.downVotes ?? 0,
393434
questionScore: item.questionScore ?? null,
394435
createdAt: new Date(),
395436
// TODO: Remove this once prisma middleware implementation is done
@@ -771,7 +812,12 @@ export class AiWorkflowService {
771812
const items = await this.prisma.aiWorkflowRunItem.findMany({
772813
where: { workflowRunId: runId },
773814
include: {
774-
comments: true,
815+
comments: {
816+
include: {
817+
votes: true,
818+
}
819+
},
820+
votes: true,
775821
},
776822
orderBy: {
777823
createdAt: 'asc',
@@ -820,15 +866,6 @@ export class AiWorkflowService {
820866
);
821867
}
822868

823-
const updateData: any = {};
824-
825-
if (patchData.upVotes !== undefined) {
826-
updateData.upVotes = patchData.upVotes;
827-
}
828-
if (patchData.downVotes !== undefined) {
829-
updateData.downVotes = patchData.downVotes;
830-
}
831-
832869
if (!user.isMachine) {
833870
const keys = Object.keys(patchData);
834871
const prohibitedKeys = ['content', 'questionScore'];
@@ -839,21 +876,70 @@ export class AiWorkflowService {
839876
}
840877
}
841878

842-
// Update properties which can be updated only via m2m
879+
if (patchData.upVote !== undefined || patchData.downVote !== undefined) {
880+
// Remove existing votes by this user for this item
881+
if (!user.userId) {
882+
throw new BadRequestException('User id is not available');
883+
}
884+
885+
await this.prisma.aiWorkflowRunItemVote.deleteMany({
886+
where: {
887+
workflowRunItemId: itemId,
888+
createdBy: user.userId.toString(),
889+
},
890+
});
891+
892+
// Add new vote if upVote or downVote is true
893+
if (patchData.upVote) {
894+
await this.prisma.aiWorkflowRunItemVote.create({
895+
data: {
896+
workflowRunItemId: itemId,
897+
voteType: VoteType.UPVOTE,
898+
createdBy: user.userId.toString(),
899+
},
900+
});
901+
} else if (patchData.downVote) {
902+
await this.prisma.aiWorkflowRunItemVote.create({
903+
data: {
904+
workflowRunItemId: itemId,
905+
voteType: VoteType.DOWNVOTE,
906+
createdBy: user.userId.toString(),
907+
},
908+
});
909+
}
910+
911+
delete patchData.downVote;
912+
delete patchData.upVote;
913+
}
914+
915+
// Update other properties only allowed for machine users
916+
const updateData: any = {};
843917
if (user.isMachine) {
844918
if (patchData.content) {
845919
updateData.content = patchData.content;
846920
}
847-
848921
if (patchData.questionScore) {
849922
updateData.questionScore = patchData.questionScore;
850923
}
851924
}
852925

926+
// If there are no other fields to update
927+
// just return the run item
928+
if (Object.keys(updateData).length === 0) {
929+
return this.prisma.aiWorkflowRunItem.findUnique({
930+
where: { id: itemId },
931+
include: {
932+
comments: true,
933+
votes: true,
934+
},
935+
});
936+
}
937+
853938
return this.prisma.aiWorkflowRunItem.update({
854939
where: { id: itemId },
855940
include: {
856941
comments: true,
942+
votes: true,
857943
},
858944
data: updateData,
859945
});

src/dto/aiWorkflow.dto.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
IsUUID,
1818
Max,
1919
IsEmpty,
20+
IsBoolean,
2021
} from 'class-validator';
2122
import { Type, Transform } from 'class-transformer';
2223

@@ -134,15 +135,13 @@ export class CreateAiWorkflowRunItemDto {
134135

135136
@ApiProperty({ required: false })
136137
@IsOptional()
137-
@IsInt()
138-
@Min(0)
139-
upVotes?: number;
138+
@IsBoolean()
139+
upVote?: boolean;
140140

141141
@ApiProperty({ required: false })
142142
@IsOptional()
143-
@IsInt()
144-
@Min(0)
145-
downVotes?: number;
143+
@IsBoolean()
144+
downVote?: boolean;
146145

147146
@ApiProperty({ required: false })
148147
@IsOptional()
@@ -202,14 +201,14 @@ export class UpdateRunItemCommentDto {
202201
content?: string;
203202

204203
@ApiProperty({ required: false })
205-
@IsInt()
204+
@IsBoolean()
206205
@IsOptional()
207-
upVotes?: number;
206+
upVote?: boolean;
208207

209208
@ApiProperty({ required: false })
210-
@IsInt()
209+
@IsBoolean()
211210
@IsOptional()
212-
downVotes?: number;
211+
downVote?: boolean;
213212

214213
@ApiHideProperty()
215214
@IsEmpty({ message: 'parentId cannot be updated' })

0 commit comments

Comments
 (0)