Skip to content

Commit

Permalink
SIMSBIOHUB-424: Publish Documents to BioHub from SIMS (#1193)
Browse files Browse the repository at this point in the history
Submit artifacts to BioHub as part of Survey Submission.
---------
Co-authored-by: Curtis Upshall <[email protected]>
Co-authored-by: Al Rosenthal <[email protected]>
Co-authored-by: Nick Phura <[email protected]>
Co-authored-by: Nick Phura <[email protected]>
  • Loading branch information
KjartanE authored Jan 23, 2024
1 parent c93d780 commit 000209e
Show file tree
Hide file tree
Showing 50 changed files with 659 additions and 2,959 deletions.
36 changes: 28 additions & 8 deletions api/src/models/biohub-create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
PostSurveySubmissionToBioHubObject,
PostSurveyToBiohubObject
} from './biohub-create';
import { GetSurveyData } from './survey-view';
import { GetSurveyData, GetSurveyPurposeAndMethodologyData } from './survey-view';

describe('PostSurveyObservationToBiohubObject', () => {
describe('All values provided', () => {
Expand Down Expand Up @@ -114,7 +114,13 @@ describe('PostSurveyToBiohubObject', () => {
} as GetSurveyData;

before(() => {
data = new PostSurveyToBiohubObject(survey_obj, [observation_obj], { type: 'FeatureCollection', features: [] });
data = new PostSurveyToBiohubObject(
survey_obj,
[observation_obj],
{ type: 'FeatureCollection', features: [] },
[],
[]
);
});

it('sets id', () => {
Expand All @@ -139,7 +145,7 @@ describe('PostSurveyToBiohubObject', () => {
});

it('sets features', () => {
expect(data.features).to.eql([new PostSurveyObservationToBiohubObject(observation_obj)]);
expect(data.child_features).to.eql([new PostSurveyObservationToBiohubObject(observation_obj)]);
});
});
});
Expand Down Expand Up @@ -180,19 +186,29 @@ describe('PostSurveySubmissionToBioHubObject', () => {
revision_count: 1
};

const purpose_and_methodology: GetSurveyPurposeAndMethodologyData = {
intended_outcome_ids: [],
additional_details: 'A description of the purpose',
revision_count: 0,
vantage_code_ids: []
};

const survey_geometry: FeatureCollection = {
type: 'FeatureCollection',
features: []
};

const additionalInformation = 'A description of the submission';
const submissionComment = 'A comment about the submission';

before(() => {
data = new PostSurveySubmissionToBioHubObject(
survey_obj,
purpose_and_methodology,
observation_obj,
survey_geometry,
additionalInformation
[],
[],
submissionComment
);
});

Expand All @@ -205,11 +221,15 @@ describe('PostSurveySubmissionToBioHubObject', () => {
});

it('sets description', () => {
expect(data.description).to.equal('A description of the submission');
expect(data.description).to.equal('A description of the purpose');
});

it('sets features', () => {
expect(data.features).to.eql([new PostSurveyToBiohubObject(survey_obj, observation_obj, survey_geometry)]);
it('sets comment', () => {
expect(data.comment).to.equal('A comment about the submission');
});

it('sets content', () => {
expect(data.content).to.eql(new PostSurveyToBiohubObject(survey_obj, observation_obj, survey_geometry, [], []));
});
});
});
125 changes: 112 additions & 13 deletions api/src/models/biohub-create.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { FeatureCollection } from 'geojson';
import { ATTACHMENT_TYPE } from '../constants/attachments';
import { ISurveyAttachment, ISurveyReportAttachment } from '../repositories/attachment-repository';
import { ObservationRecord } from '../repositories/observation-repository';
import { getLogger } from '../utils/logger';
import { GetSurveyData } from './survey-view';
import { GetSurveyData, GetSurveyPurposeAndMethodologyData } from './survey-view';

const defaultLog = getLogger('models/biohub-create');

export interface BioHubSubmission {
id: string;
name: string;
description: string;
content: BioHubSubmissionFeature;
}
export interface BioHubSubmissionFeature {
id: string;
type: string;
properties: Record<string, any>;
features: BioHubSubmissionFeature[];
child_features: BioHubSubmissionFeature[];
}

/**
Expand All @@ -23,7 +31,7 @@ export class PostSurveyObservationToBiohubObject implements BioHubSubmissionFeat
id: string;
type: string;
properties: Record<string, any>;
features: BioHubSubmissionFeature[];
child_features: BioHubSubmissionFeature[];

constructor(observationRecord: ObservationRecord) {
defaultLog.debug({ label: 'PostSurveyObservationToBiohubObject', message: 'params', observationRecord });
Expand Down Expand Up @@ -58,7 +66,68 @@ export class PostSurveyObservationToBiohubObject implements BioHubSubmissionFeat
: []
}
};
this.features = [];
this.child_features = [];
}
}

/**
* Object to be sent to Biohub API for creating an artifact (for a SIMS attachment).
*
* @export
* @class PostSurveyAttachmentsToBiohubObject
* @implements {BioHubSubmissionFeature}
*/
export class PostSurveyAttachmentsToBiohubObject implements BioHubSubmissionFeature {
id: string;
type: string;
properties: Record<string, any>;
child_features: BioHubSubmissionFeature[];

constructor(attachmentRecord: ISurveyAttachment) {
defaultLog.debug({ label: 'PostSurveyAttachmentsToBiohubObject', message: 'params', attachmentRecord });

this.id = attachmentRecord.uuid;
this.type = BiohubFeatureType.ARTIFACT;
this.properties = {
artifact_id: attachmentRecord.survey_attachment_id,
filename: attachmentRecord.file_name,
file_type: attachmentRecord.file_type,
file_size: attachmentRecord.file_size,
title: attachmentRecord?.title || null,
description: attachmentRecord?.description || null
};
this.child_features = [];
}
}

/**
* Object to be sent to Biohub API for creating an artifact (for a SIMS report attachment).
*
* @export
* @class PostSurveyReportAttachmentsToBiohubObject
* @implements {BioHubSubmissionFeature}
*/
export class PostSurveyReportAttachmentsToBiohubObject implements BioHubSubmissionFeature {
id: string;
type: string;
properties: Record<string, any>;
child_features: BioHubSubmissionFeature[];

constructor(reportAttachmentRecord: ISurveyReportAttachment) {
defaultLog.debug({ label: 'PostSurveyReportAttachmentsToBiohubObject', message: 'params', reportAttachmentRecord });

this.id = reportAttachmentRecord.uuid;
this.type = BiohubFeatureType.ARTIFACT;
this.properties = {
artifact_id: reportAttachmentRecord.survey_report_attachment_id,
filename: reportAttachmentRecord.file_name,
file_type: ATTACHMENT_TYPE.REPORT,
file_size: reportAttachmentRecord.file_size,
title: reportAttachmentRecord.title,
description: reportAttachmentRecord.description,
year_published: reportAttachmentRecord.year_published
};
this.child_features = [];
}
}

Expand All @@ -73,11 +142,29 @@ export class PostSurveyToBiohubObject implements BioHubSubmissionFeature {
id: string;
type: string;
properties: Record<string, any>;
features: PostSurveyObservationToBiohubObject[];
child_features: PostSurveyObservationToBiohubObject[];

constructor(surveyData: GetSurveyData, observationRecords: ObservationRecord[], surveyGeometry: FeatureCollection) {
constructor(
surveyData: GetSurveyData,
observationRecords: ObservationRecord[],
surveyGeometry: FeatureCollection,
surveyAttachments: ISurveyAttachment[],
surveyReports: ISurveyReportAttachment[]
) {
defaultLog.debug({ label: 'PostSurveyToBiohubObject', message: 'params', surveyData });

const observationFeatures = observationRecords.map(
(observation) => new PostSurveyObservationToBiohubObject(observation)
);

const attachmentFeatures = surveyAttachments.map(
(attachment) => new PostSurveyAttachmentsToBiohubObject(attachment)
);

const reportAttachmentFeatures = surveyReports.map(
(attachment) => new PostSurveyReportAttachmentsToBiohubObject(attachment)
);

this.id = surveyData.uuid;
this.type = BiohubFeatureType.DATASET;
this.properties = {
Expand All @@ -90,32 +177,44 @@ export class PostSurveyToBiohubObject implements BioHubSubmissionFeature {
revision_count: surveyData.revision_count,
geometry: surveyGeometry
};
this.features = observationRecords.map((observation) => new PostSurveyObservationToBiohubObject(observation));
this.child_features = [...observationFeatures, ...reportAttachmentFeatures, ...attachmentFeatures];
}
}

export class PostSurveySubmissionToBioHubObject {
export class PostSurveySubmissionToBioHubObject implements BioHubSubmission {
id: string;
name: string;
description: string;
features: BioHubSubmissionFeature[];
comment: string;
content: BioHubSubmissionFeature;

constructor(
surveyData: GetSurveyData,
GetSurveyPurposeAndMethodologyData: GetSurveyPurposeAndMethodologyData,
observationRecords: ObservationRecord[],
surveyGeometry: FeatureCollection,
additionalInformation: string
surveyAttachments: ISurveyAttachment[],
surveyReports: ISurveyReportAttachment[],
submissionComment: string
) {
defaultLog.debug({ label: 'PostSurveySubmissionToBioHubObject' });

this.id = surveyData.uuid;
this.name = surveyData.survey_name;
this.description = additionalInformation;
this.features = [new PostSurveyToBiohubObject(surveyData, observationRecords, surveyGeometry)];
this.description = GetSurveyPurposeAndMethodologyData.additional_details;
this.comment = submissionComment;
this.content = new PostSurveyToBiohubObject(
surveyData,
observationRecords,
surveyGeometry,
surveyAttachments,
surveyReports
);
}
}

export enum BiohubFeatureType {
DATASET = 'dataset',
OBSERVATION = 'observation'
OBSERVATION = 'observation',
ARTIFACT = 'artifact'
}
3 changes: 2 additions & 1 deletion api/src/models/project-survey-attachments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { default as dayjs } from 'dayjs';
import { ATTACHMENT_TYPE } from '../constants/attachments';
import { getLogger } from '../utils/logger';
import { SurveySupplementaryData } from './survey-view';

Expand Down Expand Up @@ -28,7 +29,7 @@ export class GetAttachmentsWithSupplementalData {
attachment.project_attachment_id ||
attachment.project_report_attachment_id;
this.fileName = attachment.file_name;
this.fileType = attachment.file_type || 'Report';
this.fileType = attachment.file_type || ATTACHMENT_TYPE.REPORT;
this.lastModified = dayjs(attachment.update_date || attachment.create_date).toISOString();
this.size = attachment.file_size;
this.status = attachment.status;
Expand Down
4 changes: 2 additions & 2 deletions api/src/models/project-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface IGetProject {

export const ProjectData = z.object({
project_id: z.number(),
uuid: z.string(),
uuid: z.string().uuid(),
project_name: z.string(),
project_programs: z.array(z.number()),
start_date: z.string(),
Expand All @@ -35,7 +35,7 @@ export type ProjectData = z.infer<typeof ProjectData>;

export const ProjectListData = z.object({
project_id: z.number(),
uuid: z.string(),
uuid: z.string().uuid(),
project_name: z.string(),
project_programs: z.array(z.number()).default([]),
regions: z.array(z.string()).default([]),
Expand Down
4 changes: 2 additions & 2 deletions api/src/paths/project/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('create', () => {

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

sinon.stub(ProjectService.prototype, 'createProjectAndUploadMetadataToBioHub').resolves(1);
sinon.stub(ProjectService.prototype, 'createProject').resolves(1);

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand All @@ -51,7 +51,7 @@ describe('create', () => {

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

sinon.stub(ProjectService.prototype, 'createProjectAndUploadMetadataToBioHub').rejects(new Error('a test error'));
sinon.stub(ProjectService.prototype, 'createProject').rejects(new Error('a test error'));

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand Down
2 changes: 1 addition & 1 deletion api/src/paths/project/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function createProject(): RequestHandler {

const projectService = new ProjectService(connection);

const projectId = await projectService.createProjectAndUploadMetadataToBioHub(sanitizedProjectPostData);
const projectId = await projectService.createProject(sanitizedProjectPostData);

await connection.commit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,6 @@ describe('deleteAttachment', () => {

await result(sampleReq, (sampleRes as unknown) as any, (null as unknown) as any);

expect(handleDeleteProjectAttachmentStub).to.be.calledOnceWith(1, 2, 'Report', false);
expect(handleDeleteProjectAttachmentStub).to.be.calledOnceWith(1, 2, 'Report');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { PROJECT_PERMISSION, SYSTEM_ROLE } from '../../../../../constants/roles';
import { getDBConnection } from '../../../../../database/db';
import { SystemUser } from '../../../../../repositories/user-repository';
import { authorizeRequestHandler } from '../../../../../request-handlers/security/authorization';
import { AttachmentService } from '../../../../../services/attachment-service';
import { UserService } from '../../../../../services/user-service';
import { getLogger } from '../../../../../utils/logger';
import { attachmentApiDocObject } from '../../../../../utils/shared-api-docs';

Expand Down Expand Up @@ -104,13 +102,10 @@ export function deleteAttachment(): RequestHandler {

const attachmentService = new AttachmentService(connection);

const systemUserObject: SystemUser = req['system_user'];

await attachmentService.handleDeleteProjectAttachment(
Number(req.params.projectId),
Number(req.params.attachmentId),
req.body.attachmentType,
UserService.isAdmin(systemUserObject)
req.body.attachmentType
);

await connection.commit();
Expand Down
4 changes: 2 additions & 2 deletions api/src/paths/project/{projectId}/survey/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('survey/create', () => {

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

sinon.stub(SurveyService.prototype, 'createSurveyAndUploadMetadataToBioHub').resolves(2);
sinon.stub(SurveyService.prototype, 'createSurvey').resolves(2);

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand All @@ -45,7 +45,7 @@ describe('survey/create', () => {

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

sinon.stub(SurveyService.prototype, 'createSurveyAndUploadMetadataToBioHub').rejects(new Error('a test error'));
sinon.stub(SurveyService.prototype, 'createSurvey').rejects(new Error('a test error'));

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand Down
2 changes: 1 addition & 1 deletion api/src/paths/project/{projectId}/survey/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export function createSurvey(): RequestHandler {
await connection.open();

const surveyService = new SurveyService(connection);
const surveyId = await surveyService.createSurveyAndUploadMetadataToBioHub(projectId, sanitizedPostSurveyData);
const surveyId = await surveyService.createSurvey(projectId, sanitizedPostSurveyData);

await connection.commit();

Expand Down
Loading

0 comments on commit 000209e

Please sign in to comment.