Skip to content

Commit ac47f69

Browse files
fix(domain-acl): create closeChallenge method
1 parent 20accda commit ac47f69

36 files changed

+5835
-195
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"dotenv": "^16.0.3",
2323
"grpc-server-reflection": "^0.1.5",
2424
"lodash": "^4.17.21",
25+
"moment": "^2.29.4",
26+
"momentjs": "^2.0.0",
2527
"source-map-support": "^0.5.21",
2628
"topcoder-interface": "github:topcoder-platform/plat-interface-definition#feat/challenge-acl-proto",
2729
"uuidv4": "^6.2.13"

src/config/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const PhaseStatusIds = {
2+
Scheduled: 1,
3+
Open: 2,
4+
Closed: 3
5+
};
6+
7+
export const PhaseTypeIds = {
8+
Submission: 2,
9+
Review: 4,
10+
IterativeReview: 18
11+
};

src/domain/LegacyChallenge.ts

Lines changed: 207 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import {
55
CheckChallengeExistsResponse, CloseChallengeInput, LegacyChallenge, LegacyChallengeId, UpdateChallengeInput,
66
} from "../models/domain-layer/legacy/challenge";
77
import { ProjectSchema } from "../schema/project/Project";
8+
import LegacyPhaseDomain from "./Phase";
9+
import LegacyReviewDomain from "./Review";
10+
import LegacyResourceDomain from "./Resource";
11+
import LegacyPrizeDomain from "./Prize";
12+
import { PhaseStatusIds, PhaseTypeIds } from '../config/constants';
13+
import moment from "moment";
814

