diff --git a/sequencing-server/src/lib/batchLoaders/simulatedActivityBatchLoader.ts b/sequencing-server/src/lib/batchLoaders/simulatedActivityBatchLoader.ts index 074b7258d5..0e0462ba3f 100644 --- a/sequencing-server/src/lib/batchLoaders/simulatedActivityBatchLoader.ts +++ b/sequencing-server/src/lib/batchLoaders/simulatedActivityBatchLoader.ts @@ -14,29 +14,41 @@ export const simulatedActivitiesBatchLoader: BatchLoader< const result = await opts.graphqlClient.batchRequests< { data: { - simulated_activity: GraphQLSimulatedActivityInstance[]; + simulation_dataset: { + id: number; + simulation: { + plan: { + id: number; + model_id: number; + }; + }; + simulation_start_time: string; + dataset: { spans: GQLSpan[] }; + }; }; }[] >( keys.map(key => ({ document: gql` query ($simulationDatasetId: Int!) { - simulated_activity(where: { simulation_dataset_id: { _eq: $simulationDatasetId } }) { + simulation_dataset: simulation_dataset_by_pk(id: $simulationDatasetId) { id - simulation_dataset { - id - simulation { - plan { - model_id - } + simulation { + plan { + model_id + id + } + } + simulation_start_time + dataset { + spans { + id + attributes + start_offset + duration + activity_type_name: type } } - attributes - start_offset - start_time - end_time - duration - activity_type_name } } `, @@ -48,18 +60,33 @@ export const simulatedActivitiesBatchLoader: BatchLoader< return Promise.all( keys.map(async ({ simulationDatasetId }) => { - const simulatedActivities = result.find( - res => res.data.simulated_activity[0]?.simulation_dataset.id === simulationDatasetId, - )?.data.simulated_activity; - if (simulatedActivities === undefined) { + const simulation_dataset = result.find(res => res.data.simulation_dataset.id === simulationDatasetId)?.data + .simulation_dataset; + if (simulation_dataset === undefined) { return new ErrorWithStatusCode(`No simulation_dataset with id: ${simulationDatasetId}`, 404); } + + const spans = simulation_dataset.dataset.spans; + + const simulatedActivities: GraphQLSimulatedActivityInstance[] = spans.map(span => { + return { + id: span.id, + simulation_dataset_id: simulation_dataset.id, + plan_id: simulation_dataset.simulation.plan.id, + model_id: simulation_dataset.simulation.plan.model_id, + attributes: span.attributes, + duration: span.duration, + start_offset: span.start_offset, + simulation_start_time: simulation_dataset.simulation_start_time, + activity_type_name: span.activity_type_name, + }; + }); return Promise.all( simulatedActivities.map(async simulatedActivity => mapGraphQLActivityInstance( simulatedActivity, await opts.activitySchemaDataLoader.load({ - missionModelId: simulatedActivity.simulation_dataset.simulation.plan.model_id, + missionModelId: simulation_dataset.simulation.plan.model_id, activityTypeName: simulatedActivity.activity_type_name, }), ), @@ -77,32 +104,41 @@ export const simulatedActivityInstanceBySimulatedActivityIdBatchLoader: BatchLoa const result = await opts.graphqlClient.batchRequests< { data: { - simulated_activity: GraphQLSimulatedActivityInstance[]; + simulation_dataset: { + id: number; + simulation_start_time: string; + simulation: { + plan: { + id: number; + model_id: number; + }; + }; + dataset: { spans: GQLSpan[] }; + }; }; }[] >( keys.map(key => ({ document: gql` query ($simulationDatasetId: Int!, $simulatedActivityId: Int!) { - simulated_activity( - where: { simulation_dataset_id: { _eq: $simulationDatasetId }, id: { _eq: $simulatedActivityId } } - ) { + simulation_dataset: simulation_dataset_by_pk(id: $simulationDatasetId) { id - simulation_dataset { - id - simulation { - plan { - model_id - } - plan_id + simulation_start_time + simulation { + plan { + id + model_id + } + } + dataset { + spans: spans(where: { id: { _eq: $simulatedActivityId } }) { + id + attributes + start_offset + duration + activity_type_name: type } } - attributes - start_offset - start_time - end_time - duration - activity_type_name } } `, @@ -115,21 +151,44 @@ export const simulatedActivityInstanceBySimulatedActivityIdBatchLoader: BatchLoa return Promise.all( keys.map(async ({ simulationDatasetId, simulatedActivityId }) => { - const simulatedActivity = result.find( - res => - res.data.simulated_activity[0]?.simulation_dataset.id === simulationDatasetId && - res.data.simulated_activity[0]?.id === simulatedActivityId, - )?.data.simulated_activity[0]; - if (simulatedActivity === undefined) { + const simulation_dataset = result.find(res => + res.data?.simulation_dataset?.dataset?.spans?.some(span => span.id === simulatedActivityId), + )?.data.simulation_dataset; + if (simulation_dataset === undefined) { + return new ErrorWithStatusCode(`No simulation_dataset with id: ${simulationDatasetId}`, 404); + } + + const spans = simulation_dataset?.dataset.spans; + if (spans === undefined || spans.length === 0 || spans[0] === undefined) { return new ErrorWithStatusCode( `No simulation_dataset with id: ${simulationDatasetId} and simulated activity id: ${simulatedActivityId}`, 404, ); } + + if(spans.length > 1) { + return new ErrorWithStatusCode( + `Too many spans with simulated activity id ${simulatedActivityId} found for simulation_dataset with id ${simulationDatasetId}`, + 404, + ); + } + + const span = spans[0]; + const simulatedActivity: GraphQLSimulatedActivityInstance = { + id: span.id, + simulation_dataset_id: simulation_dataset.id, + plan_id: simulation_dataset.simulation.plan.id, + model_id: simulation_dataset.simulation.plan.model_id, + attributes: span.attributes, + duration: span.duration, + start_offset: span.start_offset, + simulation_start_time: simulation_dataset.simulation_start_time, + activity_type_name: span.activity_type_name, + }; return mapGraphQLActivityInstance( simulatedActivity, await opts.activitySchemaDataLoader.load({ - missionModelId: simulatedActivity.simulation_dataset.simulation.plan.model_id, + missionModelId: simulation_dataset.simulation.plan.model_id, activityTypeName: simulatedActivity.activity_type_name, }), ); @@ -173,25 +232,29 @@ export interface SimulatedActivity< activityTypeName: string; } +export interface GQLSpan< + ActivityArguments extends Record = Record, + ActivityComputedAttributes extends Record = Record, +> { + id: number; + attributes: GraphQLSimulatedActivityAttributes; + start_offset: string; + duration: string; + activity_type_name: string; +} + export interface GraphQLSimulatedActivityInstance< ActivityArguments extends Record = Record, ActivityComputedAttributes extends Record = Record, > { id: number; - simulation_dataset: { - id: number; - simulation: { - plan: { - model_id: number; - }; - plan_id: number; - }; - }; + simulation_dataset_id: number; + plan_id: number; + model_id: number; attributes: GraphQLSimulatedActivityAttributes; duration: string; start_offset: string; - start_time: string; - end_time: string; + simulation_start_time: string; activity_type_name: string; } @@ -199,18 +262,28 @@ export function mapGraphQLActivityInstance( activityInstance: GraphQLSimulatedActivityInstance, activitySchema: GraphQLActivitySchema, ): SimulatedActivity { + const duration = activityInstance.duration + ? Temporal.Duration.from(parse(activityInstance.duration).toISOString()) + : null; + const startOffset: Temporal.Duration = Temporal.Duration.from(parse(activityInstance.start_offset).toISOString()); + const startTime: Temporal.Instant = Temporal.Instant.from(activityInstance.simulation_start_time) + .toZonedDateTimeISO('UTC') + .add(startOffset) + .toInstant(); + const endTime = duration ? startTime.toZonedDateTimeISO('UTC').add(duration).toInstant() : null; + return { simulationDataset: { simulation: { - planId: activityInstance.simulation_dataset.simulation.plan_id, + planId: activityInstance.plan_id, }, }, id: activityInstance.id, - duration: activityInstance.duration ? Temporal.Duration.from(parse(activityInstance.duration).toISOString()) : null, - startOffset: Temporal.Duration.from(parse(activityInstance.start_offset).toISOString()), - startTime: Temporal.Instant.from(activityInstance.start_time), - endTime: activityInstance.end_time ? Temporal.Instant.from(activityInstance.end_time) : null, - simulationDatasetId: activityInstance.simulation_dataset.id, + duration, + startOffset, + startTime, + endTime, + simulationDatasetId: activityInstance.simulation_dataset_id, activityTypeName: activityInstance.activity_type_name, attributes: { arguments: Object.entries(activityInstance.attributes.arguments).reduce((acc, [key, value]) => { @@ -219,7 +292,7 @@ export function mapGraphQLActivityInstance( acc[key] = convertType(value, param.schema); } return acc; - }, {} as { [attributeName: string]: any }), + }, {} as Record), directiveId: activityInstance.attributes.directiveId, computed: activityInstance.attributes.computedAttributes ? convertType(activityInstance.attributes.computedAttributes, activitySchema.computed_attributes_value_schema) diff --git a/sequencing-server/src/routes/seqjson.ts b/sequencing-server/src/routes/seqjson.ts index 14a3fc2b40..3aeedd93f2 100644 --- a/sequencing-server/src/routes/seqjson.ts +++ b/sequencing-server/src/routes/seqjson.ts @@ -137,25 +137,22 @@ seqjsonRouter.post('/get-seqjson-for-seqid-and-simulation-dataset', async (req, errors: ReturnType[] | null; }>( ` - with joined_table as (select activity_instance_commands.commands, - activity_instance_commands.activity_instance_id, - activity_instance_commands.errors, - activity_instance_commands.expansion_run_id - from sequence - join sequence_to_simulated_activity - on sequence.seq_id = sequence_to_simulated_activity.seq_id and - sequence.simulation_dataset_id = - sequence_to_simulated_activity.simulation_dataset_id - join activity_instance_commands - on sequence_to_simulated_activity.simulated_activity_id = - activity_instance_commands.activity_instance_id - join expansion_run - on activity_instance_commands.expansion_run_id = expansion_run.id - where sequence.seq_id = $2 - and sequence.simulation_dataset_id = $1), - max_values as (select activity_instance_id, max(expansion_run_id) as max_expansion_run_id - from joined_table - group by activity_instance_id) + with joined_table as ( + select aic.commands, + aic.activity_instance_id, + aic.errors, + aic.expansion_run_id + from sequence_to_simulated_activity ssa + join activity_instance_commands aic + on ssa.simulated_activity_id = aic.activity_instance_id + where (ssa.simulation_dataset_id, ssa.seq_id) = ($1, $2)), + max_values as ( + select + activity_instance_id, + max(expansion_run_id) as max_expansion_run_id + from joined_table + group by activity_instance_id + ) select joined_table.commands, joined_table.activity_instance_id, joined_table.errors @@ -283,23 +280,16 @@ seqjsonRouter.post('/bulk-get-seqjson-for-seqid-and-simulation-dataset', async ( with joined_table as ( select - activity_instance_commands.commands, - activity_instance_commands.activity_instance_id, - activity_instance_commands.errors, - activity_instance_commands.expansion_run_id, - sequence.seq_id, - sequence.simulation_dataset_id - from sequence - join sequence_to_simulated_activity - on sequence.seq_id = sequence_to_simulated_activity.seq_id - and sequence.simulation_dataset_id = - sequence_to_simulated_activity.simulation_dataset_id - join activity_instance_commands - on sequence_to_simulated_activity.simulated_activity_id = - activity_instance_commands.activity_instance_id - join expansion_run - on activity_instance_commands.expansion_run_id = expansion_run.id - where (sequence.seq_id, sequence.simulation_dataset_id) in (${pgFormat('%L', inputTuples)}) + aic.commands, + aic.activity_instance_id, + aic.errors, + aic.expansion_run_id, + ssa.seq_id, + ssa.simulation_dataset_id + from sequence_to_simulated_activity ssa + join activity_instance_commands aic + on ssa.simulated_activity_id = aic.activity_instance_id + where (ssa.seq_id, ssa.simulation_dataset_id) in (${pgFormat('%L', inputTuples)}) ), max_values as ( select @@ -328,8 +318,8 @@ seqjsonRouter.post('/bulk-get-seqjson-for-seqid-and-simulation-dataset', async ( }>( ` select metadata, seq_id, simulation_dataset_id - from sequence - where (sequence.seq_id, sequence.simulation_dataset_id) in (${pgFormat('%L', inputTuples)}); + from sequence s + where (s.seq_id, s.simulation_dataset_id) in (${pgFormat('%L', inputTuples)}); `, ), ]);