Skip to content

Commit

Permalink
Merge branch 'develop' into 1313-Add-Instrument-filter-to-Proposal-an…
Browse files Browse the repository at this point in the history
…d-Assignments
  • Loading branch information
RasmiaKulan authored Feb 21, 2025
2 parents 9a0615e + 2eb1367 commit 723f6a6
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 103 deletions.
58 changes: 47 additions & 11 deletions apps/backend/src/factory/pdf/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,23 +169,31 @@ const addTopicInformation = async (
}

const questionaryAttachments: Attachment[] = [];

const updatedAnswers: Answer[] = [];
for (let i = 0; i < answers.length; i++) {
const answer = answers[i];

questionaryAttachments.push(...getFileAttachments(answer));

if (answer.question.dataType === DataType.SAMPLE_DECLARATION) {
answer.value = samples
const value = samples
.filter((sample) => sample.questionId === answer.question.id)
.map((sample) => sample);
updatedAnswers.push({
...answer,
value,
});
} else if (answer.question.dataType === DataType.GENERIC_TEMPLATE) {
answer.value = genericTemplates
const value = genericTemplates
.filter(
(genericTemplate) =>
genericTemplate.questionId === answer.question.id
)
.map((genericTemplate) => genericTemplate);
updatedAnswers.push({
...answer,
value,
});
} else if (answer.question.dataType === DataType.INSTRUMENT_PICKER) {
const ids = Array.isArray(answer.value)
? answer.value.map((v: { instrumentId: string }) =>
Expand All @@ -203,13 +211,21 @@ const addTopicInformation = async (
const call = await callDataSource.getCallByAnswerIdProposal(
answer.answerId
);
answer.value = instrumentPickerAnswer(answer, instruments, call);
const value = instrumentPickerAnswer(answer, instruments, call);
updatedAnswers.push({
...answer,
value,
});
} else {
updatedAnswers.push({
...answer,
});
}
}

updatedProposalPDFData.questionarySteps.push({
...step,
fields: answers,
fields: updatedAnswers,
});
updatedProposalPDFData.attachments.push(...questionaryAttachments);
updatedProposalPDFData.attachments.push(...sampleAttachments);
Expand Down Expand Up @@ -365,23 +381,31 @@ export const collectProposalPDFData = async (
}

const questionaryAttachments: Attachment[] = [];

const updatedAnswers: Answer[] = [];
for (let i = 0; i < answers.length; i++) {
const answer = answers[i];

questionaryAttachments.push(...getFileAttachments(answer));

if (answer.question.dataType === DataType.SAMPLE_DECLARATION) {
answer.value = samples
const value = samples
.filter((sample) => sample.questionId === answer.question.id)
.map((sample) => sample);
updatedAnswers.push({
...answer,
value,
});
} else if (answer.question.dataType === DataType.GENERIC_TEMPLATE) {
answer.value = genericTemplates
const value = genericTemplates
.filter(
(genericTemplate) =>
genericTemplate.questionId === answer.question.id
)
.map((genericTemplate) => genericTemplate);
updatedAnswers.push({
...answer,
value,
});
} else if (answer.question.dataType === DataType.INSTRUMENT_PICKER) {
const ids = Array.isArray(answer.value)
? answer.value.map((v: { instrumentId: string }) =>
Expand All @@ -391,7 +415,11 @@ export const collectProposalPDFData = async (
const instruments =
await baseContext.queries.instrument.getInstrumentsByIds(user, ids);

answer.value = instrumentPickerAnswer(answer, instruments, call);
const value = instrumentPickerAnswer(answer, instruments, call);
updatedAnswers.push({
...answer,
value,
});
} else if (answer.question.dataType === DataType.TECHNIQUE_PICKER) {
const techniqueIds = Array.isArray(answer.value)
? answer.value
Expand All @@ -401,15 +429,23 @@ export const collectProposalPDFData = async (
user,
techniqueIds
);
answer.value = techniques?.length
const value = techniques?.length
? techniques.map((technique) => technique.name).join(', ')
: '';
updatedAnswers.push({
...answer,
value,
});
} else {
updatedAnswers.push({
...answer,
});
}
}

out.questionarySteps.push({
...step,
fields: answers,
fields: updatedAnswers,
});
out.attachments.push(...questionaryAttachments);
out.attachments.push(...sampleAttachments);
Expand Down
3 changes: 3 additions & 0 deletions apps/backend/src/factory/xlsx/stfc/StfcFapDataColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ export const StfcFapDataColumns = [
'Reviewer 2',
'Reviewer 2 score',
'Reviewer 2 review comment',
'Reviewer 3',
'Reviewer 3 score',
'Reviewer 3 review comment',
];
16 changes: 14 additions & 2 deletions apps/backend/src/factory/xlsx/stfc/StfcFapDataRow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ export async function getStfcDataRow(
}

export function populateStfcRow(row: RowObj) {
const reviews = row.reviews ? row.reviews : [];

while (reviews.length < 3) {
reviews.push(['No reviewer', '-', '-']);
}

return [
row.propShortCode ?? '<missing>',
row.principalInv ?? '<missing>',
Expand All @@ -90,10 +96,16 @@ export function populateStfcRow(row: RowObj) {
row.propTitle ?? '<missing>',
row.techReviewComment ?? '<missing>',
row.propReviewAvgScore ?? '<missing>',
].concat(row.reviews ? row.reviews?.flat() : []);
].concat(reviews.flat());
}

export function callFapStfcPopulateRow(row: CallRowObj): (string | number)[] {
const reviews = row.reviews ? row.reviews : [];

while (reviews.length < 3) {
reviews.push(['No reviewer', '-', '-']);
}

return [
row.propShortCode ?? '<missing>',
row.principalInv ?? '<missing>',
Expand All @@ -104,7 +116,7 @@ export function callFapStfcPopulateRow(row: CallRowObj): (string | number)[] {
row.techReviewComment ?? '<missing>',
row.propReviewAvgScore ?? '<missing>',
]
.concat(row.reviews ? row.reviews?.flat() : [])
.concat(reviews.flat())
.concat([
row.fapTimeAllocation ?? row.daysRequested ?? '<missing>',
row.fapMeetingDecision ?? '<missing>',
Expand Down
11 changes: 8 additions & 3 deletions apps/backend/src/models/questionTypes/TechniquePicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,14 @@ export const techniquePickerDefinition: Question<DataType.TECHNIQUE_PICKER> = {

return {
...config,
techniques: uniqueTechniques.map(
(technique) => new TechniqueOptionClass(technique.id, technique.name)
),
techniques: uniqueTechniques
.map(
(technique) =>
new TechniqueOptionClass(technique.id, technique.name)
)
.sort((a, b) =>
a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
),
};
} catch (err) {
logger.logError('Techniques fetch failed', {
Expand Down
126 changes: 122 additions & 4 deletions apps/backend/src/mutations/QuestionaryMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { container, inject, injectable } from 'tsyringe';

import { QuestionaryAuthorization } from '../auth/QuestionaryAuthorization';
import { Tokens } from '../config/Tokens';
import { GenericTemplateDataSource } from '../datasources/GenericTemplateDataSource';
import { ProposalDataSource } from '../datasources/ProposalDataSource';
import { QuestionaryDataSource } from '../datasources/QuestionaryDataSource';
import { TemplateDataSource } from '../datasources/TemplateDataSource';
import { Authorized, EventBus } from '../decorators';
Expand All @@ -15,10 +17,15 @@ import {
import { AnswerBasic } from '../models/Questionary';
import { getQuestionDefinition } from '../models/questionTypes/QuestionRegistry';
import { rejection } from '../models/Rejection';
import { DataType } from '../models/Template';
import { UserJWT, UserWithRole } from '../models/User';
import { AnswerTopicArgs } from '../resolvers/mutations/AnswerTopicMutation';
import {
AnswerInput,
AnswerTopicArgs,
} from '../resolvers/mutations/AnswerTopicMutation';
import { CreateQuestionaryArgs } from '../resolvers/mutations/CreateQuestionaryMutation';
import { UpdateAnswerArgs } from '../resolvers/mutations/UpdateAnswerMutation';
import { SubTemplateConfig } from '../resolvers/types/FieldConfig';

@injectable()
export default class QuestionaryMutations {
Expand All @@ -28,13 +35,19 @@ export default class QuestionaryMutations {
@inject(Tokens.QuestionaryDataSource)
private dataSource: QuestionaryDataSource,
@inject(Tokens.TemplateDataSource)
private templateDataSource: TemplateDataSource
private templateDataSource: TemplateDataSource,
@inject(Tokens.GenericTemplateDataSource)
private genericTemplateDataSource: GenericTemplateDataSource,
@inject(Tokens.ProposalDataSource)
private proposalDataSource: ProposalDataSource
) {}

async deleteOldAnswers(
templateId: number,
questionaryId: number,
topicId: number
topicId: number,
answers: AnswerInput[],
agent: UserWithRole | null
) {
const templateSteps =
await this.templateDataSource.getTemplateSteps(templateId);
Expand All @@ -53,9 +66,108 @@ export default class QuestionaryMutations {
const questionIds: string[] = stepQuestions.map(
(question) => question.question.id
);

const genericTemplateQuestions = stepQuestions.filter(
(step) => step.question.dataType == DataType.GENERIC_TEMPLATE
);

if (genericTemplateQuestions.length > 0) {
// Confirm dependency with dependency condition
const unsatisfiedDependencyQues = genericTemplateQuestions.filter(
(genericTemplateQues) => {
const notSatisfiedQuestions = genericTemplateQues.dependencies.filter(
(dependency) => {
const answer = answers.find(
(answer) => answer.questionId === dependency.dependencyId
);

if (answer) {
const { value } = JSON.parse(answer.value);
if (value[0] !== dependency.condition.params) {
return true;
}
}
}
);

if (notSatisfiedQuestions.length !== 0) {
return true;
}
}
);

for (const subTemplateQues of unsatisfiedDependencyQues) {
const config = subTemplateQues.config as SubTemplateConfig;
await this.deleteSubTemplatesAnswers(
subTemplateQues.question.id,
config.templateId,
questionaryId,
agent
);
}
}

await this.dataSource.deleteAnswers(questionaryId, questionIds);
}

async deleteSubTemplatesAnswers(
questionId: string,
genericTemplateId: number | null,
questionaryId: number,
agent: UserWithRole | null
): Promise<void> {
if (!genericTemplateId) {
return;
}

const proposal = (
await this.proposalDataSource.getProposals({
questionaryIds: [questionaryId],
})
).proposals[0];

const genericTemplates =
await this.genericTemplateDataSource.getGenericTemplates(
{
filter: {
questionId: questionId,
proposalPk: proposal?.primaryKey,
},
},
agent
);

if (!genericTemplates) {
return;
}

const templateSteps =
await this.templateDataSource.getTemplateSteps(genericTemplateId);
const genericTemplateStepQuestions = templateSteps.flatMap(
(step) => step.fields
);

if (!genericTemplateStepQuestions) {
return;
}

const genericTemplateQuestionIds = genericTemplateStepQuestions.map(
(question) => question.question.id
);

if (!genericTemplateQuestionIds) {
return;
}

for (const genericTemplate of genericTemplates) {
await this.dataSource.deleteAnswers(
genericTemplate.questionaryId,
genericTemplateQuestionIds
);
await this.genericTemplateDataSource.delete(genericTemplate.id);
}
}

@Authorized()
@EventBus(Event.TOPIC_ANSWERED)
async answerTopic(agent: UserWithRole | null, args: AnswerTopicArgs) {
Expand Down Expand Up @@ -89,7 +201,13 @@ export default class QuestionaryMutations {
);
}

await this.deleteOldAnswers(template.templateId, questionaryId, topicId);
await this.deleteOldAnswers(
template.templateId,
questionaryId,
topicId,
answers,
agent
);

const updatedAnswers: AnswerBasic[] = [];
for (const answer of answers) {
Expand Down
5 changes: 4 additions & 1 deletion apps/backend/src/statusActionEngine/statusActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ export const getEmailReadyArrayOfUsersAndProposals = async (
);
for (const step of await questionarySteps) {
const stepFields = step.fields.map((field) => field);
if (step.topic.title.toUpperCase() === 'SAMPLES') {
if (
step.topic.title.toUpperCase() === 'SAMPLES' ||
step.topic.title.toUpperCase() === 'SAMPLE'
) {
const answers = await stepAnswers(
stepFields,
proposal.primaryKey
Expand Down
Loading

0 comments on commit 723f6a6

Please sign in to comment.