diff --git a/api/src/org/labkey/api/data/NameGeneratorState.java b/api/src/org/labkey/api/data/NameGeneratorState.java index fd0083024ec..d82896a0af5 100644 --- a/api/src/org/labkey/api/data/NameGeneratorState.java +++ b/api/src/org/labkey/api/data/NameGeneratorState.java @@ -50,9 +50,9 @@ import java.util.stream.Collectors; import static org.labkey.api.data.NameGenerator.getParentImportAliasFieldKeys; -import static org.labkey.api.exp.api.ExpMaterial.ALIQUOTED_FROM_INPUT; import static org.labkey.api.exp.api.ExpRunItem.INPUT_PARENT; import static org.labkey.api.exp.api.ExpRunItem.PARENT_IMPORT_ALIAS_MAP_PROP; +import static org.labkey.api.exp.api.ExperimentService.isAliquotedFromColumn; public class NameGeneratorState implements AutoCloseable { @@ -362,7 +362,7 @@ private void addParentLookupValues(String parentTypeName, boolean isMaterialParent, @Nullable Map parentImportAliases, ExpObject parentObject, - Map> inputLookupValues, + Map> inputLookupValues, @Nullable NameGenerator altNameGenerator) { String inputType = isMaterialParent ? ExpMaterial.MATERIAL_INPUT_PARENT : ExpData.DATA_INPUT_PARENT; @@ -384,7 +384,7 @@ private void addParentLookupValues(String parentTypeName, continue; // add to Input/ - inputLookupValues.computeIfAbsent(FieldKey.fromParts(INPUT_PARENT, fieldName), (s) -> new ArrayList<>()).add(lookupValue); + inputLookupValues.computeIfAbsent(FieldKey.fromParts(INPUT_PARENT, fieldName), (s) -> new LinkedHashSet<>()).add(lookupValue); // add to importAlias/ if (parentImportAliases != null) @@ -393,19 +393,19 @@ private void addParentLookupValues(String parentTypeName, .entrySet() .stream() .filter(entry -> inputFK.equals(entry.getValue())) - .forEach(entry -> inputLookupValues.computeIfAbsent(FieldKey.fromParts(entry.getKey(), fieldName), (s) -> new ArrayList<>()).add(lookupValue)); + .forEach(entry -> inputLookupValues.computeIfAbsent(FieldKey.fromParts(entry.getKey(), fieldName), (s) -> new LinkedHashSet<>()).add(lookupValue)); } // add to Inputs/ - inputLookupValues.computeIfAbsent(FieldKey.fromParts(inputType, fieldName), (s) -> new ArrayList<>()).add(lookupValue); + inputLookupValues.computeIfAbsent(FieldKey.fromParts(inputType, fieldName), (s) -> new LinkedHashSet<>()).add(lookupValue); // add to Inputs// - inputLookupValues.computeIfAbsent(FieldKey.fromParts(inputType, parentTypeName, fieldName), (s) -> new ArrayList<>()).add(lookupValue); + inputLookupValues.computeIfAbsent(FieldKey.fromParts(inputType, parentTypeName, fieldName), (s) -> new LinkedHashSet<>()).add(lookupValue); } } private void addAncestorLookupValues( ExpRunItem parentObject, - Map> inputLookupValues, + Map> inputLookupValues, @Nullable NameGenerator altNameGenerator) { String parentLsid = parentObject.getLSID(); @@ -481,8 +481,8 @@ else if (parentObject instanceof ExpData expData) if (!ancestorLookupValues.isEmpty()) { - inputLookupValues.putIfAbsent(ancestorFieldKey, new ArrayList<>()); - List lookupValues = inputLookupValues.get(ancestorFieldKey); + inputLookupValues.putIfAbsent(ancestorFieldKey, new LinkedHashSet<>()); + Set lookupValues = inputLookupValues.get(ancestorFieldKey); for (Object lookupVal : ancestorLookupValues) { if (!lookupValues.contains(lookupVal)) @@ -497,7 +497,7 @@ private void addParentLookupContext(String parentTypeName/* already decoded */, String parentName, boolean isMaterialParent, @Nullable Map parentImportAliases, - Map> inputLookupValues, + Map> inputLookupValues, @Nullable NameGenerator altNameGenerator) { NameGenerator.ExpressionSummary expressionSummary = getExpressionSummary(altNameGenerator); @@ -591,7 +591,7 @@ private Map additionalContext(@NotNull Map row if (expressionSummary.hasParentLookup() || expressionSummary.hasParentInputs()) { Map> inputs = new HashMap<>(); - Map> inputLookupValues = new HashMap<>(); + Map> inputLookupValues = new HashMap<>(); inputs.put(FieldKey.fromParts(INPUT_PARENT), new LinkedHashSet<>()); inputs.put(FieldKey.fromParts(ExpData.DATA_INPUT_PARENT), new LinkedHashSet<>()); @@ -663,7 +663,7 @@ else if (value.isEmpty()) ctx.putAll(inputValues); Map lookupValues = new HashMap<>(); - inputLookupValues.forEach((key, value) -> lookupValues.put(key, value.size() > 1 ? value : (value.size() == 1 ? value.get(0) : null))); + inputLookupValues.forEach((key, value) -> lookupValues.put(key, value.size() > 1 ? value : (value.size() == 1 ? value.iterator().next() : null))); ctx.putAll(lookupValues); } @@ -740,12 +740,12 @@ else if (value.isEmpty()) private void addParentLookupInput(String colName, Object value, @Nullable Map parentImportAliases, - Map> inputLookupValues, + Map> inputLookupValues, @Nullable NameGenerator altNameGenerator) { String prefix = null; String dataType = null; - if (ALIQUOTED_FROM_INPUT.equalsIgnoreCase(colName)) + if (isAliquotedFromColumn(colName)) { prefix = ExpMaterial.MATERIAL_INPUT_PARENT; dataType = getParentTable() != null ? getParentTable().getName() : null; @@ -791,7 +791,7 @@ private void addInputs(String colName, String[] parts = colName.split("/", 2); String prefix = null; String decodedDataType = null; - if (ALIQUOTED_FROM_INPUT.equalsIgnoreCase(colName)) + if (isAliquotedFromColumn(colName)) { prefix = ExpMaterial.MATERIAL_INPUT_PARENT; decodedDataType = getParentTable() != null ? getParentTable().getName() : null; diff --git a/api/src/org/labkey/api/exp/api/ExpMaterial.java b/api/src/org/labkey/api/exp/api/ExpMaterial.java index 95bde41b9f7..c1b9b829fdb 100644 --- a/api/src/org/labkey/api/exp/api/ExpMaterial.java +++ b/api/src/org/labkey/api/exp/api/ExpMaterial.java @@ -35,6 +35,7 @@ public interface ExpMaterial extends ExpRunItem String MATERIAL_INPUTS_PREFIX_LC = MATERIAL_INPUTS_PREFIX.toLowerCase(); String MATERIAL_OUTPUT_CHILD = "MaterialOutputs"; String ALIQUOTED_FROM_INPUT = "AliquotedFrom"; + String ALIQUOTED_FROM_INPUT_LABEL = "Aliquoted From"; @Nullable ExpSampleType getSampleType(); diff --git a/api/src/org/labkey/api/exp/api/ExpSampleType.java b/api/src/org/labkey/api/exp/api/ExpSampleType.java index 496afe9e2c8..7560e159d8f 100644 --- a/api/src/org/labkey/api/exp/api/ExpSampleType.java +++ b/api/src/org/labkey/api/exp/api/ExpSampleType.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; import org.labkey.api.exp.ExperimentException; @@ -242,6 +243,15 @@ String createSampleName(@NotNull Map rowMap, @NotNull Map getImportAliases() throws IOException; + // Issue 53063: support "Aliquoted From" + @NotNull + default Map getImportAliasesIncludingAliquot() throws IOException + { + Map aliases = new CaseInsensitiveHashMap<>(getImportAliases()); + aliases.put(ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL, ExpMaterial.ALIQUOTED_FROM_INPUT); + return aliases; + } + @NotNull Map getRequiredImportAliases() throws IOException; @NotNull Map> getImportAliasMap() throws IOException; diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index 40d9651fd26..cc3c1cca948 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -506,6 +506,12 @@ static boolean isInputOutputColumn(String columnName) ExpMaterial.MATERIAL_OUTPUT_CHILD.equalsIgnoreCase(prefix); } + static boolean isAliquotedFromColumn(String columnName) + { + return ExpMaterial.ALIQUOTED_FROM_INPUT.equalsIgnoreCase(columnName) || + ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL.equalsIgnoreCase(columnName); + } + // convert MaterialInputs/Blood/Type to MaterialInputs/Blood$SType static @Nullable String getEncodedLineageKey(String inputColumn /*not encoded*/) { diff --git a/api/src/org/labkey/api/query/QueryUpdateService.java b/api/src/org/labkey/api/query/QueryUpdateService.java index 78723b7d7ac..53925404dec 100644 --- a/api/src/org/labkey/api/query/QueryUpdateService.java +++ b/api/src/org/labkey/api/query/QueryUpdateService.java @@ -109,6 +109,7 @@ enum ConfigParameters SkipRequiredFieldValidation, // (Bool) skip validation of required fields, used during import when the import of data happens in two hitches (e.g., samples in one file and sample statuses in a second) BulkLoad, // (Bool) skips detailed auditing CheckForCrossProjectData, // (Bool) Check if data belong to other projects + ProcessingPartition, // (Bool) Importing a partitioned file from original file SkipInsertOptionValidation, // (Bool) Skip assert(supportsInsertOption(context.getInsertOption())) for special scenarios (e.g., folder import uses merge action that's otherwise not supported for a table), PreferPKOverObjectUriAsKey, // (Bool) Prefer getPkColumnNames instead of getObjectURIColumnName to use as keys SkipReselectRows, // (Bool) If true, skip qus.getRows and use raw returned rows. Applicable for CommandType.insert/insertWithKeys/update/updateChangingKeys diff --git a/experiment/src/org/labkey/experiment/ExpDataIterators.java b/experiment/src/org/labkey/experiment/ExpDataIterators.java index 264733d0a42..e0a9723ecad 100644 --- a/experiment/src/org/labkey/experiment/ExpDataIterators.java +++ b/experiment/src/org/labkey/experiment/ExpDataIterators.java @@ -162,6 +162,7 @@ import static org.labkey.api.exp.api.ExpData.DATA_INPUTS_PREFIX_LC; import static org.labkey.api.exp.api.ExpData.DATA_INPUT_PARENT; import static org.labkey.api.exp.api.ExpMaterial.ALIQUOTED_FROM_INPUT; +import static org.labkey.api.exp.api.ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL; import static org.labkey.api.exp.api.ExpMaterial.MATERIAL_INPUTS_PREFIX_LC; import static org.labkey.api.exp.api.ExpMaterial.MATERIAL_INPUT_PARENT; import static org.labkey.api.exp.api.ExpRunItem.INPUTS_PREFIX_LC; @@ -2591,6 +2592,7 @@ public boolean next() throws BatchValidationException { _context.setCrossTypeImport(false); _context.setCrossFolderImport(false); + _context.putConfigParameter(QueryUpdateService.ConfigParameters.ProcessingPartition, true); boolean hasCrossFolderImport = false; @@ -2610,6 +2612,7 @@ public boolean next() throws BatchValidationException if (_isCrossFolder && !_context.getInsertOption().updateOnly && hasCrossFolderImport) // all updates are cross-folder due to lack of Container column SimpleMetricsService.get().increment(ExperimentService.MODULE_NAME, _isSamples ? "sampleImport" : "dataClassImport", "multiFolderImport"); + _context.putConfigParameter(QueryUpdateService.ConfigParameters.ProcessingPartition, false); _context.setCrossTypeImport(_isCrossType); _context.setCrossFolderImport(_isCrossFolder); } @@ -2861,6 +2864,7 @@ private TypeData createSampleHeaderRow(ExpSampleTypeImpl sampleType, Container c Map aliasMap = sampleType.getImportAliases(); validFields.addAll(aliasMap.keySet()); validFields.add(ALIQUOTED_FROM_INPUT); + validFields.add(ALIQUOTED_FROM_INPUT_LABEL); validFields.add("StorageUnit"); validFields.add("Storage Unit"); validFields.add("StorageUnitLabel"); diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java index 01899ea6430..9b3bd485f40 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java @@ -1370,6 +1370,32 @@ else if (name != null) return dataRow; } + + @Override + protected Map updateRow(User user, Container container, Map row, @NotNull Map oldRow, boolean allowOwner, boolean retainCreation) + throws InvalidKeyException, ValidationException, QueryUpdateServiceException, SQLException + { + Map result = super.updateRow(user, container, row, oldRow, allowOwner, retainCreation); + + // add MaterialInput/DataInputs field from parent alias + try + { + Map parentAliases = _dataClass.getImportAliases(); + for (String alias : parentAliases.keySet()) + { + if (row.containsKey(alias)) + result.put(parentAliases.get(alias), result.get(alias)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + return result; + + } + @Override protected Map _update(User user, Container c, Map row, Map oldRow, Object[] keys) throws SQLException, ValidationException { diff --git a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java index 2356cc65f8b..80393dc07d5 100644 --- a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java @@ -1725,7 +1725,7 @@ public DataIteratorBuilder persistRows(DataIteratorBuilder data, DataIteratorCon // TODO: subclass PersistDataIteratorBuilder to index Materials! not DataClass! try { - var persist = new ExpDataIterators.PersistDataIteratorBuilder(data, this, propertiesTable, _ss, getUserSchema().getContainer(), getUserSchema().getUser(), _ss.getImportAliases(), sampleTypeObjectId) + var persist = new ExpDataIterators.PersistDataIteratorBuilder(data, this, propertiesTable, _ss, getUserSchema().getContainer(), getUserSchema().getUser(), _ss.getImportAliasesIncludingAliquot(), sampleTypeObjectId) .setFileLinkDirectory(SAMPLETYPE_FILE_DIRECTORY); ExperimentServiceImpl experimentServiceImpl = ExperimentServiceImpl.get(); SearchService.TaskIndexingQueue queue = SearchService.get().defaultTask().getQueue(getContainer(), SearchService.PRIORITY.modified); diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp b/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp index ff3606d9a6b..c3fc1591d95 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp @@ -468,23 +468,80 @@ public void testNameExpression() throws Exception List> aliquotRows = new ArrayList<>(); aliquotRows.add(CaseInsensitiveHashMap.of("aliquotedFrom", expectedName1, "AliquotCount", 10)); - aliquotRows.add(CaseInsensitiveHashMap.of("Aliquotedfrom", expectedName2, "aliquotCount", 5)); - aliquotRows.add(CaseInsensitiveHashMap.of("ALIQUOTEDFROM", expectedName1, "Aliquotcount", 15)); - aliquotRows.add(CaseInsensitiveHashMap.of("AliquotedFrom", expectedName3, "ALIQUOTCOUNT", 2)); - List> aliquots = insertSampleRows(sampleTypeName, aliquotRows); - assertExpectedName(st, expectedName1 + "-ALI-0004"); assertEquals(expectedName1, aliquots.get(0).get("AliquotedFrom")); + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("Aliquotedfrom", expectedName2, "aliquotCount", 5)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); assertExpectedName(st, expectedName2 + "-ALI-0005"); - assertEquals(expectedName2, aliquots.get(1).get("aliquotedFrom")); + assertEquals(expectedName2, aliquots.get(0).get("aliquotedFrom")); + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("ALIQUOTEDFROM", expectedName1, "Aliquotcount", 15)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); assertExpectedName(st, expectedName1 + "-ALI-0006"); - assertEquals(expectedName1, aliquots.get(2).get("aliquotedfrom")); + assertEquals(expectedName1, aliquots.get(0).get("aliquotedfrom")); + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("AliquotedFrom", expectedName3, "ALIQUOTCOUNT", 2)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); assertExpectedName(st, expectedName3 + "-ALI-0007"); - assertEquals(expectedName3, aliquots.get(3).get("ALIQUOTEDFROM")); + assertEquals(expectedName3, aliquots.get(0).get("ALIQUOTEDFROM")); + + // Issue 53063: Support "Aliquoted From" + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("Aliquoted From", expectedName2, "ALIQUOTCOUNT", 2)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName2 + "-ALI-0008"); + assertEquals(expectedName2, aliquots.get(0).get("ALIQUOTEDFROM")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("aliquoted from", expectedName3, "ALIQUOTCOUNT", 3)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName3 + "-ALI-0009"); + assertEquals(expectedName3, aliquots.get(0).get("aliquotedFrom")); + + // test the default aliquot naming pattern (${${AliquotedFrom}-:withCounter} + st.setAliquotNameExpression(""); + st.save(user); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("aliquotedFrom", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-1"); + assertEquals(expectedName1, aliquots.get(0).get("AliquotedFrom")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("Aliquotedfrom", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-2"); + assertEquals(expectedName1, aliquots.get(0).get("aliquotedFrom")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("ALIQUOTEDFROM", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-3"); + assertEquals(expectedName1, aliquots.get(0).get("aliquotedfrom")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("AliquotedFrom", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-4"); + assertEquals(expectedName1, aliquots.get(0).get("ALIQUOTEDFROM")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("Aliquoted From", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-5"); + assertEquals(expectedName1, aliquots.get(0).get("ALIQUOTEDFROM")); + + aliquotRows = new ArrayList<>(); + aliquotRows.add(CaseInsensitiveHashMap.of("aliquoted from", expectedName1)); + aliquots = insertSampleRows(sampleTypeName, aliquotRows); + assertExpectedName(st, expectedName1 + "-6"); + assertEquals(expectedName1, aliquots.get(0).get("aliquotedFrom")); } @Test diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 5d2e5f94876..1f1908e175b 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -10252,6 +10252,7 @@ else if (inputColumns != null) allColumns.addAll(inputColumns); Map parentAliasColumnMap = new CaseInsensitiveHashMap<>(); + parentAliasColumnMap.put(ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL, ExpMaterial.ALIQUOTED_FROM_INPUT); try { if (currentDataType instanceof ExpDataClass dataClass) @@ -10270,7 +10271,7 @@ else if (currentDataType instanceof ExpSampleType sampleType) String columnName = inputColName; if (parentAliasColumnMap.containsKey(columnName)) columnName = parentAliasColumnMap.get(columnName); - if (ExperimentService.isInputOutputColumn(columnName) || "parent".equalsIgnoreCase(columnName)) + if (ExperimentService.isInputOutputColumn(columnName) || ExperimentService.isAliquotedFromColumn(columnName) || "parent".equalsIgnoreCase(columnName)) { if (seenColumns.contains(columnName)) throw new ApiUsageException(String.format(DUPLICATE_COLUMN_IN_DATA_ERROR, columnName)); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeUpdateServiceDI.java b/experiment/src/org/labkey/experiment/api/SampleTypeUpdateServiceDI.java index 461eb6f64fe..8417fd01071 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeUpdateServiceDI.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeUpdateServiceDI.java @@ -858,6 +858,31 @@ public static boolean isAliquotStatusChangeNeedRecalc(Collection available return false; } + @Override + protected Map updateRow(User user, Container container, Map row, @NotNull Map oldRow, boolean allowOwner, boolean retainCreation) + throws InvalidKeyException, ValidationException, QueryUpdateServiceException, SQLException + { + Map result = super.updateRow(user, container, row, oldRow, allowOwner, retainCreation); + + // add MaterialInput/DataInputs field from parent alias + try + { + Map parentAliases = _sampleType.getImportAliases(); + for (String alias : parentAliases.keySet()) + { + if (row.containsKey(alias)) + result.put(parentAliases.get(alias), result.get(alias)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + return result; + + } + @Override protected Map _update(User user, Container c, Map row, Map oldRow, Object[] keys) throws SQLException, ValidationException { @@ -1631,7 +1656,7 @@ public DataIterator getDataIterator(DataIteratorContext context) addColumns.addUniqueIdDbSequenceColumns(ContainerManager.getRoot(), materialTable); // recompute only add when AliquotedFrom column is not null - if (columnNameMap.containsKey(ExpMaterial.ALIQUOTED_FROM_INPUT)) + if (columnNameMap.containsKey(ExpMaterial.ALIQUOTED_FROM_INPUT) || columnNameMap.containsKey(ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL)) { addColumns.addNullColumn(ROOT_RECOMPUTE_ROWID_COL, JdbcType.INTEGER); addColumns.addNullColumn(PARENT_RECOMPUTE_NAME_COL, JdbcType.VARCHAR); @@ -1752,7 +1777,7 @@ static class _GenerateNamesDataIterator extends SimpleTranslator try { - Map importAliasMap = sampleType.getImportAliases(); + Map importAliasMap = sampleType.getImportAliasesIncludingAliquot(); _extraPropsFns.add(() -> Map.of(PARENT_IMPORT_ALIAS_MAP_PROP, importAliasMap)); } catch (IOException e) @@ -1792,24 +1817,7 @@ protected void processNextInput() { Map map = new CaseInsensitiveHashMap<>(((MapDataIterator)getInput()).getMap()); - String aliquotedFrom = null; - Object aliquotedFromObj = map.get(ExpMaterial.ALIQUOTED_FROM_INPUT); - if (aliquotedFromObj != null) - { - if (aliquotedFromObj instanceof String) - { - // Issue 45563: We need the AliquotedFrom name to be quoted so we can properly find the parent, - // but we don't want to include the quotes in the name we generate using AliquotedFrom - aliquotedFrom = StringUtilsLabKey.unquoteString((String) aliquotedFromObj).trim(); - map.put(ExpMaterial.ALIQUOTED_FROM_INPUT, aliquotedFrom); - } - else if (aliquotedFromObj instanceof Number) - { - aliquotedFrom = aliquotedFromObj.toString(); - } - } - - boolean isAliquot = !StringUtils.isEmpty(aliquotedFrom); + boolean isAliquot = isAliquotRow(map); try { @@ -1840,7 +1848,8 @@ else if (aliquotedFromObj instanceof Number) catch (NameGenerator.NameGenerationException e) { // Failed to generate a name due to some part of the expression not in the row - String rowText = _context.isCrossFolderImport() || _context.isCrossTypeImport() ? "" : " on row " + e.getRowNumber(); + // Issue 53963: Cross-sample-type import gives incorrect row number in message + String rowText = _context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.ProcessingPartition) ? "" : " on row " + e.getRowNumber(); if (isAliquot) addRowError("Failed to generate name for aliquot" + rowText + " using aliquot naming pattern " + _sampleType.getAliquotNameExpression() + ". Check the syntax of the aliquot naming pattern and the data values for the aliquot."); else if (_sampleType.hasNameExpression()) @@ -1926,7 +1935,7 @@ void init(TableInfo target, boolean useImportAliases, boolean initRollupCounts) ColumnInfo from = di.getColumnInfo(i); if (from != null) { - if (getAliquotedFromColName().equalsIgnoreCase(from.getName())) + if (isAliquotedFromColName(from.getName())) aliquotedFromDataColInd = i; else if (unitsImportAliasSet.contains(from.getName())) unitDataColInd = i; @@ -2011,10 +2020,12 @@ else if (StoredAmount.name().equalsIgnoreCase(name)) } } - private String getAliquotedFromColName() + private boolean isAliquotedFromColName(String fromCol) { - // for update, AliquotedFromLSID is reselected from existing row. For other actions, "AliquotedFrom" needs to be provided - return _context.getInsertOption().updateOnly ? AliquotedFromLSID.name() : ExpMaterial.ALIQUOTED_FROM_INPUT; + if (_context.getInsertOption().updateOnly) + return AliquotedFromLSID.name().equalsIgnoreCase(fromCol); + + return ExperimentService.isAliquotedFromColumn(fromCol); } private void _addConvertColumn(String name, int fromIndex, JdbcType toType, ForeignKey toFk, int derivationDataColInd, boolean isAliquotField) @@ -2175,6 +2186,36 @@ else if (aliquotedFrom instanceof Number) } } + private static boolean isAliquotRow(Map map, String aliquotedFromColName) + { + String aliquotedFrom = null; + Object aliquotedFromObj = map.get(aliquotedFromColName); + if (aliquotedFromObj == null && map.containsKey(ColumnInfo.labelFromName(ExpMaterial.ALIQUOTED_FROM_INPUT))) + aliquotedFromObj = map.get(ColumnInfo.labelFromName(ExpMaterial.ALIQUOTED_FROM_INPUT)); + if (aliquotedFromObj != null) + { + if (aliquotedFromObj instanceof String) + { + // Issue 45563: We need the AliquotedFrom name to be quoted so we can properly find the parent, + // but we don't want to include the quotes in the name we generate using AliquotedFrom + aliquotedFrom = StringUtilsLabKey.unquoteString((String) aliquotedFromObj).trim(); + if (!StringUtils.isEmpty(aliquotedFrom)) + map.put(ExpMaterial.ALIQUOTED_FROM_INPUT, aliquotedFrom); + } + else if (aliquotedFromObj instanceof Number) + { + aliquotedFrom = aliquotedFromObj.toString(); + } + } + + return !StringUtils.isEmpty(aliquotedFrom); + } + + private static boolean isAliquotRow(Map map) + { + return isAliquotRow(map, ExpMaterial.ALIQUOTED_FROM_INPUT) || isAliquotRow(map, ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL); + } + public static class SampleNameGeneratorState extends NameGeneratorState { private final NameGenerator aliquotNameGenerator; @@ -2195,24 +2236,7 @@ public String nextName(Map map, @Nullable Set parentSamples, @Nullable List>> _extraPropsFns) throws NameGenerator.NameGenerationException { - String aliquotedFrom = null; - Object aliquotedFromObj = map.get(ExpMaterial.ALIQUOTED_FROM_INPUT); - if (aliquotedFromObj != null) - { - if (aliquotedFromObj instanceof String) - { - // Issue 45563: We need the AliquotedFrom name to be quoted so we can properly find the parent, - // but we don't want to include the quotes in the name we generate using AliquotedFrom - aliquotedFrom = StringUtilsLabKey.unquoteString((String) aliquotedFromObj).trim(); - map.put(ExpMaterial.ALIQUOTED_FROM_INPUT, aliquotedFrom); - } - else if (aliquotedFromObj instanceof Number) - { - aliquotedFrom = aliquotedFromObj.toString(); - } - } - - boolean isAliquot = !StringUtils.isEmpty(aliquotedFrom); + boolean isAliquot = isAliquotRow(map); String generatedName = null; if (isAliquot && aliquotNameGenerator != null) diff --git a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java index 49d87a94d84..255b762d8ab 100644 --- a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java +++ b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java @@ -4422,6 +4422,7 @@ protected Map getRenamedColumns() Set aliases = new CaseInsensitiveHashSet(); // Issue 53419: Aliquot parent with number like names that starts with leading zeroes aren't resolved during import aliases.add(ExpMaterial.ALIQUOTED_FROM_INPUT); + aliases.add(ExpMaterial.ALIQUOTED_FROM_INPUT_LABEL); boolean crossTypeImport = getOptionParamValue(AbstractQueryImportAction.Params.crossTypeImport); // Issue 51894: We need to stop conversion to numbers for alias fields for all type // If there are aliases defined for one type that are number fields in another type, this will prevent