915
class LegacyChallengeDomain {
1016

@@ -13,7 +19,207 @@ class LegacyChallengeDomain {
1319
}
1420

1521
public async closeChallenge(input:CloseChallengeInput) {
16-
// TODO: close challenge
22+
// Get the challenge phases:
23+
const { projectPhases } = await LegacyPhaseDomain.getProjectPhases({ projectId: input.projectId })
24+
25+
// close the submission phase
26+
const submissionPhase = _.find(projectPhases, p => p.phaseTypeId === PhaseTypeIds.Submission)
27+
if (!submissionPhase) throw new Error("Cannot find submission phase");
28+
if (submissionPhase) {
29+
if (submissionPhase.phaseStatusId === PhaseStatusIds.Open) {
30+
await LegacyPhaseDomain.updateProjectPhase({
31+
phaseStatusId: PhaseStatusIds.Closed,
32+
projectPhaseId: submissionPhase.projectPhaseId,
33+
actualEndTime: "CURRENT"
34+
});
35+
} else if (submissionPhase.phaseStatusId === PhaseStatusIds.Scheduled) {
36+
await LegacyPhaseDomain.updateProjectPhase({
37+
phaseStatusId: PhaseStatusIds.Closed,
38+
projectPhaseId: submissionPhase.projectPhaseId,
39+
actualEndTime: "CURRENT",
40+
actualStartTime: "CURRENT"
41+
});
42+
}
43+
}
44+
45+
// Open review phase
46+
const reviewPhases = _.filter(projectPhases, p => _.includes([PhaseTypeIds.Review, PhaseTypeIds.IterativeReview], p.phaseTypeId) && p.phaseStatusId !== PhaseStatusIds.Closed)
47+
_.each(reviewPhases, async (p) => {
48+
await LegacyPhaseDomain.updateProjectPhase({
49+
phaseStatusId: PhaseStatusIds.Open,
50+
projectPhaseId: p.projectPhaseId,
51+
actualStartTime: "CURRENT"
52+
});
53+
});
54+
55+
// Get winner resource
56+
const { resources } = await LegacyResourceDomain.getResources({
57+
projectId: input.projectId
58+
});
59+
const winner = _.find(resources, r => r.userId === input.winnerId && r.resourceRoleId === 1);
60+
if (!winner) throw new Error("Cannot close challenge without winner");
61+
// Get winner's submission:
62+
const submission = await LegacyReviewDomain.getSubmission({
63+
projectId: input.projectId,
64+
submissionStatusId: 1,
65+
uploadStatusId: 1,
66+
resourceId: winner.resourceId
67+
});
68+
let submissionId = 0;
69+
70+
const prize = await LegacyPrizeDomain.getSingle({
71+
projectId: input.projectId,
72+
prizeTypeId: 15,
73+
place: 1
74+
})
75+
if (!prize) throw new Error("cannot close challenge without prize")
76+
77+
if (submission) {
78+
submissionId = submission.submissionId;
79+
await LegacyReviewDomain.updateSubmission({
80+
submissionId: submission.submissionId,
81+
initialScore: 100,
82+
finalScore: 100,
83+
placement: 1,
84+
prizeId: prize.prizeId
85+
})
86+
} else {
87+
// Create the missing submission in order to close the challenge
88+
const upload = await LegacyReviewDomain.createUpload({
89+
projectId: input.projectId,
90+
projectPhaseId: submissionPhase.projectPhaseId,
91+
resourceId: winner.resourceId,
92+
uploadTypeId: 1,
93+
uploadStatusId: 1,
94+
parameter: "parameter",
95+
});
96+
const uploadId = upload.kind ? _.get(upload.kind, upload.kind?.$case, undefined) : undefined
97+
if (uploadId) {
98+
const createSubmissionRes = await LegacyReviewDomain.createSubmission({
99+
uploadId,
100+
submissionStatusId: 1,
101+
initialScore: 100,
102+
finalScore: 100,
103+
placement: 1,
104+
submissionTypeId: 1,
105+
prizeId: prize.prizeId
106+
})
107+
if (createSubmissionRes.kind) {
108+
submissionId = _.get(createSubmissionRes.kind, createSubmissionRes.kind?.$case, 0)
109+
}
110+
if (!submissionId) throw new Error("Failed to create submission");
111+
}
112+
}
113+
114+
const reviewers = _.filter(resources, r => r.resourceRoleId === 4 || r.resourceRoleId === 21)
115+
116+
for (const reviewer of reviewers) {
117+
// Remove all review item comments
118+
await LegacyReviewDomain.deleteReviewItemComment(reviewer.resourceId)
119+
// Remove all review items
120+
const { items: reviewItems } = await LegacyReviewDomain.getReviews(reviewer.resourceId)
121+
for (const r of reviewItems) {
122+
await LegacyReviewDomain.deleteReviewItem(r.reviewId)
123+
}
124+
// Remove all review comments
125+
await LegacyReviewDomain.deleteReviewComment(reviewer.resourceId)
126+
// Remove all reviews
127+
await LegacyReviewDomain.deleteReview(reviewer.resourceId)
128+
// Remove all resource_info
129+
await LegacyResourceDomain.deleteResourceInfos({ resourceId: reviewer.resourceId })
130+
131+
// Remove all reviewers
132+
await LegacyResourceDomain.deleteResources({
133+
projectId: input.projectId,
134+
resourceRoleId: 4 // Reviewer
135+
})
136+
await LegacyResourceDomain.deleteResources({
137+
projectId: input.projectId,
138+
resourceRoleId: 21 // Iterative Reviewer
139+
})
140+
}
141+
142+
// Create new reviewer using current user's id (22838965 - tcwebservice)
143+
const createResourceRes = await LegacyResourceDomain.createResource({
144+
resourceRoleId: 4,
145+
projectId: input.projectId,
146+
userId: 22838965 // TODO: get this from interceptors
147+
})
148+
const reviewerResourceId = createResourceRes.kind ? _.get(createResourceRes.kind, createResourceRes.kind?.$case, undefined) : undefined
149+
if (!reviewerResourceId) throw new Error("error creating resource");
150+
await LegacyResourceDomain.createResourceInfos({
151+
resourceId: reviewerResourceId,
152+
resourceInfoTypeId: 1,
153+
value: "22838965"
154+
})
155+
await LegacyResourceDomain.createResourceInfos({
156+
resourceId: reviewerResourceId,
157+
resourceInfoTypeId: 2,
158+
value: "tcwebservice"
159+
})
160+
await LegacyResourceDomain.createResourceInfos({
161+
resourceId: reviewerResourceId,
162+
resourceInfoTypeId: 6,
163+
value: moment().format('MM.dd.yyyy hh:mm a')
164+
})
165+
await LegacyResourceDomain.createResourceInfos({
166+
resourceId: reviewerResourceId,
167+
resourceInfoTypeId: 7,
168+
value: "N/A"
169+
})
170+
await LegacyResourceDomain.createResourceInfos({
171+
resourceId: reviewerResourceId,
172+
resourceInfoTypeId: 8,
173+
value: "N/A"
174+
})
175+
await LegacyResourceDomain.createResourceInfos({
176+
resourceId: reviewerResourceId,
177+
resourceInfoTypeId: 15,
178+
value: "true"
179+
})
180+
// Get scorecard id
181+
const { phaseCriteriaList } = await LegacyPhaseDomain.getPhaseCriteria({
182+
projectPhaseId: reviewPhases[0].projectPhaseId, // Assuming there will only be one (currently open) review phase
183+
phaseCriteriaTypeId: 1
184+
})
185+
let scorecardId = 0;
186+
if (phaseCriteriaList[0].parameter) {
187+
scorecardId = _.toNumber(phaseCriteriaList[0].parameter);
188+
}
189+
// Create review
190+
const createReviewRes = await LegacyReviewDomain.createReview({
191+
resourceId: reviewerResourceId,
192+
submissionId,
193+
projectPhaseId: reviewPhases[0].projectPhaseId,
194+
scorecardId,
195+
committed: 1,
196+
score: 100,
197+
initialScore: 100
198+
})
199+
const createdReviewId = createReviewRes.kind ? _.get(createReviewRes.kind, createReviewRes.kind?.$case, undefined) : undefined
200+
if (!createdReviewId) throw new Error("cannot create review")
201+
// Get scorecard questions
202+
const { items: scorecardGroups } = await LegacyReviewDomain.getScorecardGroups(scorecardId);
203+
for (const sg of scorecardGroups) {
204+
const { items: scorecardSections } = await LegacyReviewDomain.getScorecardSections(sg.scorecardGroupId)
205+
for (const ss of scorecardSections) {
206+
const createReviewItemRes = await LegacyReviewDomain.createReviewItem({
207+
reviewId: createdReviewId,
208+
scorecardQuestionId: ss.scorecardSectionId,
209+
answer: "10",
210+
sort: ss.sort
211+
})
212+
const createdReviewItemId = createReviewItemRes.kind ? _.get(createReviewItemRes.kind, createReviewItemRes.kind?.$case, undefined) : undefined
213+
if (!createdReviewItemId) throw new Error("cannot create review item")
214+
await LegacyReviewDomain.createReviewItemComment({
215+
resourceId: reviewerResourceId,
216+
reviewItemId: createdReviewItemId,
217+
commentTypeId: 1,
218+
content: "Ok",
219+
sort: ss.sort
220+
})
221+
}
222+
}
17223
}
18224

19225
public async update(input:UpdateChallengeInput) {

src/domain/Phase.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Operator, QueryBuilder } from "@topcoder-framework/client-relational";
22
import { CreateResult } from "@topcoder-framework/lib-common";
33
import _ from "lodash";
44
import { queryRunner } from "../helper/QueryRunner";
5-
import { CreatePhaseDependencyInput, CreateProjectPhaseInput, DeleteProjectPhasesInput, GetProjectPhasesInput, PhaseType, PhaseTypeList, ProjectPhase, ProjectPhaseList, UpdateProjectPhaseInput } from "../models/domain-layer/legacy/phase";
5+
import { CreatePhaseDependencyInput, CreateProjectPhaseInput, DeleteProjectPhasesInput, GetPhaseCriteriaInput, GetProjectPhasesInput, PhaseType, PhaseTypeList, ProjectPhase, ProjectPhaseList, UpdateProjectPhaseInput } from "../models/domain-layer/legacy/phase";
66
import { CreatePhaseCriteriaInput, DeletePhaseCriteriaInput, PhaseCriteria, PhaseCriteriaList } from "../models/domain-layer/legacy/phase";
77
import { PhaseCriteriaSchema } from "../schema/project/PhaseCriteria";
88
import { PhaseDependencySchema } from "../schema/project/PhaseDependency";
@@ -21,14 +21,26 @@ class LegacyPhaseDomain {
2121
return { phaseTypes: rows!.map(r => PhaseType.fromPartial(r as PhaseType)) };
2222
}
2323

24-
public async getPhaseCriteria(): Promise<PhaseCriteriaList|undefined> {
24+
public async getPhaseCriteria(input:GetPhaseCriteriaInput): Promise<PhaseCriteriaList> {
2525
const query = new QueryBuilder(PhaseCriteriaSchema)
2626
.select(..._.map(PhaseCriteriaSchema.columns))
27+
.where(PhaseCriteriaSchema.columns.projectPhaseId, Operator.OPERATOR_EQUAL, {
28+
value: {
29+
$case: "intValue",
30+
intValue: input.projectPhaseId,
31+
},
32+
})
33+
.andWhere(PhaseCriteriaSchema.columns.phaseCriteriaTypeId, Operator.OPERATOR_EQUAL, {
34+
value: {
35+
$case: "intValue",
36+
intValue: input.phaseCriteriaTypeId,
37+
},
38+
})
2739
.limit(500)
2840
.build();
2941

3042
const { rows } = await queryRunner.run(query);
31-
return rows?.length ? { phaseCriteriaList: rows.map(r => PhaseCriteria.fromPartial(r as PhaseCriteria)) } : undefined
43+
return { phaseCriteriaList: rows!.map(r => PhaseCriteria.fromPartial(r as PhaseCriteria)) };
3244
}
3345

3446
public async createPhaseCriteria(input:CreatePhaseCriteriaInput): Promise<CreateResult> {
@@ -73,7 +85,7 @@ class LegacyPhaseDomain {
7385
);
7486
}
7587

76-
public async getProjectPhases(input:GetProjectPhasesInput): Promise<ProjectPhaseList|undefined> {
88+
public async getProjectPhases(input:GetProjectPhasesInput): Promise<ProjectPhaseList> {
7789
let query = new QueryBuilder(ProjectPhaseSchema)
7890
.select(..._.map(ProjectPhaseSchema.columns))
7991
.where(ProjectPhaseSchema.columns.projectId, Operator.OPERATOR_EQUAL, {

src/domain/Prize.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { QueryBuilder } from "@topcoder-framework/client-relational";
1+
import { Operator, QueryBuilder } from "@topcoder-framework/client-relational";
22
import { CreateResult, ScanCriteria } from "@topcoder-framework/lib-common";
33
import { queryRunner } from "../helper/QueryRunner";
4-
import { CreatePrizeInput, Prize, PrizeList } from "../models/domain-layer/legacy/prize";
4+
import { CreatePrizeInput, GetSinglePrizeInput, Prize, PrizeList } from "../models/domain-layer/legacy/prize";
55
import { PrizeSchema } from "../schema/project_payment/Prize";
6+
import _ from "lodash";
67

78
class PrizeDomain {
89
public async create(input: CreatePrizeInput): Promise<CreateResult> {
@@ -24,6 +25,34 @@ class PrizeDomain {
2425
};
2526
}
2627

28+
public async getSingle(input: GetSinglePrizeInput): Promise<Prize|undefined> {
29+
const { rows } = await queryRunner.run(
30+
new QueryBuilder(PrizeSchema)
31+
.select(..._.map(PrizeSchema.columns))
32+
.where(PrizeSchema.columns.projectId, Operator.OPERATOR_EQUAL, {
33+
value: {
34+
$case: "intValue",
35+
intValue: input.projectId,
36+
},
37+
})
38+
.andWhere(PrizeSchema.columns.prizeTypeId, Operator.OPERATOR_EQUAL, {
39+
value: {
40+
$case: "intValue",
41+
intValue: input.prizeTypeId,
42+
}
43+
})
44+
.andWhere(PrizeSchema.columns.place, Operator.OPERATOR_EQUAL, {
45+
value: {
46+
$case: "intValue",
47+
intValue: input.place,
48+
},
49+
})
50+
.build()
51+
);
52+
53+
return rows && rows.length ? Prize.fromPartial(rows[0] as Prize) : undefined;
54+
}
55+
2756
public async scan(criteria: ScanCriteria): Promise<PrizeList> {
2857
const { rows: prizes } = await queryRunner.run(
2958
new QueryBuilder(PrizeSchema)

0 commit comments

Comments
 (0)