Skip to content

Commit 1e8a112

Browse files
Merge pull request #490 from bcgsc/release/v8.5.0
Release/v8.5.0
2 parents 284970f + d3798dc commit 1e8a112

20 files changed

Lines changed: 316 additions & 107 deletions

app/libs/helperFunctions.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,38 @@ const removeKeys = (obj, targetKey) => {
227227
return obj;
228228
};
229229

230+
const cleanSampleInfo = (sampleInfo = []) => {
231+
return sampleInfo.map((sampleInfoObject) => {
232+
return {
233+
sample: sampleInfoObject.Sample ?? sampleInfoObject.sample,
234+
pathoTc: sampleInfoObject['Patho TC'] ?? sampleInfoObject.pathoTc,
235+
biopsySite: sampleInfoObject['Biopsy Site'] ?? sampleInfoObject.biopsySite,
236+
biopsyType: sampleInfoObject['Biopsy Type'] ?? sampleInfoObject.biopsyType,
237+
sampleName: sampleInfoObject['Sample Name'] ?? sampleInfoObject.sampleName,
238+
primarySite: sampleInfoObject['Primary Site'] ?? sampleInfoObject.primarySite,
239+
collectionDate: sampleInfoObject['Collection Date'] ?? sampleInfoObject.collectionDate,
240+
};
241+
});
242+
};
243+
244+
const cleanSeqQC = (seqQC = []) => {
245+
return seqQC.map((seqQCObject) => {
246+
return {
247+
reads: seqQCObject.Reads ?? seqQCObject.reads,
248+
bioQC: seqQCObject.bioQC,
249+
labQC: seqQCObject.labQC,
250+
sample: seqQCObject.Sample ?? seqQCObject.sample,
251+
library: seqQCObject.Library ?? seqQCObject.library,
252+
coverage: seqQCObject.Coverage ?? seqQCObject.coverage,
253+
inputNg: seqQCObject.Input_ng ?? seqQCObject.inputNg,
254+
inputUg: seqQCObject.Input_ug ?? seqQCObject.inputUg,
255+
protocol: seqQCObject.Protocol ?? seqQCObject.protocol,
256+
sampleName: seqQCObject['Sample Name'] ?? seqQCObject.sampleName,
257+
duplicateReadsPerc: seqQCObject.Duplicate_Reads_Perc ?? seqQCObject.duplicateReadsPerc,
258+
};
259+
});
260+
};
261+
230262
module.exports = {
231263
includesAll,
232264
sanitizeHtml,
@@ -244,4 +276,6 @@ module.exports = {
244276
projectAccess,
245277
isIntersectionBy,
246278
removeKeys,
279+
cleanSampleInfo,
280+
cleanSeqQC,
247281
};

app/middleware/report.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ module.exports = async (req, res, next, ident) => {
3838
model: db.models.signatures.scope('public'),
3939
as: 'signatures',
4040
},
41+
{
42+
model: db.models.seqQC.scope('public'),
43+
as: 'seqQC',
44+
},
4145
];
4246

4347
let result;

app/models/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ patientInformation.belongsTo(analysisReports, {
113113
as: 'report', foreignKey: 'reportId', onDelete: 'CASCADE', onUpdate: 'CASCADE', constraints: true,
114114
});
115115

116+
// seqqc
117+
const seqQC = require('./reports/seqqc')(sequelize, Sq);
118+
119+
seqQC.belongsTo(analysisReports, {
120+
as: 'report', foreignKey: 'reportId', targetKey: 'id', onDelete: 'CASCADE', constraints: true,
121+
});
122+
analysisReports.hasMany(seqQC, {
123+
as: 'seqQC', foreignKey: 'reportId', onDelete: 'CASCADE', constraints: true,
124+
});
125+
116126
// Summary
117127
const summary = {};
118128
summary.variantCounts = require('./reports/genomic/summary/variantCounts')(sequelize, Sq);

