From accec26fe1ab1f91ad4bae22bceef94168dd971a Mon Sep 17 00:00:00 2001 From: Grant James Date: Thu, 16 Jul 2026 12:44:14 +0100 Subject: [PATCH 1/5] Add DLS fap export handler with PI org added. --- .../backend/src/config/dependencyConfigDLS.ts | 18 ++-- .../src/factory/xlsx/dls/DLSFapDataColumns.ts | 13 +++ .../src/factory/xlsx/dls/DLSFapDataRow.ts | 84 +++++++++++++++++++ .../fixtures/exampleCallFapExportDLS.json | 37 ++++++++ 4 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts create mode 100644 apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts create mode 100644 apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json diff --git a/apps/backend/src/config/dependencyConfigDLS.ts b/apps/backend/src/config/dependencyConfigDLS.ts index 0e506f2ec2..3c70c74979 100644 --- a/apps/backend/src/config/dependencyConfigDLS.ts +++ b/apps/backend/src/config/dependencyConfigDLS.ts @@ -55,12 +55,12 @@ import { createPostToRabbitMQHandler, } from '../eventHandlers/messageBroker'; import { createApplicationEventBus } from '../events'; -import { FapDataColumns } from '../factory/xlsx/FapDataColumns'; +import { DLSFapDataColumns } from '../factory/xlsx/dls/DLSFapDataColumns'; import { - callFapPopulateRow, - getDataRow, - populateRow, -} from '../factory/xlsx/FapDataRow'; + callFapDLSPopulateRow, + getDLSDataRow, + populateDLSRow, +} from '../factory/xlsx/dls/DLSFapDataRow'; import BasicUserDetailsLoader from '../loaders/BasicUserDetailsLoader'; import { SkipAssetRegistrar } from '../services/assetRegistrar/skip/SkipAssetRegistrar'; @@ -127,10 +127,10 @@ mapClass(Tokens.AssetRegistrar, SkipAssetRegistrar); mapClass(Tokens.MailService, SMTPMailService); -mapValue(Tokens.FapDataColumns, FapDataColumns); -mapValue(Tokens.FapDataRow, getDataRow); -mapValue(Tokens.PopulateRow, populateRow); -mapValue(Tokens.PopulateCallRow, callFapPopulateRow); +mapValue(Tokens.FapDataColumns, DLSFapDataColumns); +mapValue(Tokens.FapDataRow, getDLSDataRow); +mapValue(Tokens.PopulateRow, populateDLSRow); +mapValue(Tokens.PopulateCallRow, callFapDLSPopulateRow); mapValue(Tokens.EmailEventHandler, DLSEmailHandler); mapClass(Tokens.EmailTemplateDataSource, PostgresEmailTemplateDataSource); diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts new file mode 100644 index 0000000000..fd28416ad3 --- /dev/null +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts @@ -0,0 +1,13 @@ +export const DLSFapDataColumns = [ + 'Proposal Reference Number', + 'Proposal Title', + 'Principal Investigator', + 'Institution', + 'Instrument', + 'Instrument available time', + 'Technical review allocated time', + 'Fap allocated time', + 'Average Score', + 'Current rank', + 'Is in availability zone', +]; diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts new file mode 100644 index 0000000000..9fe87bb43e --- /dev/null +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts @@ -0,0 +1,84 @@ +import { container } from 'tsyringe'; + +import { Tokens } from '../../../config/Tokens'; +import { UserDataSource } from '../../../datasources/UserDataSource'; +import { CallRowObj } from '../callFaps'; +import { RowObj } from '../fap'; +import { getDataRow } from '../FapDataRow'; + +type DLSFapRowObj = RowObj & { + //piInstitution: string | null | undefined; +}; + +function nullFieldHelper( + data: string | number | null | undefined +): string | number { + return data ?? ''; +} + +export async function getDLSDataRow( + proposalPk: number, + piName: string, + proposalAverageScore: number, + instrumentName: string, + instrumentAvailabilityTime: number, + fapTimeAllocation: number | null, + proposalTitle: string, + proposalId: number | null, + techReviewTimeAllocation: number | null, + technicalReviewComment: string | null, + propFapRankOrder: number | null, + proposerId: number | null +): Promise { + const userDataSource = container.resolve( + Tokens.UserDataSource + ); + const pi = proposerId + ? await userDataSource.getBasicUserInfo(proposerId) + : null; + + return { + ...getDataRow( + proposalPk, + piName, + proposalAverageScore, + instrumentName, + instrumentAvailabilityTime, + fapTimeAllocation, + proposalTitle, + proposalId, + techReviewTimeAllocation, + technicalReviewComment, + propFapRankOrder + ), + piOrg: pi?.institution, + }; +} + +export function populateDLSRow(row: DLSFapRowObj): (string | number)[] { + return [ + nullFieldHelper(row.propShortCode), + nullFieldHelper(row.propTitle), + nullFieldHelper(row.principalInv), + nullFieldHelper(row.piOrg), + nullFieldHelper(row.instrName), + nullFieldHelper(row.instrAvailTime), + nullFieldHelper(row.techReviewTimeAllocation), + nullFieldHelper(row.fapTimeAllocation ?? row.techReviewTimeAllocation), + nullFieldHelper(row.propReviewAvgScore), + nullFieldHelper(row.propFapRankOrder), + nullFieldHelper(row.inAvailZone), + ]; +} + +export function callFapDLSPopulateRow( + row: CallRowObj & DLSFapRowObj +): (string | number)[] { + return [ + ...populateDLSRow(row), + row.fapTimeAllocation ?? row.timeRequested ?? '', + row.fapMeetingDecision ?? '', + row.fapMeetingInComment ?? '', + row.fapMeetingExComment ?? '', + ].concat(row.reviews ? row.reviews.flatMap((review) => review) : []); +} diff --git a/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json b/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json new file mode 100644 index 0000000000..f04eb67a3b --- /dev/null +++ b/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json @@ -0,0 +1,37 @@ +[ + { "Proposal Reference Number": "Central lavender" }, + { + "Proposal Reference Number": "567122", + "Proposal Title": "lumen proofread hertz", + "Principal Investigator": "Carl Carlsson", + "Institution": "Other", + "Instrument": "Central lavender", + "Instrument available time": 20, + "Technical review allocated time": 25, + "Fap allocated time": 25, + "Average Score": 3, + "Current rank": "", + "Is in availability zone": "no", + "Fap Time allocation": "", + "Fap Meeting Decision": "Accepted", + "Fap Meeting Comment for User": "test", + "Fap Meeting Internal Comment": "test" + }, + { + "Proposal Reference Number": "701367", + "Proposal Title": "web Connecticut driver", + "Principal Investigator": "Carl Carlsson", + "Institution": "Other", + "Instrument": "Central lavender", + "Instrument available time": 20, + "Technical review allocated time": 5, + "Fap allocated time": 5, + "Average Score": 3, + "Current rank": "", + "Is in availability zone": "no", + "Fap Time allocation": "", + "Fap Meeting Decision": "Accepted", + "Fap Meeting Comment for User": "test2", + "Fap Meeting Internal Comment": "test2" + } +] From cd4c86b8f88aeb6de93205e4ef371b70900abba6 Mon Sep 17 00:00:00 2001 From: Grant James Date: Thu, 16 Jul 2026 13:14:27 +0100 Subject: [PATCH 2/5] Create token to override extra fap columns for call export --- apps/backend/src/config/Tokens.ts | 1 + .../backend/src/config/dependencyConfigDLS.ts | 6 +++- .../src/factory/xlsx/dls/DLSFapDataColumns.ts | 15 ++++++--- apps/backend/src/middlewares/factory/xlsx.ts | 7 +++- .../fixtures/exampleCallFapExportDLS.json | 32 +++++++++---------- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/apps/backend/src/config/Tokens.ts b/apps/backend/src/config/Tokens.ts index d5a07cacc8..2c48780fcd 100644 --- a/apps/backend/src/config/Tokens.ts +++ b/apps/backend/src/config/Tokens.ts @@ -50,6 +50,7 @@ export const Tokens = { PredefinedMessageDataSource: Symbol('PredefinedMessageDataSource'), InternalReviewDataSource: Symbol('InternalReviewDataSource'), FapDataColumns: Symbol('FapDataColumns'), + CallExtraFapDataColumns: Symbol('CallExtraFapDataColumns'), FapDataRow: Symbol('FapDataRow'), PopulateRow: Symbol('PopulateRow'), PopulateCallRow: Symbol('PopulateCallRow'), diff --git a/apps/backend/src/config/dependencyConfigDLS.ts b/apps/backend/src/config/dependencyConfigDLS.ts index 3c70c74979..7f357857e9 100644 --- a/apps/backend/src/config/dependencyConfigDLS.ts +++ b/apps/backend/src/config/dependencyConfigDLS.ts @@ -55,7 +55,10 @@ import { createPostToRabbitMQHandler, } from '../eventHandlers/messageBroker'; import { createApplicationEventBus } from '../events'; -import { DLSFapDataColumns } from '../factory/xlsx/dls/DLSFapDataColumns'; +import { + DLSCallExtraFapDataColumns, + DLSFapDataColumns, +} from '../factory/xlsx/dls/DLSFapDataColumns'; import { callFapDLSPopulateRow, getDLSDataRow, @@ -128,6 +131,7 @@ mapClass(Tokens.AssetRegistrar, SkipAssetRegistrar); mapClass(Tokens.MailService, SMTPMailService); mapValue(Tokens.FapDataColumns, DLSFapDataColumns); +mapValue(Tokens.CallExtraFapDataColumns, DLSCallExtraFapDataColumns); mapValue(Tokens.FapDataRow, getDLSDataRow); mapValue(Tokens.PopulateRow, populateDLSRow); mapValue(Tokens.PopulateCallRow, callFapDLSPopulateRow); diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts index fd28416ad3..4e5feaa436 100644 --- a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts @@ -4,10 +4,17 @@ export const DLSFapDataColumns = [ 'Principal Investigator', 'Institution', 'Instrument', - 'Instrument available time', - 'Technical review allocated time', - 'Fap allocated time', + 'Instrument available Time', + 'TA Recommended Time', + 'FAP Recommended Time', 'Average Score', - 'Current rank', + 'Current Rank', 'Is in availability zone', ]; + +export const DLSCallExtraFapDataColumns = [ + 'FAP Time Allocation', // Investigate if duplicate of FAP Recommended/Allocated Time + 'FAP Meeting Recommendation', + 'FAP Meeting Comment for User', + 'FAP Meeting Internal Comment', +]; diff --git a/apps/backend/src/middlewares/factory/xlsx.ts b/apps/backend/src/middlewares/factory/xlsx.ts index 64c9861f89..1ed164c81f 100644 --- a/apps/backend/src/middlewares/factory/xlsx.ts +++ b/apps/backend/src/middlewares/factory/xlsx.ts @@ -21,6 +21,11 @@ import { } from '../../factory/xlsx/proposal'; const fapDataColumns = container.resolve(Tokens.FapDataColumns); +const callExtraFapDataColumns = container.isRegistered( + Tokens.CallExtraFapDataColumns +) + ? container.resolve(Tokens.CallExtraFapDataColumns) + : CallExtraFapDataColumns; const router = express.Router(); @@ -147,7 +152,7 @@ router.get(`/${XLSXType.CALL_FAP}/:call_id`, async (req, res, next) => { const meta: XLSXMetaBase = { singleFilename: filename, collectionFilename: filename, - columns: fapDataColumns.concat(CallExtraFapDataColumns), + columns: fapDataColumns.concat(callExtraFapDataColumns), }; const userRole = req.user.currentRole; diff --git a/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json b/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json index f04eb67a3b..134b752901 100644 --- a/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json +++ b/apps/e2e/cypress/fixtures/exampleCallFapExportDLS.json @@ -6,16 +6,16 @@ "Principal Investigator": "Carl Carlsson", "Institution": "Other", "Instrument": "Central lavender", - "Instrument available time": 20, - "Technical review allocated time": 25, - "Fap allocated time": 25, + "Instrument available Time": 20, + "TA Recommended Time": 25, + "FAP Recommended Time": 25, "Average Score": 3, - "Current rank": "", + "Current Rank": "", "Is in availability zone": "no", - "Fap Time allocation": "", - "Fap Meeting Decision": "Accepted", - "Fap Meeting Comment for User": "test", - "Fap Meeting Internal Comment": "test" + "FAP Time Allocation": "", + "FAP Meeting Recommendation": "Accepted", + "FAP Meeting Comment for User": "test", + "FAP Meeting Internal Comment": "test" }, { "Proposal Reference Number": "701367", @@ -23,15 +23,15 @@ "Principal Investigator": "Carl Carlsson", "Institution": "Other", "Instrument": "Central lavender", - "Instrument available time": 20, - "Technical review allocated time": 5, - "Fap allocated time": 5, + "Instrument available Time": 20, + "TA Recommended Time": 5, + "FAP Recommended Time": 5, "Average Score": 3, - "Current rank": "", + "Current Rank": "", "Is in availability zone": "no", - "Fap Time allocation": "", - "Fap Meeting Decision": "Accepted", - "Fap Meeting Comment for User": "test2", - "Fap Meeting Internal Comment": "test2" + "FAP Time Allocation": "", + "FAP Meeting Recommendation": "Accepted", + "FAP Meeting Comment for User": "test2", + "FAP Meeting Internal Comment": "test2" } ] From 683c2f757b33b148c6dee5c4e886da50938409cc Mon Sep 17 00:00:00 2001 From: Grant James Date: Thu, 16 Jul 2026 14:26:41 +0100 Subject: [PATCH 3/5] Add instrument id to fap data row builder and use object for fap data to avoid needing to use positional arguments. --- apps/backend/src/factory/xlsx/FapDataRow.ts | 48 +++++++++++++----- .../src/factory/xlsx/dls/DLSFapDataColumns.ts | 1 + .../src/factory/xlsx/dls/DLSFapDataRow.ts | 49 +++++++------------ apps/backend/src/factory/xlsx/fap.ts | 40 +++++++-------- .../src/factory/xlsx/stfc/StfcFapDataRow.ts | 44 +++-------------- 5 files changed, 83 insertions(+), 99 deletions(-) diff --git a/apps/backend/src/factory/xlsx/FapDataRow.ts b/apps/backend/src/factory/xlsx/FapDataRow.ts index b42f2a9264..bdfc95ee91 100644 --- a/apps/backend/src/factory/xlsx/FapDataRow.ts +++ b/apps/backend/src/factory/xlsx/FapDataRow.ts @@ -1,19 +1,41 @@ import { CallRowObj } from './callFaps'; import { RowObj } from './fap'; +import { QuestionaryStep } from '../../models/Questionary'; +import { Review } from '../../models/Review'; -export function getDataRow( - proposalPk: number, - piName: string, - proposalAverageScore: number, - instrumentName: string, - instrumentAvailabilityTime: number, - fapTimeAllocation: number | null, - proposalTitle: string, - proposalId: number | null, - techReviewTimeAllocation: number | null, - technicalReviewComment: string | null, - propFapRankOrder: number | null -): RowObj { +export type FapDataRowInput = { + proposalPk: number; + piName: string; + proposalAverageScore: number; + instrumentName: string; + instrumentAvailabilityTime: number; + fapTimeAllocation: number | null; + proposalTitle: string; + proposalId: number | null; + techReviewTimeAllocation: number | null; + technicalReviewComment: string | null; + propFapRankOrder: number | null; + proposerId: number | null; + proposalAnswers: QuestionaryStep[] | null; + reviews: Review[] | null; + instrumentId: number; +}; + +export type FapDataRow = (input: FapDataRowInput) => RowObj | Promise; + +export function getDataRow({ + proposalPk, + piName, + proposalAverageScore, + instrumentName, + instrumentAvailabilityTime, + fapTimeAllocation, + proposalTitle, + proposalId, + techReviewTimeAllocation, + technicalReviewComment, + propFapRankOrder, +}: FapDataRowInput): RowObj { return { proposalPk: proposalPk, propShortCode: proposalId?.toString(), diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts index 4e5feaa436..0020d16fa0 100644 --- a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts @@ -17,4 +17,5 @@ export const DLSCallExtraFapDataColumns = [ 'FAP Meeting Recommendation', 'FAP Meeting Comment for User', 'FAP Meeting Internal Comment', + 'Instrument Requested Time', ]; diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts index 9fe87bb43e..47e08f6aac 100644 --- a/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts @@ -1,13 +1,14 @@ import { container } from 'tsyringe'; import { Tokens } from '../../../config/Tokens'; +import { ProposalDataSource } from '../../../datasources/ProposalDataSource'; import { UserDataSource } from '../../../datasources/UserDataSource'; import { CallRowObj } from '../callFaps'; import { RowObj } from '../fap'; -import { getDataRow } from '../FapDataRow'; +import { FapDataRowInput, getDataRow } from '../FapDataRow'; type DLSFapRowObj = RowObj & { - //piInstitution: string | null | undefined; + instrumentRequestedTime: number | null | undefined; }; function nullFieldHelper( @@ -17,19 +18,9 @@ function nullFieldHelper( } export async function getDLSDataRow( - proposalPk: number, - piName: string, - proposalAverageScore: number, - instrumentName: string, - instrumentAvailabilityTime: number, - fapTimeAllocation: number | null, - proposalTitle: string, - proposalId: number | null, - techReviewTimeAllocation: number | null, - technicalReviewComment: string | null, - propFapRankOrder: number | null, - proposerId: number | null + input: FapDataRowInput ): Promise { + const { proposalPk, proposerId, instrumentId } = input; const userDataSource = container.resolve( Tokens.UserDataSource ); @@ -37,21 +28,18 @@ export async function getDLSDataRow( ? await userDataSource.getBasicUserInfo(proposerId) : null; + const proposalDataSource = container.resolve( + Tokens.ProposalDataSource + ); + const instrumentRequestedTime = await proposalDataSource.getRequestedTime( + proposalPk, + instrumentId + ); + return { - ...getDataRow( - proposalPk, - piName, - proposalAverageScore, - instrumentName, - instrumentAvailabilityTime, - fapTimeAllocation, - proposalTitle, - proposalId, - techReviewTimeAllocation, - technicalReviewComment, - propFapRankOrder - ), + ...getDataRow(input), piOrg: pi?.institution, + instrumentRequestedTime, }; } @@ -77,8 +65,9 @@ export function callFapDLSPopulateRow( return [ ...populateDLSRow(row), row.fapTimeAllocation ?? row.timeRequested ?? '', - row.fapMeetingDecision ?? '', - row.fapMeetingInComment ?? '', - row.fapMeetingExComment ?? '', + nullFieldHelper(row.fapMeetingDecision), + nullFieldHelper(row.fapMeetingInComment), + nullFieldHelper(row.fapMeetingExComment), + nullFieldHelper(row.instrumentRequestedTime), ].concat(row.reviews ? row.reviews.flatMap((review) => review) : []); } diff --git a/apps/backend/src/factory/xlsx/fap.ts b/apps/backend/src/factory/xlsx/fap.ts index ca0c1ece01..358e1846e1 100644 --- a/apps/backend/src/factory/xlsx/fap.ts +++ b/apps/backend/src/factory/xlsx/fap.ts @@ -2,9 +2,8 @@ import { groupBy } from 'lodash'; import { container } from 'tsyringe'; import { collectCallFapXLSXData } from './callFaps'; -import { getDataRow } from './FapDataRow'; +import { FapDataRow, FapDataRowInput } from './FapDataRow'; import baseContext from '../../buildContext'; -import { getStfcDataRow } from './stfc/StfcFapDataRow'; import { Tokens } from '../../config/Tokens'; import { FapDataSource } from '../../datasources/FapDataSource'; import { UserWithRole } from '../../models/User'; @@ -36,9 +35,7 @@ export type RowObj = { accessRoute?: string | null; }; -const fapDataRow = container.resolve( - Tokens.FapDataRow -); +const fapDataRow = container.resolve(Tokens.FapDataRow); const populateRow = container.resolve<(row: RowObj) => (string | number)[]>( Tokens.PopulateRow @@ -118,22 +115,25 @@ export const collectFapXLSXRowData = async ( { proposalPk: proposal.proposal_pk, fapId: fapId } ); - return fapDataRow( - proposal.proposal_pk, - piFullName, - proposal.average_grade, - proposal.instrument_name, - proposal.availability_time, - proposal.fap_time_allocation, - proposal.title, - proposal.proposal_id, - proposal.time_allocation, - stripHtml(proposal.comment ?? ''), - proposal.rank_order, - proposal.proposer_id, + const rowInput: FapDataRowInput = { + proposalPk: proposal.proposal_pk, + piName: piFullName, + proposalAverageScore: proposal.average_grade, + instrumentName: proposal.instrument_name, + instrumentAvailabilityTime: proposal.availability_time, + fapTimeAllocation: proposal.fap_time_allocation, + proposalTitle: proposal.title, + proposalId: proposal.proposal_id, + techReviewTimeAllocation: proposal.time_allocation, + technicalReviewComment: stripHtml(proposal.comment ?? ''), + propFapRankOrder: proposal.rank_order, + proposerId: proposal.proposer_id, proposalAnswers, - reviews - ); + reviews, + instrumentId: proposal.instrument_id, + }; + + return fapDataRow(rowInput); }) ); diff --git a/apps/backend/src/factory/xlsx/stfc/StfcFapDataRow.ts b/apps/backend/src/factory/xlsx/stfc/StfcFapDataRow.ts index b5b489adf8..351599b25a 100644 --- a/apps/backend/src/factory/xlsx/stfc/StfcFapDataRow.ts +++ b/apps/backend/src/factory/xlsx/stfc/StfcFapDataRow.ts @@ -2,29 +2,13 @@ import { container } from 'tsyringe'; import { Tokens } from '../../../config/Tokens'; import { StfcUserDataSource } from '../../../datasources/stfc/StfcUserDataSource'; -import { QuestionaryStep } from '../../../models/Questionary'; -import { Review } from '../../../models/Review'; import { stripHtml } from '../../../utils/stringStripHtml'; import { CallRowObj } from '../callFaps'; import { RowObj } from '../fap'; -import { getDataRow } from '../FapDataRow'; - -export async function getStfcDataRow( - proposalPk: number, - piName: string, - proposalAverageScore: number, - instrument: string, - instrumentAvailabilityTime: number, - fapTimeAllocation: number | null, - proposalTitle: string, - proposalId: number | null, - technicalReviewTimeAllocation: number | null, - technicalReviewComment: string | null, - propFapRankOrder: number | null, - proposer_id: number | null, - proposalAnswers: QuestionaryStep[] | null, - reviews: Review[] | null -) { +import { FapDataRowInput, getDataRow } from '../FapDataRow'; + +export async function getStfcDataRow(input: FapDataRowInput) { + const { proposerId, proposalAnswers, reviews } = input; const stfcUserDataSource: StfcUserDataSource = container.resolve( Tokens.UserDataSource ) as StfcUserDataSource; @@ -65,31 +49,19 @@ export async function getStfcDataRow( )?.value; const piDetails = await stfcUserDataSource.getStfcBasicPeopleByUserNumbers([ - proposer_id?.toString() ?? '', + proposerId?.toString() ?? '', ]); const piCountry = piDetails.find( - (user) => user.userNumber === proposer_id?.toString() + (user) => user.userNumber === proposerId?.toString() )?.country; const piOrg = piDetails.find( - (user) => user.userNumber === proposer_id?.toString() + (user) => user.userNumber === proposerId?.toString() )?.orgName; return { - ...getDataRow( - proposalPk, - piName, - proposalAverageScore, - instrument, - instrumentAvailabilityTime, - fapTimeAllocation, - proposalTitle, - proposalId, - technicalReviewTimeAllocation, - technicalReviewComment, - propFapRankOrder - ), + ...getDataRow(input), accessRoute, timeRequested, reviews: individualReviews, From a9ae25806889f5bd23f69efe9249df4d0bda8e48 Mon Sep 17 00:00:00 2001 From: Grant James Date: Thu, 16 Jul 2026 14:43:27 +0100 Subject: [PATCH 4/5] Remove duplicated column from DLS fap spreadsheet --- apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts | 1 - apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts index 0020d16fa0..e8e7e96ad3 100644 --- a/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataColumns.ts @@ -13,7 +13,6 @@ export const DLSFapDataColumns = [ ]; export const DLSCallExtraFapDataColumns = [ - 'FAP Time Allocation', // Investigate if duplicate of FAP Recommended/Allocated Time 'FAP Meeting Recommendation', 'FAP Meeting Comment for User', 'FAP Meeting Internal Comment', diff --git a/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts index 47e08f6aac..99e70bb105 100644 --- a/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts +++ b/apps/backend/src/factory/xlsx/dls/DLSFapDataRow.ts @@ -64,7 +64,6 @@ export function callFapDLSPopulateRow( ): (string | number)[] { return [ ...populateDLSRow(row), - row.fapTimeAllocation ?? row.timeRequested ?? '', nullFieldHelper(row.fapMeetingDecision), nullFieldHelper(row.fapMeetingInComment), nullFieldHelper(row.fapMeetingExComment), From e077705c3a62bbc7e4f4cd2fd8e1a2d0259e37a8 Mon Sep 17 00:00:00 2001 From: Grant James Date: Tue, 21 Jul 2026 12:30:57 +0100 Subject: [PATCH 5/5] Rename default extra fap columns to make explicit that it's default --- apps/backend/src/factory/xlsx/callFaps.ts | 2 +- apps/backend/src/middlewares/factory/xlsx.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/factory/xlsx/callFaps.ts b/apps/backend/src/factory/xlsx/callFaps.ts index fab85fc454..415d6889b7 100644 --- a/apps/backend/src/factory/xlsx/callFaps.ts +++ b/apps/backend/src/factory/xlsx/callFaps.ts @@ -108,7 +108,7 @@ export const collectCallFapXLSXData = async ( return { data: baseData, filename: filename.replace(/\s+/g, '_') }; }; -export const CallExtraFapDataColumns = [ +export const DefaultCallExtraFapDataColumns = [ 'Fap Time allocation', 'Fap Meeting Decision', 'Fap Meeting Comment for User', diff --git a/apps/backend/src/middlewares/factory/xlsx.ts b/apps/backend/src/middlewares/factory/xlsx.ts index 1ed164c81f..fefde3d0b7 100644 --- a/apps/backend/src/middlewares/factory/xlsx.ts +++ b/apps/backend/src/middlewares/factory/xlsx.ts @@ -10,7 +10,7 @@ import callFactoryService, { } from '../../factory/service'; import { getCurrentTimestamp } from '../../factory/util'; import { - CallExtraFapDataColumns, + DefaultCallExtraFapDataColumns, collectCallFapXLSXData, } from '../../factory/xlsx/callFaps'; import { collectFapXLSXData } from '../../factory/xlsx/fap'; @@ -25,7 +25,7 @@ const callExtraFapDataColumns = container.isRegistered( Tokens.CallExtraFapDataColumns ) ? container.resolve(Tokens.CallExtraFapDataColumns) - : CallExtraFapDataColumns; + : DefaultCallExtraFapDataColumns; const router = express.Router();