diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationLevelsDaoImpl.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationLevelsDaoImpl.java index bdfe4dd9e..8e44db413 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationLevelsDaoImpl.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationLevelsDaoImpl.java @@ -37,13 +37,16 @@ import cwms.cda.api.enums.VersionType; import cwms.cda.api.errors.NotFoundException; import cwms.cda.data.dto.CwmsDTOPaginated; +import cwms.cda.data.dto.TimeSeries; import cwms.cda.data.dto.catalog.LocationAlias; import cwms.cda.data.dto.locationlevel.ConstantLocationLevel; import cwms.cda.data.dto.locationlevel.LocationLevel; import cwms.cda.data.dto.locationlevel.LocationLevels; import cwms.cda.data.dto.locationlevel.SeasonalLocationLevel; import cwms.cda.data.dto.locationlevel.SeasonalValueBean; -import cwms.cda.data.dto.TimeSeries; +import cwms.cda.data.dto.locationlevel.TimeSeriesLocationLevel; +import cwms.cda.data.dto.locationlevel.VirtualLocationLevel; +import cwms.cda.formatters.UnsupportedFormatException; import hec.data.Duration; import hec.data.Parameter; import hec.data.ParameterType; @@ -73,9 +76,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; -import cwms.cda.data.dto.locationlevel.TimeSeriesLocationLevel; -import cwms.cda.data.dto.locationlevel.VirtualLocationLevel; -import cwms.cda.formatters.UnsupportedFormatException; import mil.army.usace.hec.metadata.Interval; import mil.army.usace.hec.metadata.IntervalFactory; import mil.army.usace.hec.metadata.constants.NumericalConstants; @@ -783,25 +783,31 @@ private void parseLevels(Record r, Map build JDomSeasonalIntervalImpl newSeasonalOffset = buildSeasonalOffset(calOffset, timeOffset); SeasonalValueBean seasonalValue = buildSeasonalValueBean(seasonalLevel, newSeasonalOffset); - SeasonalLocationLevel.Builder seasonalBuilder = new SeasonalLocationLevel.Builder(locLevelId, levelZdt); - seasonalBuilder.withSeasonalValue(seasonalValue); - seasonalBuilder.withInterpolateString(interp); - if (timeInterval != null) { - seasonalBuilder.withIntervalMinutes(timeInterval.getMinutes()); + if (builderMap.containsKey(levelLookup)) { + SeasonalLocationLevel.Builder existingBuilder = (SeasonalLocationLevel.Builder) builderMap.get(levelLookup); + existingBuilder.withSeasonalValue(seasonalValue); + builderMap.put(levelLookup, existingBuilder); + } else { + SeasonalLocationLevel.Builder seasonalBuilder = new SeasonalLocationLevel.Builder(locLevelId, levelZdt); + seasonalBuilder.withSeasonalValue(seasonalValue); + seasonalBuilder.withInterpolateString(interp); + if (timeInterval != null) { + seasonalBuilder.withIntervalMinutes(timeInterval.getMinutes()); + } + seasonalBuilder.withAttributeParameterId(attrId); + seasonalBuilder.withAttributeUnitsId(attrUnit); + seasonalBuilder.withLevelUnitsId(levelUnit); + seasonalBuilder.withLevelComment(levelComment); + seasonalBuilder.withAttributeComment(attributeComment); + seasonalBuilder = withLocationLevelRef(seasonalBuilder, locationLevelRef); + JDomSeasonalIntervalImpl offset = new JDomSeasonalIntervalImpl(); + offset.setYearMonthString(calendarInterval); + seasonalBuilder.withIntervalMonths(offset.getMonths()); + seasonalBuilder.withIntervalOrigin(intervalOrigin, levelZdt); + seasonalBuilder.withAliases(aliases); + seasonalBuilder.withExpirationDate(expireDate); + builderMap.put(levelLookup, seasonalBuilder); } - seasonalBuilder.withAttributeParameterId(attrId); - seasonalBuilder.withAttributeUnitsId(attrUnit); - seasonalBuilder.withLevelUnitsId(levelUnit); - seasonalBuilder.withLevelComment(levelComment); - seasonalBuilder.withAttributeComment(attributeComment); - seasonalBuilder = withLocationLevelRef(seasonalBuilder, locationLevelRef); - JDomSeasonalIntervalImpl offset = new JDomSeasonalIntervalImpl(); - offset.setYearMonthString(calendarInterval); - seasonalBuilder.withIntervalMonths(offset.getMonths()); - seasonalBuilder.withIntervalOrigin(intervalOrigin, levelZdt); - seasonalBuilder.withAliases(aliases); - seasonalBuilder.withExpirationDate(expireDate); - builderMap.put(levelLookup, seasonalBuilder); } else if (tsId != null) { TimeSeriesLocationLevel.Builder timeSeriesBuilder = new TimeSeriesLocationLevel.Builder(locLevelId, levelZdt, tsId); timeSeriesBuilder.withAttributeParameterId(attrId); @@ -1172,7 +1178,7 @@ public Field getConnections() { @Override public Field getExpirationDate() { - return AV_VIRTUAL_LOCATION_LEVEL.EXPIRATION_DATE_UTC; + return AV_LOCATION_LEVEL.EXPIRATION_DATE; } @Override @@ -1314,7 +1320,7 @@ public Field getConnections() { @Override public Field getExpirationDate() { - return DSL.field(DSL.name(TABLE_ALIAS2, "EXPIRATION_DATE_UTC"), Timestamp.class); + return DSL.field(DSL.name(TABLE_ALIAS2, "EXPIRATION_DATE"), Timestamp.class); } @Override @@ -1403,13 +1409,13 @@ private static void buildLocationLevelSelectFields() { LOCATION_LEVEL_FIELDS.add(virtView.EFFECTIVE_DATE_UTC); LOCATION_LEVEL_FIELDS.add(virtView.CONNECTIONS); LOCATION_LEVEL_FIELDS.add(virtView.DURATION_ID); - LOCATION_LEVEL_FIELDS.add(virtView.EXPIRATION_DATE_UTC); LOCATION_LEVEL_FIELDS.add(virtView.ATTR_UNIT_EN); LOCATION_LEVEL_FIELDS.add(virtView.ATTR_VALUE_EN); LOCATION_LEVEL_FIELDS.add(virtView.ATTR_UNIT_SI); LOCATION_LEVEL_FIELDS.add(virtView.ATTR_VALUE_SI); LOCATION_LEVEL_FIELDS.add(view.OFFICE_ID); LOCATION_LEVEL_FIELDS.add(view.LOCATION_LEVEL_ID); + LOCATION_LEVEL_FIELDS.add(view.EXPIRATION_DATE); LOCATION_LEVEL_FIELDS.add(view.LEVEL_DATE); LOCATION_LEVEL_FIELDS.add(view.TSID); LOCATION_LEVEL_FIELDS.add(view.CONSTANT_LEVEL); @@ -1440,12 +1446,12 @@ private static void buildAliasedLocationLevelSelectFields() { LOCATION_ALIAS_FIELDS.add(field(virtView.EFFECTIVE_DATE_UTC.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(virtView.CONNECTIONS.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(DSL.name(TABLE_ALIAS1, "DURATION_ID"))); - LOCATION_ALIAS_FIELDS.add(field(virtView.EXPIRATION_DATE_UTC.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(virtView.ATTR_UNIT_EN.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(virtView.ATTR_VALUE_EN.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(virtView.ATTR_UNIT_SI.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(virtView.ATTR_VALUE_SI.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(view.LEVEL_DATE.getUnqualifiedName())); + LOCATION_ALIAS_FIELDS.add(field(view.EXPIRATION_DATE.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(view.TSID.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(view.CONSTANT_LEVEL.getUnqualifiedName())); LOCATION_ALIAS_FIELDS.add(field(view.INTERVAL_ORIGIN.getUnqualifiedName())); diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/SeasonalLocationLevel.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/SeasonalLocationLevel.java index d91bd1a32..9078254c8 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/SeasonalLocationLevel.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/SeasonalLocationLevel.java @@ -26,13 +26,6 @@ package cwms.cda.data.dto.locationlevel; -import java.math.BigInteger; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -50,13 +43,16 @@ import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; import cwms.cda.formatters.xml.XMLv2; -import io.swagger.v3.oas.annotations.media.Schema; - import hec.data.level.IParameterTypedValue; import hec.data.level.ISeasonalInterval; import hec.data.level.ISeasonalValue; import hec.data.level.ISeasonalValues; -import hec.data.level.JDomLocationLevelImpl; +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigInteger; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import rma.util.RMAConst; @JsonRootName("SeasonalLocationLevel") diff --git a/cwms-data-api/src/test/java/cwms/cda/api/LevelsControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/LevelsControllerTestIT.java index fcc1c812e..e6e8a575c 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/LevelsControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/LevelsControllerTestIT.java @@ -1611,6 +1611,7 @@ void testStoreDeleteVirtualLocationLevel(String deletionMethod) throws Exception assertThat(response.path("constituents[0].attribute-id"), equalTo("Stage")); assertThat(response.path("constituents[1].name"), equalTo(existingSpec)); assertThat(response.path("constituents[1].type"), equalTo("RATING")); + assertThat(response.path("expiration-date"), equalTo(Instant.ofEpochMilli(1685689200000L).toString())); // Delete the level switch (deletionMethod) { @@ -1780,6 +1781,91 @@ void testStoreSeasonalLevel() throws Exception { .body("seasonal-values.size()", is(numValues)); } + @Test + void testRetrieveAllSeasonalLevel() throws Exception { + String locName = "seasonalLoc18"; + createLocation(locName, true, OFFICE); + String levelId = String.format("%s.Elev.Ave.1Day.Top of Spillway", locName); + ZonedDateTime intervalOrigin = ZonedDateTime.ofInstant(Instant.parse("2012-01-01T00:00:00Z"), ZoneId.of("UTC")); + ZonedDateTime levelDate = ZonedDateTime.ofInstant(Instant.parse("2024-01-01T00:00:00Z"), ZoneId.of("UTC")); + List values = new ArrayList<>(); + int numValues = 12; + for (int i = 1; i <= numValues; i++) { + values.add(new SeasonalValueBean.Builder() + .withValue(i + 1.0) + .withOffsetMonths(i) + .build()); + } + SeasonalLocationLevel level = new SeasonalLocationLevel.Builder(levelId, levelDate) + .withOfficeId(OFFICE) + .withLevelUnitsId("ft") + .withIntervalMonths(12) + .withIntervalOrigin(intervalOrigin) + .withSeasonalValues(values) + .withInterpolateString("T") + .withExpirationDate(levelDate.plusYears(50)) + .build(); + + String levelJson = Formats.format(new ContentType(Formats.JSONV2), level); + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .queryParam(Controllers.OFFICE, OFFICE) + .header("Authorization", TestAccounts.KeyUser.SPK_NORMAL.toHeaderValue()) + .body(levelJson) + .contentType(Formats.JSONV2) + .when() + .redirects() + .follow(true) + .redirects() + .max(3) + .post("/levels/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)) + .body(OFFICE_ID, equalTo(OFFICE)) + .body(MESSAGE, equalTo("Created Location Level")) + .body(IDENTIFIER, equalTo(levelId)); + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .queryParam(Controllers.OFFICE, OFFICE) + .queryParam(EFFECTIVE_DATE, levelDate.toInstant().toString()) + .when() + .redirects() + .follow(true) + .redirects() + .max(3) + .get("/levels/{level-id}", levelId) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("expiration-date", equalTo(levelDate.plusYears(50).toInstant().toString())) + .body("seasonal-values.size()", is(numValues)); + + given() + .log().ifValidationFails(LogDetail.ALL, true) + .queryParam(Controllers.OFFICE, OFFICE) + .queryParam(EFFECTIVE_DATE, levelDate.toInstant().toString()) + .when() + .redirects() + .follow(true) + .redirects() + .max(3) + .queryParam(LEVEL_ID_MASK, levelId) + .get("/levels/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("levels.size()", is(1)) + .body("levels[0].expiration-date", equalTo(levelDate.plusYears(50).toInstant().toString())) + .body("levels[0].seasonal-values.size()", is(numValues)) + .body("total", is(numValues)); + } + @Test void testStoreTimeSeriesLevel() throws Exception { String locName = "tsLocation123";