Skip to content

Commit baba63b

Browse files
authored
Merge branch 'develop' into feature/DEVSU-2656-email-notification
2 parents 6d12ab8 + 5ea6d17 commit baba63b

6 files changed

Lines changed: 128 additions & 10 deletions

File tree

app/libs/createReport.js

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const EXCLUDE_SECTIONS = new Set([
2121
'ReportUserFilter',
2222
'users',
2323
'kbMatchedStatements',
24+
'observedVariantAnnotations',
2425
]);
2526

2627
/**
@@ -126,6 +127,42 @@ const createStatementMatching = async (reportId, content, createdKbMatches, tran
126127
}
127128
};
128129

130+
/**
131+
* Creates a new section for the report with the provided data
132+
*
133+
* @param {Number} reportId - The id of the report this section belongs to
134+
* @param {Array|Object} sectionContent - The record or records to be created for this section
135+
* @param {object} options - Options for creating report sections
136+
* @property {object} options.transaction - Transaction to run bulkCreate under
137+
*
138+
* @returns {undefined}
139+
*/
140+
const createReportObservedVariantAnnotationSection = async (reportId, sectionContent, options = {}) => {
141+
const records = Array.isArray(sectionContent)
142+
? sectionContent
143+
: [sectionContent];
144+
const retvals = [];
145+
try {
146+
for (const record of records) {
147+
const annotationData = {
148+
variantType: record.variantType,
149+
variantId: record.variantId,
150+
annotations: record.annotations,
151+
};
152+
153+
const annotation = await db.models.observedVariantAnnotations.create({
154+
reportId,
155+
...annotationData,
156+
}, options);
157+
annotation.dataValues.variant = record.variant;
158+
retvals.push(annotation.dataValues);
159+
}
160+
return retvals;
161+
} catch (error) {
162+
throw new Error(`Unable to create section observedVariantAnnotation: ${error.message || error}`);
163+
}
164+
};
165+
129166
/**
130167
* Given the content for a report to be created, pull gene names from
131168
* the variant sections and create the genes which will be used as
@@ -237,10 +274,8 @@ const createReportVariantsSection = async (reportId, genesRecordsByName, modelNa
237274
keyCheck.add(key);
238275
}
239276
}
240-
241277
try {
242278
if (modelName === 'structuralVariants') {
243-
// add the gene FK associations
244279
records = await db.models[modelName].bulkCreate(sectionContent.map(({
245280
key, gene1, gene2, ...newEntry
246281
}) => {
@@ -252,7 +287,6 @@ const createReportVariantsSection = async (reportId, genesRecordsByName, modelNa
252287
};
253288
}), options);
254289
} else {
255-
// add the gene FK association
256290
records = await db.models[modelName].bulkCreate(sectionContent.map(({key, gene, ...newEntry}) => {
257291
return {
258292
...newEntry,
@@ -264,7 +298,6 @@ const createReportVariantsSection = async (reportId, genesRecordsByName, modelNa
264298
} catch (error) {
265299
throw new Error(`Unable to create variant section (${modelName}) ${error.message || error}`);
266300
}
267-
268301
const mapping = {};
269302
for (let i = 0; i < sectionContent.length; i++) {
270303
if (sectionContent[i].key) {
@@ -277,7 +310,6 @@ const createReportVariantsSection = async (reportId, genesRecordsByName, modelNa
277310
const createReportVariantSections = async (report, content, transaction) => {
278311
// create the genes first since they will need to be linked to the variant records
279312
const geneDefns = await createReportGenes(report, content, {transaction});
280-
281313
// create the variants and create a mapping from their input 'key' value to the new records
282314
const variantMapping = {};
283315
const variantPromises = Object.keys(KB_PIVOT_MAPPING).map(async (variantType) => {
@@ -287,16 +319,14 @@ const createReportVariantSections = async (report, content, transaction) => {
287319
if (!Array.isArray(content[variantModel]) && typeof content[variantModel] === 'object') {
288320
content[variantModel] = [content[variantModel]];
289321
}
290-
291322
const mapping = await createReportVariantsSection(report.id, geneDefns, variantModel, content[variantModel] || [], {transaction});
323+
292324
variantMapping[variantType] = mapping;
293325
});
294-
295326
// create the probe results (linked to gene but not to kbMatches)
296327
variantPromises.push(createReportVariantsSection(report.id, geneDefns, 'probeResults', content.probeResults || [], {transaction}));
297328

298329
await Promise.all(variantPromises);
299-
300330
// then the kb matches which must be linked to the variants
301331
const kbMatches = (content.kbMatches || []).map(({variant, variantType, ...match}) => {
302332
if (variantMapping[variantType] === undefined) {
@@ -313,6 +343,19 @@ const createReportVariantSections = async (report, content, transaction) => {
313343
const createdKbMatches = await createReportKbMatchSection(report.id, 'kbMatches', kbMatches, {transaction});
314344

315345
await createStatementMatching(report.id, content, createdKbMatches, transaction);
346+
// then the observed variant annotations which must be linked to the variants
347+
348+
const observedVariantAnnotations = (content.observedVariantAnnotations || []).map(({variant, variantType, ...annotation}) => {
349+
if (variantMapping[variantType] === undefined) {
350+
throw new Error(`cannot link annotations to variant type ${variantType} as none were specified`);
351+
}
352+
if (variantMapping[variantType][variant] === undefined) {
353+
throw new Error(`invalid link (variant=${variant}) variant definition does not exist`);
354+
}
355+
return {...annotation, variantId: variantMapping[variantType][variant], variantType, variant};
356+
});
357+
358+
await createReportObservedVariantAnnotationSection(report.id, observedVariantAnnotations, {transaction});
316359
};
317360

318361
/**

app/models/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,13 @@ for (const [pivotValue, modelName] of Object.entries(KB_PIVOT_MAPPING)) {
424424
});
425425
}
426426

427+
observedVariantAnnotations.belongsTo(analysisReports, {
428+
as: 'report', foreignKey: 'reportId', targetKey: 'id', onDelete: 'CASCADE', constraints: true, foreignKeyConstraint: true,
429+
});
430+
analysisReports.hasMany(observedVariantAnnotations, {
431+
as: 'observedVariantAnnotations', foreignKey: 'reportId', onDelete: 'CASCADE', constraints: true, foreignKeyConstraint: true,
432+
});
433+
427434
// Presentation Data
428435
const presentation = {};
429436
presentation.discussion = require('./reports/genomic/presentation/discussion.model')(sequelize, Sq);

app/schemas/report/reportUpload/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const db = require('../../../models');
22
const {BASE_EXCLUDE} = require('../../exclude');
33
const variantSchemas = require('./variant');
44
const kbMatchesSchema = require('./kbMatches');
5+
const observedVariantAnnotationsSchema = require('./observedVariantAnnotations');
56
const schemaGenerator = require('../../schemaGenerator');
67
const {UPLOAD_BASE_URI} = require('../../../constants');
78

@@ -68,7 +69,7 @@ const generateReportUploadSchema = (isJsonSchema) => {
6869
presentationSlides, users, projects, ...associations
6970
} = db.models.report.associations;
7071

71-
schema.definitions = {...variantSchemas(isJsonSchema), kbMatches: kbMatchesSchema(isJsonSchema)};
72+
schema.definitions = {...variantSchemas(isJsonSchema), kbMatches: kbMatchesSchema(isJsonSchema), observedVariantAnnotations: observedVariantAnnotationsSchema(isJsonSchema)};
7273

7374
// add all associated schemas
7475
Object.values(associations).forEach((association) => {
@@ -86,7 +87,6 @@ const generateReportUploadSchema = (isJsonSchema) => {
8687
schema.definitions[model] = generatedSchema;
8788
}
8889
});
89-
9090
return schema;
9191
};
9292

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const db = require('../../../models');
2+
const schemaGenerator = require('../../schemaGenerator');
3+
const {REPORT_EXCLUDE} = require('../../exclude');
4+
5+
module.exports = (isJsonSchema) => {
6+
return schemaGenerator(db.models.observedVariantAnnotations, {
7+
isJsonSchema,
8+
properties: {
9+
variant: {
10+
type: 'string', description: 'the variant key linking this to one of the variant records',
11+
},
12+
annotations: {type: 'object', description: 'json annotations'},
13+
},
14+
isSubSchema: true,
15+
exclude: [...REPORT_EXCLUDE, 'variantId'],
16+
required: ['variant'],
17+
});
18+
};

test/reportUpload.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,19 @@ describe('Tests for uploading a report and all of its components', () => {
226226
expect(totalExpectedStatements).toEqual(kbStatements.length);
227227
});
228228

229+
test('ObservedVariantAnnotations are linked correctly to observed variants', async () => {
230+
const muts = await db.models.smallMutations.findAll({where: {report_id: reportId}});
231+
const test3mut = muts.filter((mut) => {
232+
return mut.hgvsProtein === 'ZFP36L2:p.Q111del-test3';
233+
})[0];
234+
const annotations = await db.models.observedVariantAnnotations.findAll({where: {report_id: reportId}});
235+
const test3annotation = annotations.filter((ann) => {
236+
return ann.annotations.isThisAnObservedVariantAnnotationsTest;
237+
})[0];
238+
expect(test3annotation.variantType).toEqual('mut');
239+
expect(test3annotation.variantId).toEqual(test3mut.id);
240+
});
241+
229242
test('One variant record created for each input kbmatch record', async () => {
230243
const variants = mockReportData.kbMatches;
231244
expect(variants.length).toEqual(kbVariants.length);

test/testData/mockReportData.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,15 @@
494494
"kbStatementRelated": true
495495
}
496496
],
497+
"observedVariantAnnotations": [
498+
{
499+
"variant": "test3",
500+
"variantType": "mut",
501+
"annotations": {
502+
"isThisAnObservedVariantAnnotationsTest": true
503+
}
504+
}
505+
],
497506
"smallMutations": [
498507
{
499508
"gene": "ZFP36L2",
@@ -552,6 +561,34 @@
552561
"library": "TEST LIBRARY",
553562
"selected": false,
554563
"key": "test1"
564+
},
565+
{
566+
"gene": "ZFP36L2",
567+
"transcript": "ENST00000282388",
568+
"proteinChange": "p.Q999del",
569+
"startPosition": 43451739,
570+
"endPosition": 43451739,
571+
"chromosome": "2",
572+
"refSeq": "CCTG",
573+
"altSeq": "C",
574+
"zygosity": "sub [0/3]",
575+
"tumourRefCount": 583,
576+
"tumourAltCount": 4,
577+
"tumourDepth": 590,
578+
"rnaRefCount": 400,
579+
"rnaAltCount": 15,
580+
"rnaDepth": 415,
581+
"normalRefCount": 40,
582+
"normalAltCount": 0,
583+
"normalDepth": 40,
584+
"ncbiBuild": "GRCh37",
585+
"hgvsProtein": "ZFP36L2:p.Q111del-test3",
586+
"germline": false,
587+
"tumourRefCopies": 60,
588+
"tumourAltCopies": 20,
589+
"library": "TEST LIBRARY",
590+
"selected": false,
591+
"key": "test3"
555592
}
556593
],
557594
"mutationSignature": [

0 commit comments

Comments
 (0)