app/models/reports/genomic/summary/genomicAlterationsIdentified.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module.exports = (sequelize, Sq) => {
3737
scopes: {
3838
public: {
3939
attributes: {
40-
exclude: ['id', 'reportId', 'deletedAt', 'updatedBy'],
40+
exclude: ['id', 'reportId', 'variantId', 'deletedAt', 'updatedBy'],
4141
},
4242
},
4343
},

app/models/reports/report.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ module.exports = (sequelize, Sq) => {
7676
},
7777
allowNull: false,
7878
},
79-
seqQC: {
79+
seqQc: {
80+
name: 'seqQc',
81+
field: 'seq_qc',
8082
type: Sq.JSONB,
8183
jsonSchema: {
8284
schema: {

app/models/reports/seqqc.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
const {DEFAULT_COLUMNS, DEFAULT_REPORT_OPTIONS} = require('../base');
2+
3+
module.exports = (sequelize, Sq) => {
4+
const seqQC = sequelize.define('seqQC', {
5+
...DEFAULT_COLUMNS,
6+
reportId: {
7+
name: 'reportId',
8+
field: 'report_id',
9+
type: Sq.INTEGER,
10+
references: {
11+
model: 'reports',
12+
key: 'id',
13+
},
14+
},
15+
reads: {
16+
name: 'Reads',
17+
field: 'reads',
18+
type: Sq.TEXT,
19+
},
20+
bioQC: {
21+
name: 'bioQC',
22+
field: 'bio_qc',
23+
type: Sq.TEXT,
24+
},
25+
labQC: {
26+
name: 'labQC',
27+
field: 'lab_qc',
28+
type: Sq.TEXT,
29+
},
30+
sample: {
31+
name: 'Sample',
32+
field: 'sample',
33+
type: Sq.TEXT,
34+
},
35+
library: {
36+
name: 'Library',
37+
field: 'library',
38+
type: Sq.TEXT,
39+
},
40+
coverage: {
41+
name: 'Coverage',
42+
field: 'coverage',
43+
type: Sq.TEXT,
44+
},
45+
inputNg: {
46+
name: 'InputNg',
47+
field: 'input_ng',
48+
type: Sq.TEXT,
49+
},
50+
inputUg: {
51+
name: 'InputUg',
52+
field: 'input_ug',
53+
type: Sq.TEXT,
54+
},
55+
protocol: {
56+
name: 'Protocol',
57+
field: 'protocol',
58+
type: Sq.TEXT,
59+
},
60+
sampleName: {
61+
name: 'SampleName',
62+
field: 'sample_name',
63+
type: Sq.TEXT,
64+
},
65+
duplicateReadsPerc: {
66+
name: 'DuplicateReadsPerc',
67+
field: 'duplicate_reads_perc',
68+
type: Sq.TEXT,
69+
},
70+
}, {
71+
...DEFAULT_REPORT_OPTIONS,
72+
tableName: 'reports_seqqc',
73+
scopes: {
74+
public: {
75+
attributes: {exclude: ['id', 'reportId', 'deletedAt', 'updatedBy']},
76+
},
77+
},
78+
});
79+
80+
// set instance methods
81+
seqQC.prototype.view = function (scope) {
82+
if (scope === 'public') {
83+
const {
84+
id, reportId, deletedAt, updatedBy, ...publicView
85+
} = this.dataValues;
86+
return publicView;
87+
}
88+
return this;
89+
};
90+
91+
return seqQC;
92+
};

app/routes/report/appendices.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ router.route('/')
1313
try {
1414
const result = await db.models.report.findOne({
1515
where: {ident: req.report.ident},
16-
attributes: ['seqQC', 'config'],
16+
attributes: ['seqQc', 'config'],
17+
include: ['seqQC'],
1718
});
1819
return res.json(result);
1920
} catch (error) {

app/routes/report/report.js

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const {parseReportSortQuery} = require('../../libs/queryOperations');
99
const db = require('../../models');
1010
const logger = require('../../log');
1111
const {getUserProjects} = require('../../libs/helperFunctions');
12-
const {removeKeys} = require('../../libs/helperFunctions');
12+
const {removeKeys, cleanSampleInfo, cleanSeqQC} = require('../../libs/helperFunctions');
1313

1414
const {hasAccessToNonProdReports,
1515
hasAccessToUnreviewedReports, isAdmin} = require('../../libs/helperFunctions');
@@ -54,22 +54,11 @@ router.route('/schema')
5454
})
5555
.post(async (req, res) => {
5656
if (req.body.sampleInfo) {
57-
// Clean sampleInfo input
58-
const cleanSampleInfo = [];
59-
for (const sampleInfoObject of req.body.sampleInfo) {
60-
cleanSampleInfo.push(
61-
{
62-
sample: (sampleInfoObject.Sample) ? sampleInfoObject.Sample : sampleInfoObject.sample,
63-
pathoTc: (sampleInfoObject['Patho TC']) ? sampleInfoObject['Patho TC'] : sampleInfoObject.pathoTc,
64-
biopsySite: (sampleInfoObject['Biopsy Site']) ? sampleInfoObject['Biopsy Site'] : sampleInfoObject.biopsySite,
65-
biopsyType: (sampleInfoObject['Biopsy Type']) ? sampleInfoObject['Biopsy Type'] : sampleInfoObject.biopsyType,
66-
sampleName: (sampleInfoObject['Sample Name']) ? sampleInfoObject['Sample Name'] : sampleInfoObject.sampleName,
67-
primarySite: (sampleInfoObject['Primary Site']) ? sampleInfoObject['Primary Site'] : sampleInfoObject.primarySite,
68-
collectionDate: (sampleInfoObject['Collection Date']) ? sampleInfoObject['Collection Date'] : sampleInfoObject.collectionDate,
69-
},
70-
);
71-
}
72-
req.body.sampleInfo = cleanSampleInfo;
57+
req.body.sampleInfo = cleanSampleInfo(req.body.sampleInfo);
58+
}
59+
60+
if (req.body.seqQC) {
61+
req.body.seqQC = cleanSeqQC(req.body.seqQC);
7362
}
7463

7564
try {
@@ -380,29 +369,16 @@ router.route('/')
380369
}
381370

382371
if (req.body.sampleInfo) {
383-
// Clean sampleInfo input
384-
const cleanSampleInfo = [];
385-
for (const sampleInfoObject of req.body.sampleInfo) {
386-
cleanSampleInfo.push(
387-
{
388-
sample: (sampleInfoObject.Sample) ? sampleInfoObject.Sample : sampleInfoObject.sample,
389-
pathoTc: (sampleInfoObject['Patho TC']) ? sampleInfoObject['Patho TC'] : sampleInfoObject.pathoTc,
390-
biopsySite: (sampleInfoObject['Biopsy Site']) ? sampleInfoObject['Biopsy Site'] : sampleInfoObject.biopsySite,
391-
biopsyType: (sampleInfoObject['Biopsy Type']) ? sampleInfoObject['Biopsy Type'] : sampleInfoObject.biopsyType,
392-
sampleName: (sampleInfoObject['Sample Name']) ? sampleInfoObject['Sample Name'] : sampleInfoObject.sampleName,
393-
primarySite: (sampleInfoObject['Primary Site']) ? sampleInfoObject['Primary Site'] : sampleInfoObject.primarySite,
394-
collectionDate: (sampleInfoObject['Collection Date']) ? sampleInfoObject['Collection Date'] : sampleInfoObject.collectionDate,
395-
},
396-
);
397-
}
398-
req.body.sampleInfo = cleanSampleInfo;
372+
req.body.sampleInfo = cleanSampleInfo(req.body.sampleInfo);
399373
}
400374

401375
if (req.body.seqQC) {
376+
req.body.seqQC = cleanSeqQC(req.body.seqQC);
377+
402378
req.body.dataType = req.body.seqQC.filter(
403-
(item) => {return item.Sample?.startsWith('Tumour');},
379+
(item) => {return item.sample?.startsWith('Tumour');},
404380
).map(
405-
(item) => {return item.Sample.replace(/^Tumour\s+/i, '');},
381+
(item) => {return item.sample.replace(/^Tumour\s+/i, '');},
406382
).join(', ');
407383
}
408384

app/routes/report/reportAsync.js

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const logger = require('../../log');
1010
const router = express.Router({mergeParams: true});
1111

1212
const validateAgainstSchema = require('../../libs/validateAgainstSchema');
13-
const {getUserProjects} = require('../../libs/helperFunctions');
13+
const {getUserProjects, cleanSampleInfo, cleanSeqQC} = require('../../libs/helperFunctions');
1414

1515
// Generate schema's
1616
const reportUploadSchema = require('../../schemas/report/reportUpload')(true);
@@ -41,29 +41,16 @@ router.route('/')
4141
return res.status(HTTP_STATUS.FORBIDDEN).json({error: `User does not have access to project ${req.body.project}`});
4242
}
4343
if (req.body.sampleInfo) {
44-
// Clean sampleInfo input
45-
const cleanSampleInfo = [];
46-
for (const sampleInfoObject of req.body.sampleInfo) {
47-
cleanSampleInfo.push(
48-
{
49-
sample: (sampleInfoObject.Sample) ? sampleInfoObject.Sample : sampleInfoObject.sample,
50-
pathoTc: (sampleInfoObject['Patho TC']) ? sampleInfoObject['Patho TC'] : sampleInfoObject.pathoTc,
51-
biopsySite: (sampleInfoObject['Biopsy Site']) ? sampleInfoObject['Biopsy Site'] : sampleInfoObject.biopsySite,
52-
biopsyType: (sampleInfoObject['Biopsy Type']) ? sampleInfoObject['Biopsy Type'] : sampleInfoObject.biopsyType,
53-
sampleName: (sampleInfoObject['Sample Name']) ? sampleInfoObject['Sample Name'] : sampleInfoObject.sampleName,
54-
primarySite: (sampleInfoObject['Primary Site']) ? sampleInfoObject['Primary Site'] : sampleInfoObject.primarySite,
55-
collectionDate: (sampleInfoObject['Collection Date']) ? sampleInfoObject['Collection Date'] : sampleInfoObject.collectionDate,
56-
},
57-
);
58-
}
59-
req.body.sampleInfo = cleanSampleInfo;
44+
req.body.sampleInfo = cleanSampleInfo(req.body.sampleInfo);
6045
}
6146

6247
if (req.body.seqQC) {
48+
req.body.seqQC = cleanSeqQC(req.body.seqQC);
49+
6350
req.body.dataType = req.body.seqQC.filter(
64-
(item) => {return item.Sample?.startsWith('Tumour');},
51+
(item) => {return item.sample?.startsWith('Tumour');},
6552
).map(
66-
(item) => {return item.Sample.replace(/^Tumour\s+/i, '');},
53+
(item) => {return item.sample.replace(/^Tumour\s+/i, '');},
6754
).join(', ');
6855
}
6956

app/routes/report/summary/genomicAlterationsIdentified.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ const {REPORT_CREATE_BASE_URI, REPORT_UPDATE_BASE_URI} = require('../../../const
1515

1616
// Generate schemas
1717
const createSchema = schemaGenerator(db.models.genomicAlterationsIdentified, {
18-
baseUri: REPORT_CREATE_BASE_URI,
18+
baseUri: REPORT_CREATE_BASE_URI, additionalProperties: true,
1919
});
2020
const updateSchema = schemaGenerator(db.models.genomicAlterationsIdentified, {
21-
baseUri: REPORT_UPDATE_BASE_URI, nothingRequired: true,
21+
baseUri: REPORT_UPDATE_BASE_URI, nothingRequired: true, additionalProperties: true,
2222
});
2323

2424
// Middleware for genomic alterations
@@ -40,7 +40,15 @@ router.route('/:alteration([A-z0-9-]{36})')
4040
}
4141

4242
try {
43-
await req.alteration.update(req.body, {userId: req.user.id});
43+
let variantId;
44+
if (req.body.variantIdent) {
45+
const modelName = KB_PIVOT_MAPPING[req.body.variantType];
46+
const variant = await db.models[modelName].findOne({
47+
where: {ident: req.body.variantIdent},
48+
});
49+
variantId = variant.id;
50+
}
51+
await req.alteration.update({...req.body, variantId}, {userId: req.user.id});
4452
return res.json(req.alteration.view('public'));
4553
} catch (error) {
4654
logger.error(`Unable to update genomic alterations ${error}`);
@@ -115,8 +123,15 @@ router.route('/')
115123

116124
try {
117125
req.body.reportId = req.report.id;
118-
119-
const result = await db.models.genomicAlterationsIdentified.create(req.body);
126+
let variantId;
127+
if (req.body.variantIdent) {
128+
const modelName = KB_PIVOT_MAPPING[req.body.variantType];
129+
const variant = await db.models[modelName].findOne({
130+
where: {ident: req.body.variantIdent},
131+
});
132+
variantId = variant.id;
133+
}
134+
const result = await db.models.genomicAlterationsIdentified.create({...req.body, variantId});
120135
return res.status(HTTP_STATUS.CREATED).json(result.view('public'));
121136
} catch (error) {
122137
logger.error(`Unable to create genomic alteration entry ${error}`);

0 commit comments

Comments
 (0)