Skip to content

Commit e806b3b

Browse files
authored
Support additional unit types (#7243)
1 parent 875ff79 commit e806b3b

File tree

9 files changed

+622
-92
lines changed

9 files changed

+622
-92
lines changed

api/src/org/labkey/api/exp/query/ExpMaterialTable.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ enum Column
5252
Properties,
5353
Property,
5454
QueryableInputs,
55+
RawAliquotUnit(false, "Raw Aliquot Unit"),
56+
RawAliquotVolume(false, "Raw Aliquot Total Amount"),
57+
RawAvailableAliquotVolume(false, "Raw Available Aliquot Amount"),
5558
RawAmount(true),
5659
RawUnits,
5760
RootMaterialRowId,

api/src/org/labkey/api/ontology/KindOfQuantity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public List<Unit> getCommonUnits()
2929
@Override
3030
public List<Unit> getCommonUnits()
3131
{
32-
return List.of(Unit.kg, Unit.g, Unit.mg);
32+
return List.of(Unit.kg, Unit.g, Unit.mg, Unit.ug, Unit.ng);
3333
}
3434
},
3535

@@ -39,7 +39,7 @@ public List<Unit> getCommonUnits()
3939
@Override
4040
public List<Unit> getCommonUnits()
4141
{
42-
return List.of(Unit.unit);
42+
return List.of(Unit.blocks, Unit.bottles, Unit.boxes, Unit.cells, Unit.kits, Unit.packs, Unit.pieces, Unit.slides, Unit.tests, Unit.unit);
4343
}
4444
};
4545

api/src/org/labkey/api/ontology/Quantity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ public void testParse()
465465
assertEquals(Quantity.of(0, Unit.count), parse("0 units"));
466466
assertEquals(Quantity.of(0, Unit.count), parse("0count"));
467467

468+
assertEquals(Quantity.of(1, Unit.count), parse("1", Unit.boxes));
469+
assertEquals(Quantity.of(1, Unit.unit), parse("1", Unit.blocks));
470+
assertEquals(Quantity.of(1, Unit.cells), parse("1", Unit.tests));
471+
468472
assertEquals(parse("1000mg", Unit.g), parse("0.001kg", Unit.g));
469473
assertEquals(parse(" 1000mg", Unit.g), parse("0.001kg", Unit.g));
470474
assertEquals(parse("1000mg ", Unit.g), parse("0.001kg", Unit.g));

api/src/org/labkey/api/ontology/Unit.java

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,33 @@ public enum Unit
1919
count(KindOfQuantity.Count, unit, 1.0, 2, "count",
2020
Quantity.class,
2121
"count", "count"),
22+
pieces(KindOfQuantity.Count, unit, 1.0, 2, "pieces",
23+
Quantity.class,
24+
"piece", "pieces"),
25+
packs(KindOfQuantity.Count, unit, 1.0, 2, "packs",
26+
Quantity.class,
27+
"pack", "packs"),
28+
blocks(KindOfQuantity.Count, unit, 1.0, 2, "blocks",
29+
Quantity.class,
30+
"block", "blocks"),
31+
slides(KindOfQuantity.Count, unit, 1.0, 2, "slides",
32+
Quantity.class,
33+
"slide", "slides"),
34+
cells(KindOfQuantity.Count, unit, 1.0, 2, "cells",
35+
Quantity.class,
36+
"cell", "cells"),
37+
boxes(KindOfQuantity.Count, unit, 1.0, 2, "boxes",
38+
Quantity.class,
39+
"box", "boxes"),
40+
kits(KindOfQuantity.Count, unit, 1.0, 2, "kits",
41+
Quantity.class,
42+
"kit", "kits"),
43+
tests(KindOfQuantity.Count, unit, 1.0, 2, "tests",
44+
Quantity.class,
45+
"test", "tests"),
46+
bottles(KindOfQuantity.Count, unit, 1.0, 2, "bottles",
47+
Quantity.class,
48+
"bottle", "bottles"),
2249

2350
mL(KindOfQuantity.Volume, null, 1e0, 6, "mL",
2451
Quantity.Volume_ml.class,
@@ -51,27 +78,27 @@ public enum Unit
5178
"picoliter", "picoliters",
5279
"pl", "picolitre", "picolitres"),
5380

