-
Notifications
You must be signed in to change notification settings - Fork 9
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
feat: fap data export for a call and refactor data collections for export #629
Changes from 57 commits
f1964aa
fa1d0d1
a742436
20d1974
f88684b
5e0b5be
27a8596
4dad407
1e2881e
24a0909
0d09759
6e9c6bf
4a0e8b8
aaf63a6
a79d8a2
98d310e
a279e63
5b96972
5342ade
07aadb1
861d8a0
fbef88b
663448c
45f0fc1
048e5e8
ec5bf9e
71aca5a
5947277
af6ede6
edcc8d3
2ae0866
3a7139b
4870b3e
42a8d9e
5c844e9
80753a1
c6d1f9b
8d8732f
a0eb9f0
7acfd8d
15a94b0
2f87c1d
9c7574e
843a55f
be781c7
4140ac2
f2f2bf3
5f04967
4e6e8e0
4a05582
8abe212
0b3c7fc
52f8794
1a6dfae
fa15274
d8e9b40
6d1b69c
a95dee0
3d2bfa5
5f7e7b4
1690474
dc5d8ae
c05c9f1
cf8f670
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
DO | ||
$$ | ||
BEGIN | ||
IF register_patch('0156_CreateFAPProposalView.sql', 'Thomas Cottee Meldrum', 'Create View for FAP data', '2024-04-05') THEN | ||
|
||
CREATE VIEW review_data AS | ||
Select proposal.*, grade.avg as average_grade from ( | ||
Select | ||
fp.proposal_pk, | ||
p.proposal_id, | ||
p.title, | ||
i.name as instrument_name, | ||
chi.availability_time, | ||
tr.time_allocation, | ||
f.fap_id, | ||
fmd.rank_order, | ||
c.call_id, | ||
p.proposer_id, | ||
i.instrument_id, | ||
fp.fap_time_allocation, | ||
p.questionary_id | ||
from fap_proposals as fp | ||
join faps as f on f.fap_id = fp.fap_id | ||
join call c on c.call_id = fp.call_id | ||
join proposals p on p.proposal_pk = fp.proposal_pk | ||
join technical_review tr on tr.proposal_pk = p.proposal_pk and tr.instrument_id = fp.instrument_id | ||
left join fap_meeting_decisions as fmd on fmd.proposal_pk = p.proposal_pk | ||
join call_has_instruments as chi on chi.instrument_id = fp.instrument_id and chi.call_id = c.call_id | ||
join instruments as i on i.instrument_id = chi.instrument_id) as proposal | ||
left join ( | ||
Select fr.proposal_pk, AVG(fr.grade) from | ||
fap_proposals as fp | ||
join fap_reviews as fr on fr.proposal_pk = fp.proposal_pk | ||
group by fr.proposal_pk | ||
) grade on grade.proposal_pk = proposal.proposal_pk; | ||
|
||
END IF; | ||
END; | ||
$$ | ||
LANGUAGE plpgsql; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// import { Review } from '../../models/Review'; | ||
import { stripHtml } from 'string-strip-html'; | ||
import { container } from 'tsyringe'; | ||
|
||
import baseContext from '../../buildContext'; | ||
import { Tokens } from '../../config/Tokens'; | ||
import { FapDataSource } from '../../datasources/FapDataSource'; | ||
import { ProposalEndStatus } from '../../models/Proposal'; | ||
import { UserWithRole } from '../../models/User'; | ||
import { RowObj, collectFapXLSXRowData } from './fap'; | ||
|
||
const fapDataSource: FapDataSource = container.resolve(Tokens.FapDataSource); | ||
|
||
const ProposalEndStatusStringValue = { | ||
[ProposalEndStatus.UNSET]: 'Unset', | ||
[ProposalEndStatus.ACCEPTED]: 'Accepted', | ||
[ProposalEndStatus.RESERVED]: 'Reserved', | ||
[ProposalEndStatus.REJECTED]: 'Rejected', | ||
}; | ||
|
||
export type CallRowRowObj = RowObj & { | ||
TCMeldrum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fapMeetingDecision?: string | null; | ||
fapMeetingExComment?: string | null; | ||
fapMeetingInComment?: string | null; | ||
}; | ||
|
||
export const collectCallFapXLSXData = async ( | ||
callId: number, | ||
user: UserWithRole | ||
) => { | ||
const faps = await baseContext.queries.fap.dataSource.getFapsByCallId(callId); | ||
const call = await baseContext.queries.call.get(user, callId); | ||
const filename = `${call?.shortCode}_FAP_Results.xlsx`; | ||
|
||
const baseData = await Promise.all( | ||
faps.map(async (fap) => { | ||
return { | ||
sheetName: fap.code, | ||
rows: await collectFAPRowData(fap.id, callId, user), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that this |
||
}; | ||
}) | ||
); | ||
|
||
return { data: baseData, filename: filename.replace(/\s+/g, '_') }; | ||
}; | ||
|
||
const collectFAPRowData = async ( | ||
fapId: number, | ||
callId: number, | ||
user: UserWithRole | ||
) => { | ||
const data = await collectFapXLSXRowData(fapId, callId, user); | ||
|
||
const extraData = await Promise.all( | ||
data.map(async (sheet) => { | ||
return { | ||
sheetName: sheet.sheetName, | ||
rows: await Promise.all( | ||
sheet.rows.map(async (proposal) => { | ||
const fapMeetingDecision = | ||
await fapDataSource.getProposalsFapMeetingDecisions([ | ||
proposal.proposalPk, | ||
]); | ||
|
||
return { | ||
...proposal, | ||
fapMeetingDecision: fapMeetingDecision[0] | ||
? ProposalEndStatusStringValue[ | ||
fapMeetingDecision[0].recommendation | ||
] | ||
: null, | ||
fapMeetingExComment: fapMeetingDecision[0] | ||
? stripHtml(fapMeetingDecision[0].commentForUser).result | ||
: null, | ||
fapMeetingInComment: fapMeetingDecision[0] | ||
? stripHtml(fapMeetingDecision[0].commentForManagement).result | ||
: null, | ||
}; | ||
}) | ||
), | ||
}; | ||
}) | ||
); | ||
|
||
const allRowData = extraData.map((inst) => { | ||
const instName: (string | number)[][] = [[inst.sheetName]]; | ||
|
||
const sortedData = sortByRankOrAverageScore(inst.rows).map( | ||
(row: CallRowRowObj) => populateRow(row) | ||
); | ||
|
||
instName.concat(instName); | ||
TCMeldrum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return instName.concat(sortedData); | ||
}); | ||
|
||
return allRowData.length | ||
? allRowData.reduce((arr, inst) => { | ||
return arr.concat(inst); | ||
}) | ||
: allRowData; | ||
}; | ||
|
||
function populateRow(row: CallRowRowObj): (string | number)[] { | ||
const individualReviews = row.reviews?.flatMap((rev) => [ | ||
rev.grade, | ||
rev.comment && stripHtml(rev.comment).result, | ||
]); | ||
|
||
return [ | ||
row.propShortCode ?? '<missing>', | ||
row.principalInv ?? '<missing>', | ||
row.piCountry ?? '<missing>', | ||
row.instrName ?? '<missing>', | ||
row.daysRequested ?? '<missing>', | ||
row.propTitle ?? '<missing>', | ||
row.propReviewAvgScore ?? '<missing>', | ||
row.fapTimeAllocation ?? row.daysRequested ?? '<missing>', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be made clear whether the user is looking at the time allocated or requested. |
||
row.fapMeetingDecision ?? '<missing>', | ||
row.fapMeetingInComment ?? '<missing>', | ||
row.fapMeetingExComment ?? '<missing>', | ||
].concat(individualReviews ? individualReviews : []); | ||
} | ||
|
||
export const CallFapDataColumns = [ | ||
'Proposal Reference Number', | ||
'Principal Investigator', | ||
'PI Country', | ||
'Instrument Name', | ||
'Requested Time', | ||
'Proposal Title', | ||
'Average score', | ||
'Fap Time allocation', | ||
'Fap Meeting Decision', | ||
'Fap Meeting Comment for User', | ||
'Fap Meeting Internal Comment', | ||
'Reviewer 1 score', | ||
'Reviewer 1 review comment', | ||
'Reviewer 2 score', | ||
'Reviewer 2 review comment', | ||
]; | ||
|
||
const sortByRankOrder = (a: RowObj, b: RowObj) => { | ||
if (a.propFapRankOrder === b.propFapRankOrder) { | ||
return -1; | ||
} else if (a.propFapRankOrder === null) { | ||
return 1; | ||
} else if (b.propFapRankOrder === null) { | ||
return -1; | ||
} else { | ||
return a.propFapRankOrder > b.propFapRankOrder ? 1 : -1; | ||
} | ||
}; | ||
|
||
const sortByRankOrAverageScore = (data: RowObj[]) => { | ||
let allocationTimeSum = 0; | ||
|
||
return data | ||
.sort((a, b) => | ||
(a.propReviewAvgScore || 0) > (b.propReviewAvgScore || 0) ? 1 : -1 | ||
) | ||
.sort(sortByRankOrder) | ||
.map((row) => { | ||
const proposalAllocationTime = | ||
row.fapTimeAllocation !== null | ||
? row.fapTimeAllocation | ||
: row.techReviewTimeAllocation || 0; | ||
|
||
const isInAvailabilityZone = | ||
allocationTimeSum + proposalAllocationTime <= (row.instrAvailTime || 0); | ||
allocationTimeSum = allocationTimeSum + proposalAllocationTime; | ||
|
||
row.inAvailZone = isInAvailabilityZone ? 'yes' : 'no'; | ||
|
||
return row; | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be removed?