Skip to content

Commit

Permalink
Merge pull request #133 from chingu-x/feature/voyage-submission
Browse files Browse the repository at this point in the history
Feature/voyage submission
  • Loading branch information
cherylli committed Apr 17, 2024
2 parents 9ad8509 + 3aa6109 commit 94b06f0
Show file tree
Hide file tree
Showing 20 changed files with 731 additions and 64 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Another example [here](https://co-pilot.dev/changelog)
- Add check in form response table, seed data, POST endpoint for submitting check in form ([#126](https://github.com/chingu-x/chingu-dashboard-be/pull/126))
- Add multiple device support ([#128](https://github.com/chingu-x/chingu-dashboard-be/pull/128))
- Add voyage project submission form seed ([#131](https://github.com/chingu-x/chingu-dashboard-be/pull/131))
- Add voyage project submission controller, service, e2e tests, responses seed ([#133](https://github.com/chingu-x/chingu-dashboard-be/pull/133))

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- CreateTable
CREATE TABLE "FormResponseVoyageProject" (
"id" SERIAL NOT NULL,
"voyageTeamId" INTEGER NOT NULL,
"responseGroupId" INTEGER NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "FormResponseVoyageProject_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "FormResponseVoyageProject_voyageTeamId_key" ON "FormResponseVoyageProject"("voyageTeamId");

-- CreateIndex
CREATE UNIQUE INDEX "FormResponseVoyageProject_responseGroupId_key" ON "FormResponseVoyageProject"("responseGroupId");

-- AddForeignKey
ALTER TABLE "FormResponseVoyageProject" ADD CONSTRAINT "FormResponseVoyageProject_voyageTeamId_fkey" FOREIGN KEY ("voyageTeamId") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "FormResponseVoyageProject" ADD CONSTRAINT "FormResponseVoyageProject_responseGroupId_fkey" FOREIGN KEY ("responseGroupId") REFERENCES "ResponseGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
37 changes: 25 additions & 12 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ model Gender {
}

model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
emailVerified Boolean @default(false)
id String @id @default(uuid()) @db.Uuid
email String @unique
emailVerified Boolean @default(false)
password String
firstName String?
lastName String?
Expand All @@ -46,7 +46,7 @@ model User {
discordId String?
twitterId String?
linkedinId String?
gender Gender? @relation(fields: [genderId], references: [id], onDelete: SetNull, onUpdate: Cascade)
gender Gender? @relation(fields: [genderId], references: [id], onDelete: SetNull, onUpdate: Cascade)
genderId Int?
countryCode String?
timezone String?
Expand Down Expand Up @@ -197,9 +197,10 @@ model VoyageTeam {
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt
voyageTeamMembers VoyageTeamMember[]
teamTechStackItems TeamTechStackItem[]
teamMeetings TeamMeeting[]
voyageTeamMembers VoyageTeamMember[]
teamTechStackItems TeamTechStackItem[]
teamMeetings TeamMeeting[]
FormResponseVoyageProject FormResponseVoyageProject?
}

model VoyageTeamMember {
Expand Down Expand Up @@ -542,11 +543,12 @@ model ResponseGroup {
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt
responses Response[]
soloProject SoloProject?
voyageApplication VoyageApplication?
formResponseMeeting FormResponseMeeting?
formResponseCheckin FormResponseCheckin?
responses Response[]
soloProject SoloProject?
voyageApplication VoyageApplication?
formResponseMeeting FormResponseMeeting?
formResponseCheckin FormResponseCheckin?
formResponseVoyageProject FormResponseVoyageProject?
}

model FormResponseMeeting {
Expand Down Expand Up @@ -580,3 +582,14 @@ model FormResponseCheckin {
@@unique(fields: [voyageTeamMemberId, sprintId], name: "voyageTeamMemberSprintId")
}

model FormResponseVoyageProject {
id Int @id @default(autoincrement())
voyageTeamId Int @unique
voyageTeam VoyageTeam @relation(fields: [voyageTeamId], references: [id], onDelete: Cascade)
responseGroupId Int @unique
responseGroup ResponseGroup @relation(fields: [responseGroupId], references: [id], onDelete: Restrict)
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt
}
5 changes: 5 additions & 0 deletions prisma/seed/forms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { populateVoyageApplicationForm } from "./voyage-app";
import { populateVoyageSubmissionForm } from "./voyage-project-submission";
import { prisma } from "../prisma-client";
import { FormTitles } from "../../../src/global/constants/formTitles";
import { populateCheckinFormResponse } from "../responses/checkinform-responses";
import { populateVoyageProjectSubmissionFormResponses } from "../responses/voyage-project-form-response";

export const populateFormsAndResponses = async () => {
// test option choices for Voyage Application form
// TODO: maybe move these to somewhere else (I don't think these are currently in use)
await prisma.optionChoice.create({
data: {
optionGroup: {
Expand Down Expand Up @@ -141,9 +144,11 @@ export const populateFormsAndResponses = async () => {

// Sprints checkin form
await populateCheckinForm();
await populateCheckinFormResponse();
await populateSoloProjectForm();
await populateVoyageApplicationForm();
await populateVoyageSubmissionForm();
await populateVoyageProjectSubmissionFormResponses();

console.log("Forms, Questions and Responses populated.");
};
2 changes: 1 addition & 1 deletion prisma/seed/forms/voyage-project-submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const populateVoyageSubmissionForm = async () => {
order: 8,
inputType: {
connect: {
name: "radioGroup",
name: "radio",
},
},
text: "On a scale of 0-10, how likely are you to suggest Chingu to a friend or colleague?",
Expand Down
45 changes: 45 additions & 0 deletions prisma/seed/responses/checkinform-responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { prisma } from "../prisma-client";
import { getQuestionsByFormTitle, populateQuestionResponses } from "./helper";
import { FormTitles } from "../../../src/global/constants/formTitles";

export const populateCheckinFormResponse = async () => {
const teamMemberId = 1;
const teamMember = await prisma.voyageTeamMember.findUnique({
where: {
id: teamMemberId,
},
select: {
id: true,
voyageTeam: {
select: {
voyage: {
select: {
sprints: true,
},
},
},
},
},
});

// get questions
const questions = await getQuestionsByFormTitle(FormTitles.sprintCheckin);

const responseGroup = await prisma.responseGroup.create({
data: {},
});

await Promise.all(
questions.map((question) => {
populateQuestionResponses(question, responseGroup.id, teamMemberId);
}),
);

await prisma.formResponseCheckin.create({
data: {
voyageTeamMemberId: teamMember.id,
sprintId: teamMember.voyageTeam.voyage.sprints[0].id,
responseGroupId: responseGroup.id,
},
});
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { formSelect } from "../../src/forms/forms.service";
import { prisma } from "./prisma-client";
import { prisma } from "../prisma-client";
import { formSelect } from "../../../src/forms/forms.service";

// TODO: move these to a helper function file
/*
generates a random option IDs (in an array) given an option group id
params:
numberOfChoices - size of the returned array
*/
const getRandomOptionId = async (
optionGroupId: number,
numberOfChoices: number,
Expand All @@ -21,6 +26,10 @@ const getRandomOptionId = async (
return choicesArray;
};

/*
gets all members in the voyage member with the given teamMemberId
this is used for input type "TeamMemberCheckboxes"
*/
const getTeamMembers = async (teamMemberId: number) => {
const team = await prisma.voyageTeam.findFirst({
where: {
Expand All @@ -45,10 +54,18 @@ const getTeamMembers = async (teamMemberId: number) => {
return team.voyageTeamMembers.map((m) => m.member.discordId);
};

const populateQuestionResponses = async (
/*
populates responses for a given question
params:
question - question (which includes id, inputType, etc)
responseGroupId - this is the group where all the responses for this particular submission are linked to
teamMemberId - optional, defaulted to 0, only used for teamMember inputType
*/
export const populateQuestionResponses = async (
question: any,
teamMemberId: number,
responseGroupId: number,
teamMemberId: number = 0, // this is only used if it's a teamMember type input
) => {
const data: any = {
questionId: question.id,
Expand Down Expand Up @@ -93,7 +110,7 @@ const populateQuestionResponses = async (
optionGroupId: true,
},
});
// popuate all subquestions
// populate all subquestions
await Promise.all(
subQuestions.map((subq) => {
// assign subquestion optionGroupId to be same as parent, as it's null for subquestions
Expand Down Expand Up @@ -130,6 +147,13 @@ const populateQuestionResponses = async (
break;
}
case "teamMembersCheckbox": {
if (teamMemberId === 0) {
console.log(question);
throw new Error(
`teamMemberId required for input type ${question.inputType.name} (question id:${question.id}).`,
);
}

const selectedTeamMembers = await getTeamMembers(teamMemberId);
await prisma.response.create({
data: {
Expand Down Expand Up @@ -165,35 +189,15 @@ const populateQuestionResponses = async (
}
};

export const populateCheckinFormResponse = async () => {
const teamMemberId = 1;
const teamMember = await prisma.voyageTeamMember.findUnique({
where: {
id: teamMemberId,
},
select: {
id: true,
voyageTeam: {
select: {
voyage: {
select: {
sprints: true,
},
},
},
},
},
});

// get questions
export const getQuestionsByFormTitle = async (formTitle: string) => {
const checkinForm = await prisma.form.findUnique({
where: {
title: "Sprint Check-in",
title: formTitle,
},
select: formSelect,
});

const questions = await prisma.question.findMany({
return prisma.question.findMany({
where: {
formId: checkinForm.id,
parentQuestionId: null,
Expand All @@ -213,22 +217,4 @@ export const populateCheckinFormResponse = async () => {
order: "asc",
},
});

const responseGroup = await prisma.responseGroup.create({
data: {},
});

await Promise.all(
questions.map((question) => {
populateQuestionResponses(question, teamMemberId, responseGroup.id);
}),
);

await prisma.formResponseCheckin.create({
data: {
voyageTeamMemberId: teamMember.id,
sprintId: teamMember.voyageTeam.voyage.sprints[0].id,
responseGroupId: responseGroup.id,
},
});
};
28 changes: 28 additions & 0 deletions prisma/seed/responses/voyage-project-form-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { prisma } from "../prisma-client";
import { FormTitles } from "../../../src/global/constants/formTitles";
import { getQuestionsByFormTitle, populateQuestionResponses } from "./helper";

export const populateVoyageProjectSubmissionFormResponses = async () => {
const voyageTeamId = 1; //hardcoded voyage team Id

const questions = await getQuestionsByFormTitle(
FormTitles.voyageProjectSubmission,
);

const responseGroup = await prisma.responseGroup.create({
data: {},
});

await Promise.all(
questions.map((question) => {
populateQuestionResponses(question, responseGroup.id);
}),
);

await prisma.formResponseVoyageProject.create({
data: {
voyageTeamId: voyageTeamId,
responseGroupId: responseGroup.id,
},
});
};
2 changes: 0 additions & 2 deletions prisma/seed/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { populateMeetings } from "./meetings";
import { populateSoloProjects } from "./solo-project";
import { populateVoyageApplications } from "./voyage-app";
import { populateChecklists } from "./checklist";
import { populateCheckinFormResponse } from "./checkinform-responses";
import { prisma } from "./prisma-client";

export const deleteAllTables = async () => {
Expand Down Expand Up @@ -46,7 +45,6 @@ export const seed = async () => {
await populateSoloProjects();
await populateVoyageApplications();
await populateChecklists();
await populateCheckinFormResponse();
console.log("===\n🌱 Database seeding completed.\n===");

prisma.$disconnect();
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { RolesGuard } from "./auth/guards/roles.guard";
import { PermissionsGuard } from "./auth/guards/permissions.guard";
import { ScheduleModule } from "@nestjs/schedule";
import { TasksModule } from "./tasks/tasks.module";
import { VoyagesModule } from "./voyages/voyages.module";

@Module({
imports: [
Expand Down Expand Up @@ -52,6 +53,7 @@ import { TasksModule } from "./tasks/tasks.module";
GlobalModule,
ScheduleModule.forRoot(),
TasksModule,
VoyagesModule,
],
controllers: [HealthCheckController],
providers: [
Expand Down
Loading

0 comments on commit 94b06f0

Please sign in to comment.