54-
g(KindOfQuantity.Mass, null, 1e0, 9, "g",
81+
g(KindOfQuantity.Mass, null, 1e0, 12, "g",
5582
Quantity.Mass_g.class,
5683
"gram", "grams"),
57-
Mg(KindOfQuantity.Mass, g, 1e6, 12, "Mg",
84+
Mg(KindOfQuantity.Mass, g, 1e6, 15, "Mg",
5885
Quantity.Mass_Megag.class,
5986
"megagram", "megagrams",
6087
"tonne", "tonnes"),
61-
kg(KindOfQuantity.Mass, g, 1e3, 12, "kg",
88+
kg(KindOfQuantity.Mass, g, 1e3, 15, "kg",
6289
Quantity.Mass_kg.class,
6390
"kilogram", "kilograms"),
64-
mg(KindOfQuantity.Mass, g, 1e-3, 6, "mg",
91+
mg(KindOfQuantity.Mass, g, 1e-3, 9, "mg",
6592
Quantity.Mass_mg.class,
6693
"milligram", "milligrams"),
67-
ug(KindOfQuantity.Mass, g, 1e-6, 3, "ug",
94+
ug(KindOfQuantity.Mass, g, 1e-6, 6, "ug",
6895
Quantity.Mass_ug.class,
6996
"microgram", "micrograms",
7097
"μg"),
7198
ng(KindOfQuantity.Mass, g, 1e-9, 3, "ng",
7299
Quantity.Mass_ng.class,
73100
"nanogram", "nanograms"),
74-
pg(KindOfQuantity.Mass, g, 1e-12, 3, "pg",
101+
pg(KindOfQuantity.Mass, g, 1e-12, 0, "pg",
75102
Quantity.Mass_pg.class,
76103
"picogram", "picograms");
77104

@@ -206,6 +233,7 @@ public void testIsBase()
206233
assertFalse(Unit.kg.isBase());
207234
assertTrue(Unit.unit.isBase());
208235
assertFalse(Unit.count.isBase());
236+
assertFalse(Unit.bottles.isBase());
209237
}
210238

211239
@Test
@@ -217,7 +245,16 @@ public void testIsCompatible()
217245
assertTrue(Unit.g.isCompatible(Unit.mg));
218246
assertFalse(Unit.g.isCompatible(Unit.mL));
219247
assertTrue(Unit.unit.isCompatible(Unit.count));
248+
assertTrue(Unit.unit.isCompatible(Unit.pieces));
249+
assertTrue(Unit.unit.isCompatible(Unit.packs));
250+
assertTrue(Unit.unit.isCompatible(Unit.bottles));
251+
assertTrue(Unit.unit.isCompatible(Unit.blocks));
252+
assertTrue(Unit.unit.isCompatible(Unit.boxes));
253+
assertTrue(Unit.unit.isCompatible(Unit.slides));
254+
assertTrue(Unit.cells.isCompatible(Unit.slides));
255+
assertTrue(Unit.cells.isCompatible(Unit.unit));
220256
assertFalse(Unit.unit.isCompatible(Unit.mL));
257+
assertFalse(Unit.bottles.isCompatible(Unit.mL));
221258
assertFalse(Unit.mL.isCompatible(null));
222259
}
223260

experiment/src/client/test/integration/SampleTypeCrud.ispec.ts

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,12 @@ describe('Amount/Unit CRUD', () => {
974974
expect(errorMsg.text).toContain(NO_AMOUNT_ERROR);
975975
errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tL", dataType, "INSERT", topFolderOptions, editorUserOptions);
976976
expect(errorMsg.text).toContain(INCOMPATIBLE_ERROR);
977+
errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tunit", dataType, "INSERT", topFolderOptions, editorUserOptions);
978+
expect(errorMsg.text).toContain('Units value (unit) is not compatible with the ' + dataType + ' display units (g).');
979+
errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tcells", dataType, "INSERT", topFolderOptions, editorUserOptions);
980+
expect(errorMsg.text).toContain('Units value (cells) is not compatible with the ' + dataType + ' display units (g).');
981+
errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t1.1\tbogus", dataType, "INSERT", topFolderOptions, editorUserOptions);
982+
expect(errorMsg.text).toContain('Unsupported Units value (bogus). Supported values are: kg, g, mg, ug, ng.');
977983
errorMsg = await ExperimentCRUDUtils.importSample(server, "Name\tStoredAmount\tUnits\nData1\t-1.1\tkg", dataType, "INSERT", topFolderOptions, editorUserOptions);
978984
expect(errorMsg.text).toContain(NEGATIVE_ERROR);
979985
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', () => {
10851091

10861092
});
10871093

1094+
it ("Test units conversion on insert/update", async () => {
1095+
const sampleTypeMass = 'SampleTypeWithMassUnits';
1096+
const sampleTypeVolume = 'SampleTypeWithVolumeUnits';
1097+
const sampleTypeCount = 'SampleTypeWithCountUnits';
1098+
1099+
const sampleTypeUnits = {
1100+
[sampleTypeMass]: 'ug',
1101+
[sampleTypeVolume]: 'L',
1102+
[sampleTypeCount]: 'unit'
1103+
};
1104+
1105+
for (const [dataType, unit] of Object.entries(sampleTypeUnits)) {
1106+
const createPayload = {
1107+
kind: 'SampleSet',
1108+
domainDesign: { name: dataType, fields: [{ name: 'Name' }] },
1109+
options: {
1110+
name: dataType,
1111+
metricUnit: unit
1112+
}
1113+
};
1114+
await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse);
1115+
}
1116+
1117+
let sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, [
1118+
{name: 'S-ng', amount: 4.56, units: 'ng'},
1119+
{name: 'S-ug', amount: 4.56, units: 'ug'},
1120+
{name: 'S-mg', amount: 4.56, units: 'mg'},
1121+
{name: 'S-g', amount: 4.56, units: 'g'},
1122+
{name: 'S-kg', amount: 4.56, units: 'kg'},
1123+
], 'samples', sampleTypeMass, topFolderOptions, editorUserOptions);
1124+
1125+
// check for raw amount in g and display amount in ug
1126+
let expectedRawAmounts : {} = {
1127+
'S-ng': 4.56e-9,
1128+
'S-ug': 4.56e-6,
1129+
'S-mg': 0.00456,
1130+
'S-g': 4.56,
1131+
'S-kg': 4560,
1132+
};
1133+
let expectedStoredAmounts : {} = {
1134+
'S-ng': 4.56e-3,
1135+
'S-ug': 4.56,
1136+
'S-mg': 4560,
1137+
'S-g': 4.56e6,
1138+
'S-kg': 4.56e9,
1139+
};
1140+
1141+
for (const sampleRow of sampleRowsWithUnits) {
1142+
const sampleName = caseInsensitive(sampleRow, 'name');
1143+
let sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeMass, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions);
1144+
expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]);
1145+
expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]);
1146+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('g');
1147+
expect(caseInsensitive(sampleData, 'Units')).toEqual('ug');
1148+
await server.post('query', 'updateRows', {
1149+
schemaName: 'samples',
1150+
queryName: sampleTypeMass,
1151+
rows: [{
1152+
amount: 6.54,
1153+
units: sampleName.substring(2),
1154+
rowId: caseInsensitive(sampleRow, 'rowId')
1155+
}]
1156+
}, { ...topFolderOptions, ...editorUserOptions }).expect(successfulResponse);
1157+
}
1158+
1159+
expectedRawAmounts = {
1160+
'S-ng': 6.54e-9,
1161+
'S-ug': 6.54e-6,
1162+
'S-mg': 0.00654,
1163+
'S-g': 6.54,
1164+
'S-kg': 6540,
1165+
};
1166+
expectedStoredAmounts = {
1167+
'S-ng': 6.54e-3,
1168+
'S-ug': 6.54,
1169+
'S-mg': 6540,
1170+
'S-g': 6.54e6,
1171+
'S-kg': 6.54e9,
1172+
};
1173+
for (const sampleRow of sampleRowsWithUnits) {
1174+
const sampleName = caseInsensitive(sampleRow, 'name');
1175+
let sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeMass, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions);
1176+
expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]);
1177+
expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]);
1178+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('g');
1179+
expect(caseInsensitive(sampleData, 'Units')).toEqual('ug');
1180+
}
1181+
1182+
sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, [
1183+
{name: 'S-L', amount: 4.56, units: 'L'},
1184+
{name: 'S-mL', amount: 4.56, units: 'mL'},
1185+
{name: 'S-uL', amount: 4.56, units: 'uL'},
1186+
], 'samples', sampleTypeVolume, topFolderOptions, editorUserOptions);
1187+
1188+
// check for storedamount in mL
1189+
expectedRawAmounts = {
1190+
'S-L': 4560,
1191+
'S-mL': 4.56,
1192+
'S-uL': 0.00456,
1193+
};
1194+
// stored amount is in L
1195+
expectedStoredAmounts = {
1196+
'S-L': 4.56,
1197+
'S-mL': 0.00456,
1198+
'S-uL': 4.56e-6,
1199+
}
1200+
for (const sampleRow of sampleRowsWithUnits) {
1201+
const sampleName = caseInsensitive(sampleRow, 'name');
1202+
const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeVolume, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions);
1203+
expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(expectedRawAmounts[sampleName]);
1204+
expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(expectedStoredAmounts[sampleName]);
1205+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual('mL');
1206+
expect(caseInsensitive(sampleData, 'Units')).toEqual('L');
1207+
}
1208+
1209+
const countRows = [
1210+
{name: 'S-unit', amount: 4.56, units: 'unit'},
1211+
{name: 'S-pieces', amount: 4.56, units: 'pieces'},
1212+
{name: 'S-kits', amount: 4.56, units: 'kits'},
1213+
{name: 'S-cells', amount: 4.56, units: 'cells'}
1214+
]
1215+
sampleRowsWithUnits = await ExperimentCRUDUtils.insertRows(server, countRows, 'samples', sampleTypeCount, topFolderOptions, editorUserOptions);
1216+
1217+
for (const sampleRow of sampleRowsWithUnits) {
1218+
const sampleName = caseInsensitive(sampleRow, 'name');
1219+
const usedUnit = sampleName.substring(2);
1220+
const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeCount, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions);
1221+
expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(4.56);
1222+
expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(4.56);
1223+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(usedUnit);
1224+
expect(caseInsensitive(sampleData, 'Units')).toEqual(usedUnit);
1225+
1226+
await server.post('query', 'updateRows', {
1227+
schemaName: 'samples',
1228+
queryName: sampleTypeCount,
1229+
rows: [{
1230+
amount: 6.54,
1231+
units: usedUnit,
1232+
rowId: caseInsensitive(sampleRow, 'rowId')
1233+
}]
1234+
}, { ...topFolderOptions, ...editorUserOptions }).expect(successfulResponse);
1235+
}
1236+
1237+
for (const sampleRow of sampleRowsWithUnits) {
1238+
const sampleName = caseInsensitive(sampleRow, 'name');
1239+
const usedUnit = sampleName.substring(2);
1240+
const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeCount, 'StoredAmount,Units,RawAmount,RawUnits', topFolderOptions, readerUserOptions);
1241+
expect(caseInsensitive(sampleData, 'RawAmount')).toBeCloseTo(6.54);
1242+
expect(caseInsensitive(sampleData, 'StoredAmount')).toBeCloseTo(6.54);
1243+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(usedUnit);
1244+
expect(caseInsensitive(sampleData, 'Units')).toEqual(usedUnit);
1245+
}
1246+
1247+
})
1248+
1249+
async function verifyCountTypeAliquotRollup(sampleTypeName: string, hasSampleTypeDisplayUnit: boolean) {
1250+
const dataRows = [
1251+
{name: 'S-no-amount'},
1252+
{AliquotedFrom: 'S-no-amount', name: 'S-no-pcs1', amount: 2, units: 'pieces'},
1253+
{AliquotedFrom: 'S-no-amount', name: 'S-no-pcs2', amount: 2, units: 'pieces'},
1254+
{name: 'S-unit', amount: 1, units: 'unit'},
1255+
{AliquotedFrom: 'S-unit', name: 'S-unit-unit1', amount: 2, units: 'unit'},
1256+
{AliquotedFrom: 'S-unit', name: 'S-unit-unit2', amount: 2, units: 'unit'},
1257+
{name: 'S-pieces', amount: 1, units: 'pieces'},
1258+
{AliquotedFrom: 'S-pieces', name: 'S-pcs-pcs1', amount: 2, units: 'pieces'},
1259+
{AliquotedFrom: 'S-pieces', name: 'S-pcs-pcs2', amount: 2, units: 'pieces'},
1260+
{name: 'S-kits', amount: 1, units: 'kits'},
1261+
{AliquotedFrom: 'S-kits', name: 'S-kit-pcs1', amount: 2, units: 'pieces'},
1262+
{AliquotedFrom: 'S-kits', name: 'S-kit-pcs2', amount: 2, units: 'pieces'},
1263+
{name: 'S-cells', amount: 1, units: 'cells'},
1264+
{AliquotedFrom: 'S-cells', name: 'S-cells-pcs1', amount: 2, units: 'pieces'},
1265+
{AliquotedFrom: 'S-cells', name: 'S-cells-cells2', amount: 2, units: 'cells'},
1266+
]
1267+
1268+
const insertedResults = await ExperimentCRUDUtils.insertRows(server, dataRows, 'samples', sampleTypeName, topFolderOptions, editorUserOptions);
1269+
const insertedMap = {};
1270+
for (const row of insertedResults) {
1271+
insertedMap[caseInsensitive(row, 'name')] = row;
1272+
}
1273+
1274+
let expectedAliquotUnit = {
1275+
'S-no-amount': 'pieces',
1276+
'S-unit': 'unit',
1277+
'S-pieces': 'pieces',
1278+
'S-kits': 'pieces',
1279+
'S-cells': hasSampleTypeDisplayUnit ? 'unit' : 'cells',
1280+
};
1281+
1282+
// for each expectedRollupAmounts
1283+
for (const [sampleName, expectedAliquotUnitValue] of Object.entries(expectedAliquotUnit)) {
1284+
let parentUnit = sampleName.substring(2);
1285+
if (parentUnit === 'no-amount') {
1286+
parentUnit = null;
1287+
}
1288+
const sampleData = await ExperimentCRUDUtils.getSampleDataByName(server, sampleName, sampleTypeName, 'Units,RawUnits,AliquotVolume,AliquotCount,AliquotUnit', topFolderOptions, readerUserOptions);
1289+
expect(caseInsensitive(sampleData, 'RawUnits')).toEqual(parentUnit);
1290+
expect(caseInsensitive(sampleData, 'Units')).toEqual(parentUnit);
1291+
expect(caseInsensitive(sampleData, 'AliquotVolume')).toEqual(4);
1292+
expect(caseInsensitive(sampleData, 'AliquotCount')).toEqual(2);
1293+
expect(caseInsensitive(sampleData, 'AliquotUnit')).toEqual(expectedAliquotUnitValue);
1294+
1295+
}
1296+
}
1297+
1298+
it ("Test aliquot rollup for count display unit", async () => {
1299+
let dataType = 'SampleTypeAliquotWithCountUnit';
1300+
let createPayload : {} = {
1301+
kind: 'SampleSet',
1302+
domainDesign: { name: dataType, fields: [{ name: 'Name' }] },
1303+
options: {
1304+
name: dataType,
1305+
metricUnit: 'unit'
1306+
}
1307+
};
1308+
await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse);
1309+
await verifyCountTypeAliquotRollup(dataType, true);
1310+
1311+
dataType = 'SampleTypeAliquoNoDisplayUnit';
1312+
createPayload = {
1313+
kind: 'SampleSet',
1314+
domainDesign: { name: dataType, fields: [{ name: 'Name' }] },
1315+
options: {
1316+
name: dataType,
1317+
}
1318+
};
1319+
await server.post('property', 'createDomain', createPayload, {...topFolderOptions, ...designerReaderOptions}).expect(successfulResponse);
1320+
await verifyCountTypeAliquotRollup(dataType, false);
1321+
1322+
})
1323+
10881324
});
10891325

