-
Notifications
You must be signed in to change notification settings - Fork 6
Support additional unit types #7243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 18 commits
2fc9a02
fee03af
9b41448
4bd66f9
0f5d249
a46c8ec
e896781
40e2b23
4173429
1e21a46
345e495
d292860
59500c1
4994155
d93154b
59893fd
15de63f
d3560fd
d87cd54
23d06d3
30c5a6d
d98e74a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,33 @@ public enum Unit | |
| count(KindOfQuantity.Count, unit, 1.0, 2, "count", | ||
| Quantity.class, | ||
| "count", "count"), | ||
| pcs(KindOfQuantity.Count, unit, 1.0, 2, "pcs", | ||
| Quantity.class, | ||
| "pcs", "pcs"), | ||
|
||
| pack(KindOfQuantity.Count, unit, 1.0, 2, "pack", | ||
| Quantity.class, | ||
| "pack", "packs"), | ||
| blocks(KindOfQuantity.Count, unit, 1.0, 2, "blocks", | ||
| Quantity.class, | ||
| "block", "blocks"), | ||
| slides(KindOfQuantity.Count, unit, 1.0, 2, "slides", | ||
| Quantity.class, | ||
| "slide", "slides"), | ||
| cells(KindOfQuantity.Count, unit, 1.0, 2, "cells", | ||
| Quantity.class, | ||
| "cell", "cells"), | ||
| box(KindOfQuantity.Count, unit, 1.0, 2, "box", | ||
| Quantity.class, | ||
| "box", "boxes"), | ||
| kit(KindOfQuantity.Count, unit, 1.0, 2, "kit", | ||
| Quantity.class, | ||
| "kit", "kits"), | ||
| tests(KindOfQuantity.Count, unit, 1.0, 2, "tests", | ||
| Quantity.class, | ||
| "test", "tests"), | ||
| bottle(KindOfQuantity.Count, unit, 1.0, 2, "bottle", | ||
| Quantity.class, | ||
| "bottle", "bottles"), | ||
|
|
||
| mL(KindOfQuantity.Volume, null, 1e0, 6, "mL", | ||
| Quantity.Volume_ml.class, | ||
|
|
@@ -51,27 +78,27 @@ public enum Unit | |
| "picoliter", "picoliters", | ||
| "pl", "picolitre", "picolitres"), | ||
|
|
||
| g(KindOfQuantity.Mass, null, 1e0, 9, "g", | ||
| g(KindOfQuantity.Mass, null, 1e0, 12, "g", | ||
| Quantity.Mass_g.class, | ||
| "gram", "grams"), | ||
| Mg(KindOfQuantity.Mass, g, 1e6, 12, "Mg", | ||
| Mg(KindOfQuantity.Mass, g, 1e6, 15, "Mg", | ||
| Quantity.Mass_Megag.class, | ||
| "megagram", "megagrams", | ||
| "tonne", "tonnes"), | ||
| kg(KindOfQuantity.Mass, g, 1e3, 12, "kg", | ||
| kg(KindOfQuantity.Mass, g, 1e3, 15, "kg", | ||
| Quantity.Mass_kg.class, | ||
| "kilogram", "kilograms"), | ||
| mg(KindOfQuantity.Mass, g, 1e-3, 6, "mg", | ||
| mg(KindOfQuantity.Mass, g, 1e-3, 9, "mg", | ||
| Quantity.Mass_mg.class, | ||
| "milligram", "milligrams"), | ||
| ug(KindOfQuantity.Mass, g, 1e-6, 3, "ug", | ||
| ug(KindOfQuantity.Mass, g, 1e-6, 6, "ug", | ||
| Quantity.Mass_ug.class, | ||
| "microgram", "micrograms", | ||
| "μg"), | ||
| ng(KindOfQuantity.Mass, g, 1e-9, 3, "ng", | ||
| Quantity.Mass_ng.class, | ||
| "nanogram", "nanograms"), | ||
| pg(KindOfQuantity.Mass, g, 1e-12, 3, "pg", | ||
| pg(KindOfQuantity.Mass, g, 1e-12, 0, "pg", | ||
| Quantity.Mass_pg.class, | ||
| "picogram", "picograms"); | ||
|
|
||
|
|
@@ -206,6 +233,7 @@ public void testIsBase() | |
| assertFalse(Unit.kg.isBase()); | ||
| assertTrue(Unit.unit.isBase()); | ||
| assertFalse(Unit.count.isBase()); | ||
| assertFalse(Unit.bottle.isBase()); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -217,6 +245,12 @@ public void testIsCompatible() | |
| assertTrue(Unit.g.isCompatible(Unit.mg)); | ||
| assertFalse(Unit.g.isCompatible(Unit.mL)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.count)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.pcs)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.pack)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.bottle)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.blocks)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.box)); | ||
| assertTrue(Unit.unit.isCompatible(Unit.slides)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some missing here. Not that I think there's risk or regression here, just for completeness. |
||
| assertFalse(Unit.unit.isCompatible(Unit.mL)); | ||
| assertFalse(Unit.mL.isCompatible(null)); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -974,6 +974,12 @@ describe('Amount/Unit CRUD', () => { | |
| expect(errorMsg.text).toContain(NO_AMOUNT_ERROR); | ||
| errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tL", dataType, "INSERT", topFolderOptions, editorUserOptions); | ||
| expect(errorMsg.text).toContain(INCOMPATIBLE_ERROR); | ||
| errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tunit", dataType, "INSERT", topFolderOptions, editorUserOptions); | ||
| expect(errorMsg.text).toContain('Units value (unit) is not compatible with the ' + dataType + ' display units (g).'); | ||
| errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tcells", dataType, "INSERT", topFolderOptions, editorUserOptions); | ||
| expect(errorMsg.text).toContain('Units value (cells) is not compatible with the ' + dataType + ' display units (g).'); | ||
| errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tbogus", dataType, "INSERT", topFolderOptions, editorUserOptions); | ||
| expect(errorMsg.text).toContain('Unsupported Units value (bogus). Supported values are: kg, g, mg, ug, ng.'); | ||
| errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t-1.1\tkg", dataType, "INSERT", topFolderOptions, editorUserOptions); | ||
| expect(errorMsg.text).toContain(NEGATIVE_ERROR); | ||
| errorMsg = await ExperimentCRUDUtils.importCrossTypeData(server, "Name\tStoredAmount\tUnits\tSampleType\nData1\t-1.1\tkg\t" + dataType ,'IMPORT', topFolderOptions, adminOptions, true); | ||
|
|
@@ -1085,5 +1091,235 @@ describe('Amount/Unit CRUD', () => { | |
|
|
||
| }); | ||
|
|
||
| it ("Test units conversion on insert/update", async () => { | ||
| const sampleTypeMass = 'SampleTypeWithMassUnits'; | ||
| const sampleTypeVolume = 'SampleTypeWithVolumeUnits'; | ||
| const sampleTypeCount = 'SampleTypeWithCountUnits'; | ||
|
|
||
| const sampleTypeUnits = { | ||
| [sampleTypeMass]: 'ug', | ||
| [sampleTypeVolume]: 'L', | ||
| [sampleTypeCount]: 'unit' | ||
| }; | ||
|
|
||
| for (const [dataType, unit] of Object.entries(sampleTypeUnits)) { | ||
| const createPayload = { | ||
| kind: 'SampleSet', | ||
| domainDesign: { name: dataType, fields: [{ name: 'Name' }] }, | ||
| options: { | ||
| name: dataType, | ||
| metricUnit: unit | ||
| } | ||
| }; | ||
| await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse); | ||
| } | ||
|
|
||
| let sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, [ | ||
| {name: 'S-ng', amount: 4.56, units: 'ng'}, | ||
| {name: 'S-ug', amount: 4.56, units: 'ug'}, | ||
| {name: 'S-mg', amount: 4.56, units: 'mg'}, | ||
| {name: 'S-g', amount: 4.56, units: 'g'}, | ||
| {name: 'S-kg', amount: 4.56, units: 'kg'}, | ||
| ], 'samples', sampleTypeMass, topFolderOptions, editorUserOptions); | ||
|
|
||
| // check for storedamount in g | ||
|
||
| let expectedRawAmounts : {} = { | ||
| 'S-ng': 4.56e-9, | ||
| 'S-ug': 4.56e-6, | ||
| 'S-mg': 0.00456, | ||
| 'S-g': 4.56, | ||
| 'S-kg': 4560, | ||
| }; | ||
| let expectedStoredAmounts : {} = { | ||
| 'S-ng': 4.56e-3, | ||
| 'S-ug': 4.56, | ||
| 'S-mg': 4560, | ||
| 'S-g': 4.56e6, | ||
| 'S-kg': 4.56e9, | ||
| }; | ||
|
|
||
| for (const sampleRow of sampleRowsWithUnits) { | ||
| const sampleName = caseInsensitive(sampleRow, 'name'); | ||
| let sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeMass, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('g'); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual('ug'); | ||
| await server.post('query', 'updateRows', { | ||
| schemaName: 'samples', | ||
| queryName: sampleTypeMass, | ||
| rows: [{ | ||
| amount: 6.54, | ||
| units: sampleName.substring(2), | ||
| rowId: caseInsensitive(sampleRow, 'rowId') | ||
| }] | ||
| }, { ...topFolderOptions, ...editorUserOptions }).expect(successfulResponse); | ||
| } | ||
|
|
||
| expectedRawAmounts = { | ||
| 'S-ng': 6.54e-9, | ||
| 'S-ug': 6.54e-6, | ||
| 'S-mg': 0.00654, | ||
| 'S-g': 6.54, | ||
| 'S-kg': 6540, | ||
| }; | ||
| expectedStoredAmounts = { | ||
| 'S-ng': 6.54e-3, | ||
| 'S-ug': 6.54, | ||
| 'S-mg': 6540, | ||
| 'S-g': 6.54e6, | ||
| 'S-kg': 6.54e9, | ||
| }; | ||
| for (const sampleRow of sampleRowsWithUnits) { | ||
| const sampleName = caseInsensitive(sampleRow, 'name'); | ||
| let sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeMass, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('g'); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual('ug'); | ||
| } | ||
|
|
||
| sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, [ | ||
| {name: 'S-L', amount: 4.56, units: 'L'}, | ||
| {name: 'S-mL', amount: 4.56, units: 'mL'}, | ||
| {name: 'S-uL', amount: 4.56, units: 'uL'}, | ||
| ], 'samples', sampleTypeVolume, topFolderOptions, editorUserOptions); | ||
|
|
||
| // check for storedamount in mL | ||
| expectedRawAmounts = { | ||
| 'S-L': 4560, | ||
| 'S-mL': 4.56, | ||
| 'S-uL': 0.00456, | ||
| }; | ||
| // stored amount is in L | ||
| expectedStoredAmounts = { | ||
| 'S-L': 4.56, | ||
| 'S-mL': 0.00456, | ||
| 'S-uL': 4.56e-6, | ||
| } | ||
| for (const sampleRow of sampleRowsWithUnits) { | ||
| const sampleName = caseInsensitive(sampleRow, 'name'); | ||
| const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeVolume, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('mL'); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual('L'); | ||
| } | ||
|
|
||
| const countRows = [ | ||
| {name: 'S-unit', amount: 4.56, units: 'unit'}, | ||
| {name: 'S-pcs', amount: 4.56, units: 'pcs'}, | ||
| {name: 'S-kit', amount: 4.56, units: 'kit'}, | ||
| {name: 'S-cells', amount: 4.56, units: 'cells'} | ||
| ] | ||
| sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, countRows, 'samples', sampleTypeCount, topFolderOptions, editorUserOptions); | ||
|
|
||
| for (const sampleRow of sampleRowsWithUnits) { | ||
| const sampleName = caseInsensitive(sampleRow, 'name'); | ||
| const usedUnit = sampleName.substring(2); | ||
| const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeCount, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(4.56); | ||
| expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(4.56); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(usedUnit); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual(usedUnit); | ||
|
|
||
| await server.post('query', 'updateRows', { | ||
| schemaName: 'samples', | ||
| queryName: sampleTypeCount, | ||
| rows: [{ | ||
| amount: 6.54, | ||
| units: usedUnit, | ||
| rowId: caseInsensitive(sampleRow, 'rowId') | ||
| }] | ||
| }, { ...topFolderOptions, ...editorUserOptions }).expect(successfulResponse); | ||
| } | ||
|
|
||
| for (const sampleRow of sampleRowsWithUnits) { | ||
| const sampleName = caseInsensitive(sampleRow, 'name'); | ||
| const usedUnit = sampleName.substring(2); | ||
| const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeCount, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(6.54); | ||
| expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(6.54); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(usedUnit); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual(usedUnit); | ||
| } | ||
|
|
||
| }) | ||
|
|
||
| async function verifyCountTypeAliquotRollup(sampleTypeName: string, hasSampleTypeDisplayUnit: boolean) { | ||
| const dataRows = [ | ||
| {name: 'S-no-amount'}, | ||
| {AliquotedFrom: 'S-no-amount', name: 'S-no-pcs1', amount: 2, units: 'pcs'}, | ||
| {AliquotedFrom: 'S-no-amount', name: 'S-no-pcs2', amount: 2, units: 'pcs'}, | ||
| {name: 'S-unit', amount: 1, units: 'unit'}, | ||
| {AliquotedFrom: 'S-unit', name: 'S-unit-unit1', amount: 2, units: 'unit'}, | ||
| {AliquotedFrom: 'S-unit', name: 'S-unit-unit2', amount: 2, units: 'unit'}, | ||
| {name: 'S-pcs', amount: 1, units: 'pcs'}, | ||
| {AliquotedFrom: 'S-pcs', name: 'S-pcs-pcs1', amount: 2, units: 'pcs'}, | ||
| {AliquotedFrom: 'S-pcs', name: 'S-pcs-pcs2', amount: 2, units: 'pcs'}, | ||
| {name: 'S-kit', amount: 1, units: 'kit'}, | ||
| {AliquotedFrom: 'S-kit', name: 'S-kit-pcs1', amount: 2, units: 'pcs'}, | ||
| {AliquotedFrom: 'S-kit', name: 'S-kit-pcs2', amount: 2, units: 'pcs'}, | ||
| {name: 'S-cells', amount: 1, units: 'cells'}, | ||
| {AliquotedFrom: 'S-cells', name: 'S-cells-pcs1', amount: 2, units: 'pcs'}, | ||
| {AliquotedFrom: 'S-cells', name: 'S-cells-cells2', amount: 2, units: 'cells'}, | ||
| ] | ||
|
|
||
| const insertedResults = await ExperimentCRUDUtils.insertRows(server, dataRows, 'samples', sampleTypeName, topFolderOptions, editorUserOptions); | ||
| const insertedMap = {}; | ||
| for (const row of insertedResults) { | ||
| insertedMap[caseInsensitive(row, 'name')] = row; | ||
| } | ||
|
|
||
| let expectedAliquotUnit = { | ||
| 'S-no-amount': 'pcs', | ||
| 'S-unit': 'unit', | ||
| 'S-pcs': 'pcs', | ||
| 'S-kit': 'pcs', | ||
| 'S-cells': hasSampleTypeDisplayUnit ? 'unit' : 'cells', | ||
| }; | ||
|
|
||
| // for each expectedRollupAmounts | ||
| for (const [sampleName, expectedAliquotUnitValue] of Object.entries(expectedAliquotUnit)) { | ||
| let parentUnit = sampleName.substring(2); | ||
| if (parentUnit === 'no-amount') { | ||
| parentUnit = null; | ||
| } | ||
| const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeName, 'Units,RawUnits,AliquotVolume,AliquotCount,AliquotUnit', topFolderOptions, readerUserOptions); | ||
| expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(parentUnit); | ||
| expect(caseInsensitive(sampleData, 'Units')).toEqual(parentUnit); | ||
| expect(caseInsensitive(sampleData, 'AliquotVolume')).toEqual(4); | ||
| expect(caseInsensitive(sampleData, 'AliquotCount')).toEqual(2); | ||
| expect(caseInsensitive(sampleData, 'AliquotUnit')).toEqual(expectedAliquotUnitValue); | ||
|
|
||
| } | ||
| } | ||
|
|
||
| it ("Test aliquot rollup for count display unit", async () => { | ||
| let dataType = 'SampleTypeAliquotWithCountUnit'; | ||
| let createPayload : {} = { | ||
| kind: 'SampleSet', | ||
| domainDesign: { name: dataType, fields: [{ name: 'Name' }] }, | ||
| options: { | ||
| name: dataType, | ||
| metricUnit: 'unit' | ||
| } | ||
| }; | ||
| await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse); | ||
| await verifyCountTypeAliquotRollup(dataType, true); | ||
|
|
||
| dataType = 'SampleTypeAliquoNoDisplayUnit'; | ||
| createPayload = { | ||
| kind: 'SampleSet', | ||
| domainDesign: { name: dataType, fields: [{ name: 'Name' }] }, | ||
| options: { | ||
| name: dataType, | ||
| } | ||
| }; | ||
| await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse); | ||
| await verifyCountTypeAliquotRollup(dataType, false); | ||
|
|
||
| }) | ||
|
|
||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should set the labels for these fields here.