Skip to content

Conversation

@vfrank66
Copy link
Contributor

No description provided.

}

// ConvertUnit converts a value from one unit to another
func ConvertUnit(fromVal float64, fromUnit, toUnit string) (float64, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if we changed this API to be ConvertUnit(fromQuantity result.Quantity, newUnit string) (result.Quantity, error)?

This would enable callers to to do something like
foo, err = ConvertUnit(foo, bar.unit)
which i thing would be slightly better for usability.

@@ -0,0 +1,36 @@
// Copyright 2024 Google LLC
Copy link
Contributor

Choose a reason for hiding this comment

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

Per the guidance at https://google.github.io/styleguide/go/best-practices.html#util-packages
I would recommend we avoid naming this class helpers, as that doesn't really help the reader know what value this file provides.

I think a better option here would be calling this units.go, the functions this file provides seem to all be related to handling logic related to units. Additionally, doing this would allow us to roll some of the other unit specific logic from ucum.go into this file so ucum.go can provide all the public APIs for this module and units.go can provide logic and constants for units.

WDYT?

}

// FixUnit applies both empty and date unit fixes
func FixUnit(unit string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of using the verb normalize or clean here in stread of fix?

package ucum

// FixEmptyUnit handles null/empty units, replacing them with "1" (dimensionless unit)
func FixEmptyUnit(unit string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this function as well as FixCQLDateUnit be public, or should the public api of ucum.go just call the fixUnit function at the public boundaries?

// See the License for the specific language governing permissions and
// limitations under the License.

// Package ucum provides UCUM (Unified Code for Units of Measure) support for the CQL engine.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind adding a link to https://unitsofmeasure.org/ucum as well as https://cql.hl7.org/02-authorsguide.html#quantities here? :)

valid, msg := ucum.CheckUnit(unitStr, true, true)
if !valid {
// Just log a warning and continue - don't block parsing
fmt.Printf("Warning: %s\n", msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above here.


// CQLToUCUMDateUnits maps CQL date units to their UCUM equivalents
var CQLToUCUMDateUnits = map[string]string{
"years": "a_g",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is right, but i cant remember / find the right docs on some of the specifics here. Can you add comments on why the _g is added?

https://cql.hl7.org/02-authorsguide.html#quantities seems to suggest that "a" is the equivalent here. But I feel like I've also seen this syntax before.

Copy link
Contributor Author

@vfrank66 vfrank66 May 3, 2025

Choose a reason for hiding this comment

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

_g = gregorian
_j = julian
https://github.com/LHNCBC/ucum-lhc/blob/master/data/ucum.csv#L278

This is the npm package used in the javascript cql engine, which is where I started. Since go does not have a ucum package this file was intended to be a minimal replacement for not having the equivalent package.

If would ask about the ucum support vs fhir support vs the cql spec on unit conversion that I do not have any answers to 😖

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the link here! Mind adding that link to the docs at the top of the file? It might help if we find further edge cases in the future. If this is what the Javascript CQL engine is using than I'm more apt to say we can go with this solution for now :)

conversionFunc := ctx.GetChild(0).(antlr.TerminalNode).GetText()
expr := v.VisitExpression(ctx.Expression())

if conversionFunc == "convert" {
Copy link
Contributor

Choose a reason for hiding this comment

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

consider reversing this case,
if conversionFunc != "covert" { ... }
that way we can unnest most of this function's code.

}
}

func (v *visitor) VisitConversionExpressionTerm(ctx *cql.ConversionExpressionTermContext) model.IExpression {
Copy link
Contributor

Choose a reason for hiding this comment

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

there's a lot of cases in this, can we get some tests that hit this code?

valid, msg := ucum.CheckUnit(unit, true, true)
if !valid {
// Log warning but proceed
fmt.Printf("Warning: %s\n", msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

According to the spec https://cql.hl7.org/09-b-cqlreference.html#toquantity the result should be null here.

When adding tests please make sure this case is covered.

@evan-gordon
Copy link
Contributor

Thanks for taking a crack at this! The UCUM standard is a complicated space, it might take a few rounds of reviews here to get this to a good spot, so this is a great start!

copybara-service bot pushed a commit that referenced this pull request Jun 2, 2025
FUTURE_COPYBARA_INTEGRATE_REVIEW=#106 from vfrank66:issue-105/add-ucum-conversion-support be44af6
PiperOrigin-RevId: 765535042
@copybara-service copybara-service bot mentioned this pull request Jun 2, 2025
@copybara-service copybara-service bot merged commit 94cedb3 into google:main Jun 2, 2025
1 of 5 checks passed
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.

2 participants