Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
5067059
Initial work on getting basic Quantity equality working
johngrimes Mar 30, 2022
2750f4f
Add documentation for targeted functionality
johngrimes Apr 1, 2022
c89cba5
Add EqualityOperatorQuantityTest
johngrimes Apr 4, 2022
c9aa2bf
Add tests for comparison and math operators
johngrimes Apr 5, 2022
495aa30
Add tests for date arithmetic
johngrimes Apr 6, 2022
ca01ca0
Get EqualityOperatorQuantityTest passing
johngrimes Apr 7, 2022
9016748
Get ComparisonOperatorQuantityTest passing
johngrimes Apr 7, 2022
eae8b3f
Get MathOperatorQuantityTest passing
johngrimes Apr 7, 2022
56657a6
Get DateArithmeticTest passing
johngrimes Apr 9, 2022
b64bda3
Add support for arithmetic on Time
johngrimes Apr 9, 2022
c44e1b9
Remove unnecessary implements clauses
johngrimes Apr 9, 2022
9438b82
Add ComparableQuantity to unit test dependencies
johngrimes Apr 9, 2022
70202de
Remove support for milliseconds and document
johngrimes Apr 9, 2022
160b003
Get all tests passing, refactor some aspects of literals
johngrimes Apr 11, 2022
8017b83
Remove duplicate SqlFunction methods
johngrimes Apr 11, 2022
11c9aad
Add ComparisonOperatorDateTimeTest
johngrimes Apr 11, 2022
65a2545
Add ComparisonOperatorDateTest and ComparisonOperatorTimeTest
johngrimes Apr 12, 2022
26022b0
Add comparison operator tests and refactor comparison implementation
johngrimes Apr 13, 2022
e4336e8
Make date arithmetic implementation tolerant of no time zone
johngrimes Apr 13, 2022
9b9d234
Align arithmetic implementation to HAPI functionality
johngrimes Apr 13, 2022
f854dd4
Merge branch 'main' into issue/340
johngrimes May 2, 2022
fcd9ede
Fix GroupingLiteralTest
johngrimes May 2, 2022
d3deb52
Add DateTimeArithmeticParserTest
johngrimes May 2, 2022
589f1f7
Add QuantityParserTest and MathOperatorQuantityTest#volumeArithmetic
johngrimes May 3, 2022
843a7f3
Cater for scenario where UCUM expression can't be parsed
johngrimes May 4, 2022
b893856
Add Quantity canonicalization tests to encoders
johngrimes May 9, 2022
3d55704
Incomplete implementation of schema building and serialization
johngrimes May 10, 2022
68dc126
Added working implementation of Quantity serializer with canonical va…
piotrszul May 10, 2022
1d11552
Fix match condition within schema builder
johngrimes May 11, 2022
b9e0a2d
Add schema test for Quantity array
johngrimes May 19, 2022
69051a4
Add serialization test for Quantity array
johngrimes May 20, 2022
03d0264
Prefix canonicalized fields with underscore
johngrimes May 20, 2022
c864b92
Clean up fixed TODOs
johngrimes May 20, 2022
00350ee
Move UCUM essence into the main resources directory
johngrimes May 20, 2022
38d4da2
Use pre-baked canonicalized Quantities within FHIRPath functions
johngrimes May 20, 2022
6e9f049
Fix unit test dependencies
johngrimes May 20, 2022
1f23efb
Return null on UCUM exception, rather than throw
johngrimes May 20, 2022
dbd8b61
Fix QuantityParserTest#lengthObservationSubtraction
johngrimes May 20, 2022
c29710b
Implement UntilFunction
johngrimes May 21, 2022
543ec10
Add parser test for UntilFunction
johngrimes May 23, 2022
c5ad171
Add UntilFunctionTest and TemporalDifferenceFunctionTest
johngrimes May 23, 2022
501b443
Add missing assertion within ParserTest#testUntilFunction
johngrimes May 23, 2022
932b46b
Reformat POMs
johngrimes May 24, 2022
97c70e6
Merge branch 'main' into issue/340
johngrimes May 24, 2022
f7f2538
Merge branch 'main' into issue/340
johngrimes Jun 16, 2022
41554a8
Merge branch 'main' into issue/340
johngrimes Jul 10, 2022
720d862
Switch Ucum-java to snapshot build, and fix referential problems
johngrimes Jul 10, 2022
2e41aae
Fix problem with Javadoc on PathlingContext.create
johngrimes Jul 11, 2022
3e13c9d
Add snapshot repository to POM
johngrimes Jul 11, 2022
6cd6def
Fix trailing whitespace in DecimalCustomCoder
johngrimes Jul 11, 2022
4210371
Merge branch 'main' into issue/340
johngrimes Jul 11, 2022
88ed4d6
Fixing the timezone for unit and integration tests to UTC.
piotrszul Sep 1, 2022
066ea8d
Refactoring of quantity serialization.
piotrszul Sep 5, 2022
7171fe6
Fixing CodeFactor formatting issues.
piotrszul Sep 5, 2022
d74c98d
Refactoring for Quantity cannocalisation tests.
piotrszul Sep 5, 2022
9b4fccb
Refactoring comparison implementation to instances od Comparable.
piotrszul Sep 5, 2022
76cbd6f
Refactoring comparison implementation for DateTime types.
piotrszul Sep 6, 2022
731a77b
Small refactoring changes.
piotrszul Sep 6, 2022
44865d7
Refactoring: removed explicit creation of UFDs for unit-tests.
piotrszul Sep 6, 2022
e859884
Refactoring: extracting out calendar duration related functions.
piotrszul Sep 9, 2022
893073d
Refactoring: moved Quantity literal creation to QuantityEncoder.
piotrszul Sep 12, 2022
d2e9d18
Updating java docs
piotrszul Sep 13, 2022
af4ec10
Make 'until' function require singular object and argument (in the sa…
piotrszul Sep 13, 2022
587d8fd
Cleanup (removing unnecessary fields, adding notes)
piotrszul Sep 13, 2022
989c1e5
Added comparison and equality precision test.
piotrszul Sep 20, 2022
330fc2d
Benchmark for two flexible decimal implementations.
piotrszul Sep 20, 2022
ecd67be
Switched canonicalized value to FlexDecimal based on string.
piotrszul Oct 7, 2022
428f990
Switched canonicalized value to FlexiDecimal based on struct(Decimal,…
piotrszul Oct 12, 2022
e3794b4
Merge branch 'main' into issue/340
johngrimes Oct 13, 2022
1a8c935
Enable arbitrary configuration of Hadoop AWS through config variables
johngrimes Oct 14, 2022
9f4ee25
Update version to 6.0.0-SNAPSHOT and create pre-release workflow
johngrimes Oct 14, 2022
91570b4
Don't set latest tag upon deployment of pre-release image
johngrimes Oct 17, 2022
055ebe3
Merge branch 'release/6.0.0' into issue/340
johngrimes Oct 23, 2022
6877729
Add standard JVM args to benchmark execution
johngrimes Oct 24, 2022
2b9edfa
Fix failing tests
johngrimes Oct 24, 2022
ef1c64c
Revert "Add standard JVM args to benchmark execution"
johngrimes Oct 24, 2022
2a6de5d
Add JVM arguments to @Fork annotation on benchmarks
johngrimes Oct 24, 2022
356101a
Revert "Add JVM arguments to @Fork annotation on benchmarks"
johngrimes Oct 24, 2022
459ed1f
Add JVM arguments to options applied within PathlingBenchmarkRunner
johngrimes Oct 24, 2022
d2c85e4
Increase Xmx on benchmark to 6G
johngrimes Oct 24, 2022
7f309f6
Make DecimalBenchmark rows smaller, to fit into test runner memory
johngrimes Oct 24, 2022
d27bbc2
Add documentation for until function
johngrimes Oct 24, 2022
d9cb40b
Documentation updates: math operators, until function
johngrimes Oct 25, 2022
55dfb4e
Add test and validation for comparable operands to math operators
johngrimes Oct 25, 2022
f53228c
Exclude DecimalBenchmark from runner, and bring memory requirement ba…
johngrimes Oct 25, 2022
84b57c6
Fix inspections in FlexiDecimal
johngrimes Oct 25, 2022
4aa6708
Remove DecimalBenchmark and FlexDecimal
johngrimes Oct 25, 2022
ff55db8
Fix inspections
johngrimes Oct 25, 2022
fec8552
Reinstate DecimalBenchmark, disabled and with FlexDecimal removed
johngrimes Oct 25, 2022
f12660d
Fix trailing white space
johngrimes Oct 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow creates a pre-release Docker image.

name: Deploy pre-release Docker image

on:
push:
branches:
- 'release/*'

jobs:
deploy-docker:
name: Docker Hub
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
# This is required so that git-commit-id-plugin can find the latest tag.
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'zulu'
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Run the deploy goal with Maven
run: |
TAG=$(echo '${{ github.ref }}' | sed 's/refs\/heads\/release\///')
mvn --batch-mode deploy \
-pl fhir-server -am \
-PdockerPreRelease \
-Dpathling.fhirServerDockerTag=$TAG \
-DskipTests -DskipScalaDocs
timeout-minutes: 30
9 changes: 8 additions & 1 deletion encoders/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<parent>
<groupId>au.csiro.pathling</groupId>
<artifactId>pathling</artifactId>
<version>5.4.0</version>
<version>6.0.0-SNAPSHOT</version>
</parent>
<artifactId>encoders</artifactId>
<packaging>jar</packaging>
Expand Down Expand Up @@ -96,6 +96,13 @@
<scope>provided</scope>
</dependency>

<!-- UCUM -->
<dependency>
<groupId>au.csiro.pathling</groupId>
<artifactId>ucum</artifactId>
<version>1.0.4-SNAPSHOT</version>
</dependency>

<!-- Logging -->
<dependency>
<artifactId>logback-classic</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* This is a modified version of the Bunsen library, originally published at
* https://github.com/cerner/bunsen.
*
* Bunsen is copyright 2017 Cerner Innovation, Inc., and is licensed under
* the Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
*
* These modifications are copyright © 2018-2022, Commonwealth Scientific
* and Industrial Research Organisation (CSIRO) ABN 41 687 119 230. Licensed
* under the CSIRO Open Source Software Licence Agreement.
*
*/

package au.csiro.pathling.encoders.terminology.ucum;

import au.csiro.pathling.encoders.datatypes.DecimalCustomCoder;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import au.csiro.pathling.sql.types.FlexiDecimal;
import org.fhir.ucum.Decimal;
import org.fhir.ucum.Pair;
import org.fhir.ucum.UcumEssenceService;
import org.fhir.ucum.UcumException;
import org.fhir.ucum.UcumService;
import org.hl7.fhir.r4.model.DecimalType;

/**
* Makes UCUM services available to the rest of the application.
*
* @author John Grimes
*/
public class Ucum {

public static final String NO_UNIT_CODE = "1";

public static final String SYSTEM_URI = "http://unitsofmeasure.org";

private static final UcumService service;

static {
final InputStream essenceStream = Ucum.class.getClassLoader()
.getResourceAsStream("tx/ucum-essence.xml");
try {
service = new UcumEssenceService(essenceStream);
} catch (final UcumException e) {
throw new RuntimeException(e);
}
}

@Nonnull
public static UcumService service() throws UcumException {
return service;
}

@Nullable
public static BigDecimal getCanonicalValue(@Nullable final BigDecimal value,
@Nullable final String code) {
try {
@Nullable final Pair result = getCanonicalForm(value, code);
if (result == null) {
return null;
}
@Nullable final Decimal decimalValue = result.getValue();
if (decimalValue == null) {
return null;
}
@Nullable final String stringValue = decimalValue.asDecimal();
if (stringValue == null) {
return null;
}
return new BigDecimal(stringValue);
} catch (final UcumException e) {
return null;
}
}

@Nullable
public static String getCanonicalCode(@Nullable final BigDecimal value,
@Nullable final String code) {
try {
@Nullable final Pair result = getCanonicalForm(value, code);
if (result == null) {
return null;
}
return result.getCode();
} catch (final UcumException e) {
return null;
}
}

@Nullable
private static Pair getCanonicalForm(final @Nullable BigDecimal value,
final @Nullable String code)
throws UcumException {
if (value == null || code == null) {
return null;
}
final Decimal decimalValue = new Decimal(value.toPlainString());
return adjustNoUnitCode(service.getCanonicalForm(new Pair(decimalValue, code)));
}

@Nullable
private static Pair adjustNoUnitCode(@Nullable Pair pair) {
if (pair == null) {
return null;
}
return (pair.getCode() != null && pair.getCode().isEmpty())
? new Pair(pair.getValue(), NO_UNIT_CODE)
: pair;
}

}
208 changes: 208 additions & 0 deletions encoders/src/main/java/au/csiro/pathling/sql/types/FlexiDecimal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package au.csiro.pathling.sql.types;

import au.csiro.pathling.encoders.datatypes.DecimalCustomCoder;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.spark.sql.Column;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.api.java.UDF1;
import org.apache.spark.sql.api.java.UDF2;
import org.apache.spark.sql.expressions.UserDefinedFunction;
import org.apache.spark.sql.functions;
import org.apache.spark.sql.types.DataType;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.Decimal;
import org.apache.spark.sql.types.Metadata;
import org.apache.spark.sql.types.MetadataBuilder;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;

/**
* Implementation of flexible decimal type represented as the unscaled value with up to 38 digits
* and the scale.
*
* @author Piotr Szul
*/
public class FlexiDecimal {

/**
* The maximum precision (the number of significant digits).
*/
public static final int MAX_PRECISION = 38;
/**
* The SQL type for the unscaled value.
*/
public static final DataType DECIMAL_TYPE = DataTypes.createDecimalType(MAX_PRECISION, 0);

@Nonnull
private static StructType createFlexibleDecimalType() {
final Metadata metadata = new MetadataBuilder().build();
final StructField value = new StructField("value", DECIMAL_TYPE, true,
metadata);
final StructField scale = new StructField("scale", DataTypes.IntegerType, true, metadata);
return new StructType(new StructField[]{value, scale});
}

/**
* The SQL (struct) type for flexible decimal.
*/
@Nonnull
public static DataType DATA_TYPE = createFlexibleDecimalType();

@Nonnull
private static UserDefinedFunction toBooleanUdf(
@Nonnull final UDF2<BigDecimal, BigDecimal, Boolean> method) {
final UDF2<Row, Row, Boolean> f = (left, right) -> {
final BigDecimal leftValue = fromValue(left);
final BigDecimal rightValue = fromValue(right);
//noinspection ReturnOfNull
return (leftValue == null || rightValue == null)
? null
: method.call(leftValue, rightValue);
};
return functions.udf(f, DataTypes.BooleanType);
}

@Nonnull
private static UDF2<Row, Row, Row> wrapBigDecimal2(
@Nonnull final UDF2<BigDecimal, BigDecimal, BigDecimal> method) {
//noinspection ReturnOfNull
return (left, right) ->
(left == null || right == null)
? null
: toValue(method.call(fromValue(left), fromValue(right)));
}

@Nonnull
private static UserDefinedFunction toBigDecimalUdf(
@Nonnull final UDF2<BigDecimal, BigDecimal, BigDecimal> method) {
return functions.udf(wrapBigDecimal2(method), DATA_TYPE);
}

/**
* Decodes a flexible decimal from the Row
*
* @param row the row to decode
* @return the BigDecimal representation of the row
*/
@Nullable
public static BigDecimal fromValue(@Nullable final Row row) {
return row != null && !row.isNullAt(0)
? row.getDecimal(0).movePointLeft(row.getInt(1))
: null;
}

/**
* Encodes a flexible decimal into a Row
*
* @param decimal the decimal to encode
* @return the Row representation of the decimal
*/
@Nullable
public static Row toValue(@Nullable final BigDecimal decimal) {
final Object[] fieldValues = toArrayValue(decimal);
return fieldValues != null
? RowFactory.create(fieldValues)
: null;
}

@Nullable
private static Object[] toArrayValue(@Nullable final BigDecimal decimal) {
final BigDecimal normalizedValue = normalize(decimal);
return normalizedValue != null
? new Object[]{Decimal.apply(normalizedValue.unscaledValue()), normalizedValue.scale()}
: null;
}

@Nullable
public static BigDecimal normalize(@Nullable final BigDecimal decimal) {
if (decimal == null) {
return null;
} else {
final BigDecimal adjustedValue = decimal.scale() < 0
? decimal.setScale(0, RoundingMode.UNNECESSARY)
: decimal;
// This may be may have too many digits
if (adjustedValue.precision() > MAX_PRECISION) {
// we need to adjust the scale to fit into the desired precision
final int desiredScale =
adjustedValue.scale() - (adjustedValue.precision() - MAX_PRECISION);
if (desiredScale >= 0) {
return adjustedValue.setScale(desiredScale, RoundingMode.HALF_UP);
} else {
return null;
}
} else {
return adjustedValue;
}
}
}

private static final UserDefinedFunction EQUALS_UDF = toBooleanUdf((l, r) -> l.compareTo(r) == 0);
private static final UserDefinedFunction LT_UDF = toBooleanUdf((l, r) -> l.compareTo(r) < 0);
private static final UserDefinedFunction LTE_UDF = toBooleanUdf((l, r) -> l.compareTo(r) <= 0);
private static final UserDefinedFunction GT_UDF = toBooleanUdf((l, r) -> l.compareTo(r) > 0);
private static final UserDefinedFunction GTE_UDF = toBooleanUdf((l, r) -> l.compareTo(r) >= 0);

private static final UserDefinedFunction PLUS_UDF = toBigDecimalUdf(BigDecimal::add);
private static final UserDefinedFunction MULTIPLY_UDF = toBigDecimalUdf(BigDecimal::multiply);
private static final UserDefinedFunction MINUS_UDF = toBigDecimalUdf(BigDecimal::subtract);
private static final UserDefinedFunction DIVIDE_UDF = toBigDecimalUdf(BigDecimal::divide);

private static final UserDefinedFunction TO_DECIMAL = functions.udf(
(UDF1<Row, BigDecimal>) FlexiDecimal::fromValue,
DecimalCustomCoder.decimalType());

@Nonnull
public static Column equals(@Nonnull final Column left, @Nonnull final Column right) {
return EQUALS_UDF.apply(left, right);
}

@Nonnull
public static Column lt(@Nonnull final Column left, @Nonnull final Column right) {
return LT_UDF.apply(left, right);
}

@Nonnull
public static Column lte(@Nonnull final Column left, @Nonnull final Column right) {
return LTE_UDF.apply(left, right);
}

@Nonnull
public static Column gt(@Nonnull final Column left, @Nonnull final Column right) {
return GT_UDF.apply(left, right);
}

@Nonnull
public static Column gte(@Nonnull final Column left, @Nonnull final Column right) {
return GTE_UDF.apply(left, right);
}

@Nonnull
public static Column plus(@Nonnull final Column left, @Nonnull final Column right) {
return PLUS_UDF.apply(left, right);
}

@Nonnull
public static Column multiply(@Nonnull final Column left, @Nonnull final Column right) {
return MULTIPLY_UDF.apply(left, right);
}

@Nonnull
public static Column minus(@Nonnull final Column left, @Nonnull final Column right) {
return MINUS_UDF.apply(left, right);
}

@Nonnull
public static Column divide(@Nonnull final Column left, @Nonnull final Column right) {
return DIVIDE_UDF.apply(left, right);
}

@Nonnull
public static Column toDecimal(@Nonnull final Column flexiDecimal) {
return TO_DECIMAL.apply(flexiDecimal);
}
}
Loading