@@ -10,9 +10,39 @@ import { QuestionaryDataSource } from '../../../datasources/QuestionaryDataSourc
1010import { UserDataSource } from '../../../datasources/UserDataSource' ;
1111import { ApplicationEvent } from '../../../events/applicationEvents' ;
1212import { Event } from '../../../events/event.enum' ;
13+ import { getTopicActiveAnswers } from '../../../models/ProposalModelFunctions' ;
14+ import { Answer , QuestionaryStep } from '../../../models/Questionary' ;
15+ import { DataType } from '../../../models/Template' ;
1316import { MailService } from '../../MailService/MailService' ;
1417import { EmailTemplateId } from '../emailTemplateId' ;
1518
19+ type InstrumentPickerAnswerValue = {
20+ instrumentId : number | string ;
21+ timeRequested ?: number | string | null ;
22+ } ;
23+
24+ const getInstrumentPickerAnswers = (
25+ questionarySteps : QuestionaryStep [ ]
26+ ) : Answer [ ] =>
27+ questionarySteps
28+ . flatMap ( ( step ) => getTopicActiveAnswers ( questionarySteps , step . topic . id ) )
29+ . filter (
30+ ( answer ) => answer . question . dataType === DataType . INSTRUMENT_PICKER
31+ ) ;
32+
33+ const getInstrumentPickerAnswerValues = (
34+ value : unknown
35+ ) : InstrumentPickerAnswerValue [ ] => {
36+ const values = Array . isArray ( value ) ? value : value ? [ value ] : [ ] ;
37+
38+ return values . filter (
39+ ( instrumentAnswer ) : instrumentAnswer is InstrumentPickerAnswerValue =>
40+ typeof instrumentAnswer === 'object' &&
41+ instrumentAnswer !== null &&
42+ 'instrumentId' in instrumentAnswer
43+ ) ;
44+ } ;
45+
1646export async function proposalSubmittedHandler ( event : ApplicationEvent ) {
1747 if ( event . type != Event . PROPOSAL_SUBMITTED ) return ;
1848
@@ -54,28 +84,49 @@ export async function proposalSubmittedHandler(event: ApplicationEvent) {
5484 event . proposal . callId
5585 ) ;
5686
57- const instruments = await instrumentSource . getInstrumentsByProposalPk (
58- event . proposal . primaryKey
87+ const questionarySteps = await questionaryDataSource . getQuestionarySteps (
88+ event . proposal . questionaryId
5989 ) ;
6090
61- // Postgres implementation doesn't match interface - impliementation wants questionaryId, not proposalId
62- const answer = await questionaryDataSource . getAnswer (
63- event . proposal . questionaryId ,
64- 'instrument_picker'
91+ const instrumentPickerAnswers = getInstrumentPickerAnswers ( questionarySteps ) ;
92+ const instrumentPickerAnswerValues = instrumentPickerAnswers . flatMap (
93+ ( answer ) => getInstrumentPickerAnswerValues ( answer . value )
94+ ) ;
95+ const instrumentIds = Array . from (
96+ new Set (
97+ instrumentPickerAnswerValues
98+ . map ( ( instrumentAnswer ) => Number ( instrumentAnswer . instrumentId ) )
99+ . filter ( ( instrumentId ) => Number . isFinite ( instrumentId ) )
100+ )
65101 ) ;
66102
67- (
68- answer ?. answer as {
69- value : { instrumentId : number ; timeRequested : number } [ ] ;
70- }
71- ) ?. value . forEach ( ( instrumentAnswer : any ) => {
72- const instrument = instruments . find (
73- ( inst ) => inst . id === Number ( instrumentAnswer . instrumentId )
74- ) ;
75- if ( instrument ) {
76- instrument . managementTimeAllocation = instrumentAnswer . timeRequested || 0 ;
77- }
78- } ) ;
103+ const instruments = await instrumentSource . getInstrumentsByIds ( instrumentIds ) ;
104+ const requested = instrumentPickerAnswerValues
105+ . map ( ( instrumentAnswer ) => {
106+ const requestedTime = Number ( instrumentAnswer . timeRequested ?? 0 ) ;
107+ const formattedRequestedTime = Number . isFinite ( requestedTime )
108+ ? requestedTime
109+ : 0 ;
110+ const instrument = instruments . find (
111+ ( inst ) => inst . id === Number ( instrumentAnswer . instrumentId )
112+ ) ;
113+ if ( ! instrument ) {
114+ return null ;
115+ }
116+
117+ const timeUnit = `${ call . allocationTimeUnit } ${
118+ formattedRequestedTime > 1 || formattedRequestedTime === 0 ? 's' : ''
119+ } `;
120+
121+ return [
122+ `${ instrument . name } :` ,
123+ instrument . description ,
124+ formattedRequestedTime ,
125+ timeUnit ,
126+ ] . join ( ' ' ) ;
127+ } )
128+ . filter ( ( request ) : request is string => request !== null )
129+ . join ( ', ' ) ;
79130
80131 const shortDateFormat = new Intl . DateTimeFormat ( 'en-GB' , {
81132 month : 'short' ,
@@ -115,19 +166,17 @@ export async function proposalSubmittedHandler(event: ApplicationEvent) {
115166 submittedOn : event . proposal . submittedDate ! . toLocaleString ( ) ,
116167 accessRoute : workflow ?. name || 'N/A' ,
117168 principalInvestigator :
118- principalInvestigator . preferredname +
169+ ( principalInvestigator . preferredname ||
170+ principalInvestigator . firstname ) +
119171 ' ' +
120172 principalInvestigator . lastname ,
121173 establishment : principalInvestigator . institution ,
122174 alternativeContacts : '' ,
123175 coinvestigators : participants . map (
124- ( partipant ) => `${ partipant . preferredname } ${ partipant . lastname } `
176+ ( partipant ) =>
177+ `${ partipant . preferredname || partipant . firstname } ${ partipant . lastname } `
125178 ) ,
126- requested : instruments
127- . map ( ( instrument ) => {
128- return `${ instrument . name } : ${ instrument . description } ${ instrument . managementTimeAllocation } ${ call . allocationTimeUnit } ${ instrument . managementTimeAllocation > 1 ? 's' : '' } ` ;
129- } )
130- . join ( ', ' ) ,
179+ requested,
131180 } ,
132181 allocationPeriod : allocationPeriod ,
133182 deadline : longDateFormat . format ( call . endCall ) ,
@@ -153,7 +202,7 @@ export async function proposalSubmittedHandler(event: ApplicationEvent) {
153202 ...options ,
154203 substitution_data : {
155204 ...( options . substitution_data as any ) ,
156- name : participant . preferredname ,
205+ name : participant . preferredname || participant . firstname ,
157206 } ,
158207 recipients : [
159208 {
0 commit comments