Skip to content

Update Amounts and Units for better sorting and filtering behavior#6994

Merged
cnathe merged 149 commits intodevelopfrom
fb_amountsAndUnits
Sep 15, 2025
Merged

Update Amounts and Units for better sorting and filtering behavior#6994
cnathe merged 149 commits intodevelopfrom
fb_amountsAndUnits

Conversation

@labkey-susanh
Copy link
Contributor

@labkey-susanh labkey-susanh commented Sep 2, 2025

Rationale

Our original implementation for the StoredAmount and Units columns added display columns over the user-provided data so the application grid displays used consistent units, but left the values as provided by users. Since we accept values in different, compatible units, this results in sorting and filtering on these columns not behaving as one would expect (e.g, samples with amounts of 2 mL and 2 L will be sorted next to each other, though displayed as 2 mL and 2000 mL in the grid).

This work update our handling of StoredAmount and Units so they always store values in the base units for sample types that have display units. This provides us with a valid basis for sorting and filtering.

We also are updating the processing of data to always require that the user provides both StoredAmount and Units when either of these values is provided.

Related Pull Requests

Changes

  • Upgrade script to convert existing amounts and units to base units, and drop any units that don't have values
  • Remove Measurement class in favor of Quantity and Units classes
  • Update detailed audit log to handle provided values that are converted before being stored
  • Update aliquot rollup logic to store values in base units
  • Remove recompute of rollup values when display unit is changed
  • Add SampleTypeAmountDisplayColumn and SampleTyepUnitDisplayColumn to display amount and units using chosen display units

labkey-matthewb and others added 30 commits December 20, 2024 15:23
(just working here, not for 24.11)
…s conversion more consistent in a bunch of places.

ColumnRenderProperties.getConvertFn()
# Conflicts:
#	api/src/org/labkey/api/data/ColumnRenderPropertiesImpl.java
#	api/src/org/labkey/api/data/DataColumn.java
#	api/src/org/labkey/api/data/DisplayColumn.java
#	api/src/org/labkey/api/ontology/KindOfQuantity.java
#	api/src/org/labkey/api/ontology/Quantity.java
#	api/src/org/labkey/api/ontology/Unit.java
#	experiment/resources/schemas/dbscripts/postgresql/exp-25.005-25.006.sql
#	experiment/resources/schemas/dbscripts/sqlserver/exp-25.005-25.006.sql
public class ExperimentWarningProvider implements WarningProvider
{
@Override
public void addDynamicWarnings(@NotNull Warnings warnings, @Nullable ViewContext context, boolean showAllWarnings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we'll call this method on every LKS page load for admins, so we should cache the message to avoid requerying (likely very large) audit table when there is a mismatch.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea is to store an additional ExperimentModule.ACTUAL_AUDIT_COUNT_PROP in ExperimentModule.AMOUNT_AND_UNIT_UPGRADE_PROP property map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing the count as a class member seems to work.

if (rawUnits == null)
return null;
if (rawUnits instanceof Unit u)
if (defaultUnits == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the outer braces to the block of if/else if/else ? It's confusing to read and error prone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Done.

public class ExperimentWarningProvider implements WarningProvider
{
@Override
public void addDynamicWarnings(@NotNull Warnings warnings, @Nullable ViewContext context, boolean showAllWarnings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea is to store an additional ExperimentModule.ACTUAL_AUDIT_COUNT_PROP in ExperimentModule.AMOUNT_AND_UNIT_UPGRADE_PROP property map.

else if (materialUnit != null && !materialUnit.isCompatible(baseUnit))
{
LOG.info("{} '{}' for sample '{}' is not compatible with the base unit '{}'. No conversion done.", isAliquot ? "Aliquot unit" : "Unit", materialUnit.name(), sampleMap.get(Name.name()), baseUnit);
sampleCounts.put("invalidUnits", sampleCounts.getOrDefault("invalidUnits", 0) + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it time to change those log level to DEBUG?

{
PropertyManager.WritablePropertyMap props = PropertyManager.getWritableProperties(AMOUNT_AND_UNIT_UPGRADE_PROP, true);
props.put(AUDIT_COUNT_PROP, auditCount.toString());
props.put(TRANSACTION_ID_PROP, transactionId == null ? null : transactionId.toString());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If transactionId is null, do we still want to set property map?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can't really be null unless my read of the logic is incorrect. This null check just to appease the IntelliJ warnings.

}

if (!StringUtils.isEmpty(totalDisplayUnitStr))
Unit totalUnit = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we are not taking advantage of the fact now all aliquots are now stored in the same base "units", if sample type has a unit set. Rollup should now be much simpler (and faster) for sample types with unit, potentially could be done with a simple sql script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I see what you're saying, but I'm going to not make this change now. We can make this optimization in a separate, smaller PR so we can concentrate on assuring no regressions with the rollup vs. the unit conversion code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue for this so we don't forget?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labkey-susanh labkey-susanh requested a review from XingY September 15, 2025 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants