Skip to content
Merged
1 change: 1 addition & 0 deletions apps/backend/src/datasources/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PaginationSortDirection } from '../utils/pagination';
import { ProposalsFilter } from './../resolvers/queries/ProposalsQuery';

export interface ProposalDataSource {
getRequestedTime(proposalPk: number, instrumentId: number): Promise<number>;
getProposalsFromView(
filter?: ProposalsFilter,
first?: number,
Expand Down
7 changes: 7 additions & 0 deletions apps/backend/src/datasources/mockups/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,11 @@ export class ProposalDataSourceMock implements ProposalDataSource {
getInvitedProposal(inviteId: number): Promise<InvitedProposal | null> {
throw new Error('Method not implemented.');
}

async getRequestedTime(
proposalPk: number,
instrumentId: number
): Promise<number> {
return 32;
}
}
26 changes: 26 additions & 0 deletions apps/backend/src/datasources/postgres/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,32 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
});
}

async getRequestedTime(
proposalPk: number,
instrumentId: number
): Promise<number> {
//Non-nullable, time is either requested for the instrument or it is zero.
const result = await database('proposals as p')
.sum({
total_time_requested: database.raw(
"(a.answer->'value'->>'timeRequested')::numeric"
),
})
.innerJoin('questionaries as q2', 'q2.questionary_id', 'p.questionary_id')
.innerJoin('answers as a', 'a.questionary_id', 'q2.questionary_id')
.innerJoin('questions as q', 'q.question_id', 'a.question_id')
.where('p.proposal_pk', proposalPk)
.where('q.data_type', 'INSTRUMENT_PICKER')
.whereRaw("a.answer->'value'->>'instrumentId' = ?", [
instrumentId.toString(),
])
.first();

return result?.total_time_requested
? Number(result.total_time_requested)
: 0;
}

async create(
proposer_id: number,
call_id: number,
Expand Down
21 changes: 21 additions & 0 deletions apps/backend/src/resolvers/queries/ProposalQuery.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { container } from 'tsyringe';
import { Query, Ctx, Resolver, Arg, Int } from 'type-graphql';

import { Tokens } from '../../config/Tokens';
import { ResolverContext } from '../../context';
import { ProposalDataSource } from '../../datasources/ProposalDataSource';
import { Proposal } from '../types/Proposal';

@Resolver()
export class ProposalQuery {
@Query(() => Proposal, { nullable: true })
Expand All @@ -19,4 +23,21 @@ export class ProposalQuery {
): Promise<boolean> {
return context.queries.proposal.get(context.user, proposalPk) !== null;
}

@Query(() => Number, { nullable: false })
async proposalTimeRequested(
@Arg('proposalPk', () => Int) proposalPk: number,
@Arg('instrumentId', () => Int) instrumentId: number,
@Ctx() context: ResolverContext
): Promise<number> {
const proposalDataSource = container.resolve<ProposalDataSource>(
Tokens.ProposalDataSource
);
const timeRequested = await proposalDataSource.getRequestedTime(
proposalPk,
instrumentId
);

return timeRequested || 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type FapProposalWithAverageScoreAndAvailabilityZone = FapProposal & {
proposalAverageScore: number | string;
proposalDeviation: number | string;
isInAvailabilityZone: boolean;
timeRequested: number;
tableData?: { index: number; id: number };
};

Expand Down Expand Up @@ -152,7 +153,7 @@ const FapInstrumentProposalsTable = ({
},
},
{
title: 'Principal Investigator',
title: 'Principal investigator',
render: (rowData: FapProposal) => {
return getFullUserName(rowData.proposal.proposer);
},
Expand Down Expand Up @@ -185,6 +186,10 @@ const FapInstrumentProposalsTable = ({
return rankOrder || '-';
},
},
{
title: 'Time requested',
field: 'timeRequested',
},
{
title: 'Time allocation',
field: 'timeAllocation',
Expand Down Expand Up @@ -223,13 +228,16 @@ const FapInstrumentProposalsTable = ({
];

// NOTE: This is needed for adding the allocation time unit information on the column title without causing some console warning on re-rendering.
const columns = assignmentColumns.map((column) => ({
...column,
title:
column.field === 'timeAllocation'
? `${column.title} (${selectedCall?.allocationTimeUnit}s)`
: column.title,
}));
const columns = assignmentColumns.map((column) => {
if (column.field === 'timeAllocation' || column.field === 'timeRequested') {
return {
...column,
title: `${column.title} (${selectedCall?.allocationTimeUnit}s)`,
};
}

return column;
});

const DragState = {
row: -1,
Expand Down Expand Up @@ -272,18 +280,25 @@ const FapInstrumentProposalsTable = ({
useState<FapProposalWithAverageScoreAndAvailabilityZone[]>([]);

useEffect(() => {
const sortByRankOrAverageScore = (data: FapProposal[]) => {
const sortByRankOrAverageScore = async (data: FapProposal[]) => {
let allocationTimeSum = 0;

return data
.map((proposalData) => {
const returnData = await Promise.all(
data.map(async (proposalData) => {
const proposalAverageScore = average(
getGradesFromReviews(proposalData.proposal.reviews ?? [])
);
const proposalDeviation = standardDeviation(
getGradesFromReviews(proposalData.proposal.reviews ?? [])
);

const result = await api().getProposalTimeRequested({
proposalPk: proposalData.proposal.primaryKey,
instrumentId: fapInstrument.id,
});

const timeRequested = result.proposalTimeRequested;

return {
...proposalData,
proposalAverageScore: isNaN(proposalAverageScore)
Expand All @@ -292,8 +307,12 @@ const FapInstrumentProposalsTable = ({
proposalDeviation: isNaN(proposalDeviation)
? '-'
: proposalDeviation,
timeRequested,
};
})
);

const sortedData = await returnData
.sort((a, b) => {
if (
typeof a.proposalDeviation === 'number' &&
Expand Down Expand Up @@ -343,10 +362,20 @@ const FapInstrumentProposalsTable = ({
};
}
});

return sortedData;
};

const sortedProposals = sortByRankOrAverageScore(instrumentProposalsData);
setSortedProposalsWithAverageScore(sortedProposals);
const run = async () => {
const sortedProposals = await sortByRankOrAverageScore(
instrumentProposalsData
);
setSortedProposalsWithAverageScore(sortedProposals);
};

if (instrumentProposalsData.length) {
run();
}
}, [instrumentProposalsData, fapInstrument.availabilityTime]);

const ProposalTimeAllocationColumn = (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query getProposalTimeRequested($proposalPk: Int!, $instrumentId: Int!) {
proposalTimeRequested(
proposalPk: $proposalPk,
instrumentId: $instrumentId
)
}
Loading