experiment/src/org/labkey/experiment/ExperimentModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ SELECT COUNT(DISTINCT DD.DomainURI) FROM
740740
results.put("sampleNegativeAmountCount", new SqlSelector(schema, "SELECT COUNT(*) FROM exp.material WHERE storedamount < 0").getObject(Long.class));
741741
results.put("sampleUnitsDifferCount", new SqlSelector(schema, "SELECT COUNT(*) from exp.material m JOIN exp.materialSource s ON m.materialsourceid = s.rowid WHERE m.units != s.metricunit").getObject(Long.class));
742742
results.put("sampleTypesWithoutUnitsCount", new SqlSelector(schema, "SELECT COUNT(*) from exp.materialSource WHERE category IS NULL AND metricunit IS NULL").getObject(Long.class));
743+
results.put("sampleTypesWithMassTypeUnit", new SqlSelector(schema, "SELECT COUNT(*) from exp.materialSource WHERE category IS NULL AND metricunit IN ('kg', 'g', 'mg', 'ug', 'ng')").getObject(Long.class));
744+
results.put("sampleTypesWithVolumeTypeUnit", new SqlSelector(schema, "SELECT COUNT(*) from exp.materialSource WHERE category IS NULL AND metricunit IN ('L', 'mL', 'uL')").getObject(Long.class));
745+
results.put("sampleTypesWithCountTypeUnit", new SqlSelector(schema, "SELECT COUNT(*) from exp.materialSource WHERE category IS NULL AND metricunit = ?", "unit").getObject(Long.class));
743746

744747
results.put("duplicateSampleMaterialNameCount", new SqlSelector(schema, "SELECT COUNT(*) as duplicateCount FROM " +
745748
"(SELECT name, cpastype FROM exp.material WHERE cpastype <> 'Material' GROUP BY name, cpastype HAVING COUNT(*) > 1) d").getObject(Long.class));

0 commit comments

Comments
 (0)