diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpLocation.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpLocation.java new file mode 100644 index 000000000..72d06680d --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpLocation.java @@ -0,0 +1,83 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.watersupply; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import cwms.cda.data.dto.CwmsId; + +@JsonDeserialize(builder = PumpLocation.Builder.class) +@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) +public final class PumpLocation { + private final CwmsId pumpIn; + private final CwmsId pumpOut; + private final CwmsId pumpBelow; + + private PumpLocation(Builder builder) { + this.pumpIn = builder.pumpIn; + this.pumpOut = builder.pumpOut; + this.pumpBelow = builder.pumpBelow; + } + + public CwmsId getPumpIn() { + return this.pumpIn; + } + + public CwmsId getPumpOut() { + return this.pumpOut; + } + + public CwmsId getPumpBelow() { + return this.pumpBelow; + } + + public static final class Builder { + private CwmsId pumpIn; + private CwmsId pumpOut; + private CwmsId pumpBelow; + + public Builder withPumpIn(CwmsId pumpIn) { + this.pumpIn = pumpIn; + return this; + } + + public Builder withPumpOut(CwmsId pumpOut) { + this.pumpOut = pumpOut; + return this; + } + + public Builder withPumpBelow(CwmsId pumpBelow) { + this.pumpBelow = pumpBelow; + return this; + } + + public PumpLocation build() { + return new PumpLocation(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpTransfer.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpTransfer.java new file mode 100644 index 000000000..019f90432 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/PumpTransfer.java @@ -0,0 +1,72 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.watersupply; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import cwms.cda.data.dto.CwmsDTOBase; + +@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) +public final class PumpTransfer extends CwmsDTOBase { + @JsonProperty(required = true) + private final PumpType pumpType; + @JsonProperty(required = true) + private final String transferTypeDisplay; + @JsonProperty(required = true) + private final Double flow; + @JsonProperty(required = true) + private final String comment; + + @JsonCreator + public PumpTransfer(@JsonProperty("pump-type") PumpType pumpType, + @JsonProperty("transfer-type-display") String transferTypeDisplay, + @JsonProperty("flow") Double flow, @JsonProperty("comment") String comment) { + this.transferTypeDisplay = transferTypeDisplay; + this.flow = flow; + this.comment = comment; + this.pumpType = pumpType; + } + + public String getTransferTypeDisplay() { + return this.transferTypeDisplay; + } + + public Double getFlow() { + return this.flow; + } + + public PumpType getPumpType() { + return this.pumpType; + } + + public String getComment() { + return this.comment; + } + +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java new file mode 100644 index 000000000..f57382b85 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java @@ -0,0 +1,115 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.watersupply; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import cwms.cda.data.dto.CwmsDTOBase; +import cwms.cda.formatters.Formats; +import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class, + aliases = {Formats.DEFAULT, Formats.JSON}) +@JsonDeserialize(builder = WaterSupplyAccounting.Builder.class) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) +@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) +public final class WaterSupplyAccounting extends CwmsDTOBase { + @JsonProperty(required = true) + private final String contractName; + @JsonProperty(required = true) + private final WaterUser waterUser; + @JsonProperty(required = true) + private final PumpLocation pumpLocations; + @Schema(name = "data-columns") + private final Map> pumpAccounting; + + private WaterSupplyAccounting(Builder builder) { + this.contractName = builder.contractName; + this.waterUser = builder.waterUser; + this.pumpLocations = builder.pumpLocations; + this.pumpAccounting = builder.pumpAccounting; + } + + public String getContractName() { + return this.contractName; + } + + public WaterUser getWaterUser() { + return this.waterUser; + } + + public Map> getPumpAccounting() { + return this.pumpAccounting; + } + + public PumpLocation getPumpLocations() { + return this.pumpLocations; + } + + + @JsonIgnoreProperties("data-columns") + public static final class Builder { + private String contractName; + private WaterUser waterUser; + private Map> pumpAccounting; + private PumpLocation pumpLocations; + + public Builder withContractName(String contractName) { + this.contractName = contractName; + return this; + } + + public Builder withWaterUser(WaterUser waterUser) { + this.waterUser = waterUser; + return this; + } + + public Builder withPumpAccounting( + Map> pumpAccounting) { + this.pumpAccounting = pumpAccounting; + return this; + } + + public Builder withPumpLocations( + PumpLocation pumpLocations) { + this.pumpLocations = pumpLocations; + return this; + } + + public WaterSupplyAccounting build() { + return new WaterSupplyAccounting(this); + } + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java new file mode 100644 index 000000000..e35499829 --- /dev/null +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/PumpAccountingTest.java @@ -0,0 +1,154 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.watersupply; + +import static org.junit.jupiter.api.Assertions.*; + +import cwms.cda.api.errors.FieldException; +import cwms.cda.data.dto.CwmsId; +import cwms.cda.formatters.Formats; +import cwms.cda.helpers.DTOMatch; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + + +class PumpAccountingTest { + private static final String OFFICE = "SPK"; + + @Test + void testWaterSupplyPumpAccountingSerializationRoundTrip() { + WaterSupplyAccounting pumpAccounting = buildTestAccounting(); + String serialized = Formats.format(Formats.parseHeader(Formats.JSONV1, WaterSupplyAccounting.class), + pumpAccounting); + WaterSupplyAccounting deserialized = Formats.parseContent(Formats.parseHeader(Formats.JSONV1, + WaterSupplyAccounting.class), serialized, WaterSupplyAccounting.class); + DTOMatch.assertMatch(pumpAccounting, deserialized); + } + + @Test + void testWaterSupplyPumpAccountingSerializationRoundTripFromFile() throws Exception { + WaterSupplyAccounting pumpAccounting = buildTestAccounting(); + InputStream resource = this.getClass().getResourceAsStream( + "/cwms/cda/data/dto/watersupply/water_pump_accounting.json"); + assertNotNull(resource); + String serialized = IOUtils.toString(resource, StandardCharsets.UTF_8); + WaterSupplyAccounting deserialized = Formats.parseContent(Formats.parseHeader(Formats.JSONV1, + WaterSupplyAccounting.class), serialized, WaterSupplyAccounting.class); + DTOMatch.assertMatch(pumpAccounting, deserialized); + } + + @Test + void testValidate() { + assertAll( + () -> { + WaterSupplyAccounting pumpAccounting = buildTestAccounting(); + assertDoesNotThrow(pumpAccounting::validate, "Expected validation to pass"); + }, + () -> { + PumpTransfer pumpTransfer = new PumpTransfer(null, "Test Transfer Type", 1.0, "Test Comment"); + assertThrows(FieldException.class, pumpTransfer::validate, "Expected validation to " + + "fail due to null pump type"); + }, + () -> { + PumpTransfer pumpTransfer = new PumpTransfer(PumpType.OUT, "Test Transfer Type 3", null, "Test Comment 3"); + assertThrows(FieldException.class, pumpTransfer::validate, "Expected validation to " + + "fail due to null flow value"); + }, + () -> { + PumpTransfer pumpTransfer = new PumpTransfer(PumpType.BELOW, null, 4.0, "Test Comment 4"); + assertThrows(FieldException.class, pumpTransfer::validate, "Expected validation to " + + "fail due to null transfer type display value"); + }, + () -> { + WaterSupplyAccounting pumpAccounting = new WaterSupplyAccounting.Builder().withPumpAccounting(buildTestPumpInAccountingList()) + .withPumpLocations(null).withContractName("Sacramento River Water Contract") + .withWaterUser(new WaterUser.Builder().withWaterRight("State of California Water Rights Permit #12345") + .withProjectId(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta") + .build()).withEntityName("California Department of Water Resources").build()).build(); + assertThrows(FieldException.class, pumpAccounting::validate, "Expected validation to " + + "fail due to null pump locations"); + }, + () -> { + WaterSupplyAccounting pumpAccounting = new WaterSupplyAccounting.Builder().withPumpAccounting(buildTestPumpInAccountingList()) + .withPumpLocations(buildTestPumpLocation()).withContractName(null) + .withWaterUser(new WaterUser.Builder().withWaterRight("State of California Water Rights Permit #12345") + .withProjectId(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta") + .build()).withEntityName("California Department of Water Resources").build()).build(); + assertThrows(FieldException.class, pumpAccounting::validate, "Expected validation to " + + "fail due to null contract name"); + }, + () -> { + WaterSupplyAccounting pumpAccounting = new WaterSupplyAccounting.Builder().withPumpAccounting(buildTestPumpInAccountingList()) + .withPumpLocations(buildTestPumpLocation()).withContractName("Sacramento River Water Contract") + .withWaterUser(null).build(); + assertThrows(FieldException.class, pumpAccounting::validate, "Expected validation to " + + "fail due to null water user"); + } + ); + } + + private WaterSupplyAccounting buildTestAccounting() { + return new WaterSupplyAccounting.Builder().withWaterUser(new WaterUser.Builder().withEntityName("California Department of Water Resources") + .withWaterRight("State of California Water Rights Permit #12345").withProjectId(new CwmsId.Builder().withOfficeId(OFFICE) + .withName("Sacramento River Delta").build()).build()).withPumpLocations(buildTestPumpLocation()) + .withContractName("Sacramento River Water Contract").withPumpAccounting(buildTestPumpInAccountingList()) + .build(); + } + + private Map> buildTestPumpInAccountingList() { + Map> retMap = new TreeMap<>(); + List pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN, "Pipeline", 1.0, "Added water to the system")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 2.0, "Removed excess water")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 3.0, "Daily water release")); + retMap.put(Instant.ofEpochMilli(1668979048000L), pumpMap); + pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN, "Pipeline", 4.0, "Pump transfer for the day")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 5.0, "Excess water transfer")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 6.0, "Water returned to the river")); + retMap.put(Instant.ofEpochMilli(1669065448000L), pumpMap); + pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN,"Pipeline", 7.0, "Pump transfer for the day")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 8.0, "Excess water transfer")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 9.0, "Water returned to the river")); + retMap.put(Instant.ofEpochMilli(1669151848000L), pumpMap); + return retMap; + } + + private PumpLocation buildTestPumpLocation() { + return new PumpLocation.Builder().withPumpIn(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 1").build()) + .withPumpOut(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 2").build()) + .withPumpBelow(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 3").build()).build(); + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/WaterSupplyAccountingTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/WaterSupplyAccountingTest.java new file mode 100644 index 000000000..253f43704 --- /dev/null +++ b/cwms-data-api/src/test/java/cwms/cda/data/dto/watersupply/WaterSupplyAccountingTest.java @@ -0,0 +1,182 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.watersupply; + +import static cwms.cda.helpers.DTOMatch.assertMatch; +import static org.junit.jupiter.api.Assertions.*; + +import cwms.cda.api.errors.FieldException; +import cwms.cda.data.dto.CwmsId; +import cwms.cda.formatters.Formats; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + + +class WaterSupplyAccountingTest { + private static final String OFFICE = "SPK"; + + @Test + void testWaterSupplyAccountingSerializationRoundTrip() { + WaterUser user = new WaterUser.Builder().withEntityName("California Department of Water Resources") + .withProjectId(new CwmsId.Builder() + .withOfficeId(OFFICE) + .withName("Sacramento River Delta") + .build()) + .withWaterRight("State of California Water Rights Permit #12345").build(); + WaterSupplyAccounting waterSupplyAccounting = new WaterSupplyAccounting.Builder() + .withWaterUser(user).withContractName("Sacramento River Water Contract").withPumpLocations( + new PumpLocation.Builder() + .withPumpIn(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 1").build()) + .withPumpOut(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 2").build()) + .withPumpBelow(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 3").build()) + .build()) + .withPumpAccounting(buildTestPumpAccountingList()) + .build(); + String serialized = Formats.format(Formats.parseHeader(Formats.JSONV1, WaterSupplyAccounting.class), + waterSupplyAccounting); + WaterSupplyAccounting deserialized = Formats.parseContent(Formats.parseHeader(Formats.JSONV1, + WaterSupplyAccounting.class), serialized, WaterSupplyAccounting.class); + assertMatch(waterSupplyAccounting, deserialized); + } + + @Test + void testWaterSupplyAccountingSerializationRoundTripFromFile() throws Exception { + WaterSupplyAccounting waterSupplyAccounting = new WaterSupplyAccounting.Builder() + .withWaterUser(new WaterUser.Builder() + .withEntityName("California Department of Water Resources") + .withProjectId(new CwmsId.Builder().withOfficeId(OFFICE) + .withName("Sacramento River Delta").build()) + .withWaterRight("State of California Water Rights Permit #12345").build()) + .withContractName("Sacramento River Water Contract") + .withPumpLocations(buildTestPumpLocation()) + .withPumpAccounting(buildTestPumpAccountingList()) + .build(); + InputStream resource = this.getClass().getResourceAsStream( + "/cwms/cda/data/dto/watersupply/water_pump_accounting.json"); + assertNotNull(resource); + String serialized = IOUtils.toString(resource, StandardCharsets.UTF_8); + WaterSupplyAccounting deserialized = Formats.parseContent(Formats.parseHeader(Formats.JSONV1, + WaterSupplyAccounting.class), serialized, WaterSupplyAccounting.class); + assertMatch(waterSupplyAccounting, deserialized); + } + + @Test + void testBuildWSA() { + WaterUser user = new WaterUser.Builder().withEntityName("California Department of Water Resources") + .withProjectId(new CwmsId.Builder() + .withOfficeId(OFFICE) + .withName("Sacramento River Delta") + .build()) + .withWaterRight("State of California Water Rights Permit #12345").build(); + + WaterSupplyAccounting wsa = new WaterSupplyAccounting.Builder() + .withPumpLocations(buildTestPumpLocation()) + .withContractName("Sacramento River Water Contract") + .withPumpAccounting(buildTestPumpAccountingList()) + .withWaterUser(user) + .build(); + assertAll( + () -> assertMatch(buildTestPumpLocation(), wsa.getPumpLocations()), + () -> assertMatch(buildTestPumpAccountingList(), wsa.getPumpAccounting()), + () -> assertMatch(user, wsa.getWaterUser()) + ); + assertDoesNotThrow(wsa::validate, "Expected validation to pass"); + } + + + @Test + void testValidate() { + assertAll( + () -> { + WaterSupplyAccounting waterSupplyAccounting = new WaterSupplyAccounting.Builder() + .withWaterUser(new WaterUser.Builder() + .withEntityName("California Department of Water Resources") + .withProjectId(new CwmsId.Builder().withOfficeId(OFFICE) + .withName("Sacramento River Delta").build()) + .withWaterRight("State of California Water Rights Permit #12345").build()) + .withContractName("Sacramento River Water Contract") + .withPumpAccounting(buildTestPumpAccountingList()) + .withPumpLocations(buildTestPumpLocation()) + .build(); + assertDoesNotThrow(waterSupplyAccounting::validate, "Expected validation to pass"); + }, + () -> { + WaterSupplyAccounting waterSupplyAccounting = new WaterSupplyAccounting.Builder() + .withWaterUser(new WaterUser.Builder() + .withEntityName("California Department of Water Resources") + .withProjectId(new CwmsId.Builder().withOfficeId(OFFICE) + .withName("Sacramento River Delta").build()) + .withWaterRight("State of California Water Rights Permit #12345").build()) + .withContractName(null) + .build(); + assertThrows(FieldException.class, waterSupplyAccounting::validate, "Expected validation to " + + "fail due to null contract name"); + }, + () -> { + WaterSupplyAccounting waterSupplyAccounting = new WaterSupplyAccounting.Builder() + .withContractName("Sacramento River Water Contract") + .build(); + assertThrows(FieldException.class, waterSupplyAccounting::validate, "Expected validation to " + + "fail due to null water user"); + } + ); + } + + + private Map> buildTestPumpAccountingList() { + Map> retMap = new TreeMap<>(); + List pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN, "Pipeline", 1.0, "Added water to the system")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 2.0, "Removed excess water")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 3.0, "Daily water release")); + retMap.put(Instant.ofEpochMilli(1668979048000L), pumpMap); + pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN, "Pipeline", 4.0, "Pump transfer for the day")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 5.0, "Excess water transfer")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 6.0, "Water returned to the river")); + retMap.put(Instant.ofEpochMilli(1669065448000L), pumpMap); + pumpMap = new ArrayList<>(); + pumpMap.add(new PumpTransfer(PumpType.IN,"Pipeline", 7.0, "Pump transfer for the day")); + pumpMap.add(new PumpTransfer(PumpType.OUT, "Pipeline", 8.0, "Excess water transfer")); + pumpMap.add(new PumpTransfer(PumpType.BELOW, "River", 9.0, "Water returned to the river")); + retMap.put(Instant.ofEpochMilli(1669151848000L), pumpMap); + return retMap; + } + + private PumpLocation buildTestPumpLocation() { + return new PumpLocation.Builder().withPumpIn(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 1").build()) + .withPumpOut(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 2").build()) + .withPumpBelow(new CwmsId.Builder().withOfficeId(OFFICE).withName("Sacramento River Delta-Dam Water Pump 3").build()).build(); + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java b/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java index ab6d4f7bc..9d7ccff38 100644 --- a/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java +++ b/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java @@ -51,11 +51,17 @@ import cwms.cda.data.dto.stream.StreamLocation; import cwms.cda.data.dto.stream.StreamNode; import cwms.cda.data.dto.stream.StreamReach; +import cwms.cda.data.dto.watersupply.PumpLocation; +import cwms.cda.data.dto.watersupply.PumpTransfer; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; import cwms.cda.data.dto.watersupply.WaterSupplyPump; import cwms.cda.data.dto.watersupply.WaterUser; import cwms.cda.data.dto.watersupply.WaterUserContract; + +import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.BiPredicate; import java.util.stream.IntStream; @@ -362,6 +368,63 @@ private static void assertMatch(Double first, Double second, String message) { assertMatch(first, second, DEFAULT_DELTA, message); } + public static void assertMatch(WaterSupplyAccounting first, WaterSupplyAccounting second) { + assertAll( + () -> assertEquals(first.getContractName(), second.getContractName()), + () -> assertMatch(first.getWaterUser(), second.getWaterUser()), + () -> assertMatch(first.getPumpAccounting(), second.getPumpAccounting()), + () -> assertMatch(first.getPumpLocations(), second.getPumpLocations()) + ); + } + + public static void assertMatch(Map> first, Map> second) { + assertAll( + () -> assertEquals(first.size(), second.size(), "Pump accounting sizes do not match"), + () -> first.forEach((key, value) -> { + List secondValue = second.get(key); + if (secondValue == null) { + fail("Pump accounting key not found: " + key); + } + assertMatch(value, secondValue, DTOMatch::assertMatch); + }) + ); + } + + public static void assertMatch(PumpLocation first, PumpLocation second) { + assertAll( + () -> { + if (first != null && second != null) { + assertMatch(first.getPumpOut(), second.getPumpOut()); + } else if (!(first == null && second == null)) { + fail("Pump out locations do not match"); + } + }, + () -> { + if (first != null && second != null) { + assertMatch(first.getPumpIn(), second.getPumpIn()); + } else if (!(first == null && second == null)) { + fail("Pump in locations do not match"); + } + }, + () -> { + if (first != null && second != null) { + assertMatch(first.getPumpBelow(), second.getPumpBelow()); + } else if (!(first == null && second == null)) { + fail("Pump below locations do not match"); + } + } + ); + } + + private static void assertMatch(PumpTransfer first, PumpTransfer second) { + assertAll( + () -> assertEquals(first.getTransferTypeDisplay(), second.getTransferTypeDisplay()), + () -> assertEquals(first.getFlow(), second.getFlow()), + () -> assertEquals(first.getComment(), second.getComment()), + () -> assertEquals(first.getPumpType(), second.getPumpType()) + ); + } + public static void assertContainsDto(List values, T expectedDto, BiPredicate identifier, AssertMatchMethod dtoMatcher, @@ -506,7 +569,6 @@ public static void assertMatch(LockLocationLevelRef first, LockLocationLevelRef ); } - @FunctionalInterface public interface AssertMatchMethod{ void assertMatch(T first, T second); diff --git a/cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/water_pump_accounting.json b/cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/water_pump_accounting.json new file mode 100644 index 000000000..8f119410b --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/data/dto/watersupply/water_pump_accounting.json @@ -0,0 +1,87 @@ +{ + "contract-name": "Sacramento River Water Contract", + "water-user": { + "entity-name": "California Department of Water Resources", + "project-id": { + "office-id": "SPK", + "name": "Sacramento River Delta" + }, + "water-right": "State of California Water Rights Permit #12345" + }, + "pump-locations": { + "pump-in": { + "office-id": "SPK", + "name": "Sacramento River Delta-Dam Water Pump 1" + }, + "pump-out": { + "office-id": "SPK", + "name": "Sacramento River Delta-Dam Water Pump 2" + }, + "pump-below": { + "office-id": "SPK", + "name": "Sacramento River Delta-Dam Water Pump 3" + } + }, + "pump-accounting": { + "2022-11-20T21:17:28Z": [ + { + "pump-type": "IN", + "transfer-type-display": "Pipeline", + "flow": 1.0, + "comment": "Added water to the system" + }, + { + "pump-type": "OUT", + "transfer-type-display": "Pipeline", + "flow": 2.0, + "comment": "Removed excess water" + }, + { + "pump-type": "BELOW", + "transfer-type-display": "River", + "flow": 3.0, + "comment": "Daily water release" + } + ], + "2022-11-21T21:17:28Z": [ + { + "pump-type": "IN", + "transfer-type-display": "Pipeline", + "flow": 4.0, + "comment": "Pump transfer for the day" + }, + { + "pump-type": "OUT", + "transfer-type-display": "Pipeline", + "flow": 5.0, + "comment": "Excess water transfer" + }, + { + "pump-type": "BELOW", + "transfer-type-display": "River", + "flow": 6.0, + "comment": "Water returned to the river" + } + ], + "2022-11-22T21:17:28Z": [ + { + "pump-type": "IN", + "transfer-type-display": "Pipeline", + "flow": 7.0, + "comment": "Pump transfer for the day" + }, + { + "pump-type": "OUT", + "transfer-type-display": "Pipeline", + "flow": 8.0, + "comment": "Excess water transfer" + }, + { + "pump-type": "BELOW", + "transfer-type-display": "River", + "flow": 9.0, + "comment": "Water returned to the river" + } + ] + } +} \ No newline at end of file