Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/voyage submission #133

Merged
merged 12 commits into from
Apr 17, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,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
Loading