Skip to content

Commit

Permalink
Addresses Issue #599, LocationLevel return over time for constant val…
Browse files Browse the repository at this point in the history
…ues. Includes tests to verify functionality
  • Loading branch information
zack-rma committed Sep 28, 2024
1 parent 7f3ebaf commit dacc6fb
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 85 deletions.
68 changes: 26 additions & 42 deletions cwms-data-api/src/main/java/cwms/cda/api/LevelsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,6 @@

package cwms.cda.api;

import static com.codahale.metrics.MetricRegistry.name;
import static cwms.cda.api.Controllers.BEGIN;
import static cwms.cda.api.Controllers.CASCADE_DELETE;
import static cwms.cda.api.Controllers.CREATE;
import static cwms.cda.api.Controllers.DATE;
import static cwms.cda.api.Controllers.DATUM;
import static cwms.cda.api.Controllers.DELETE;
import static cwms.cda.api.Controllers.EFFECTIVE_DATE;
import static cwms.cda.api.Controllers.END;
import static cwms.cda.api.Controllers.FORMAT;
import static cwms.cda.api.Controllers.GET_ALL;
import static cwms.cda.api.Controllers.GET_ONE;
import static cwms.cda.api.Controllers.LEVEL_ID;
import static cwms.cda.api.Controllers.LEVEL_ID_MASK;
import static cwms.cda.api.Controllers.NAME;
import static cwms.cda.api.Controllers.OFFICE;
import static cwms.cda.api.Controllers.PAGE;
import static cwms.cda.api.Controllers.PAGE_SIZE;
import static cwms.cda.api.Controllers.RESULTS;
import static cwms.cda.api.Controllers.SIZE;
import static cwms.cda.api.Controllers.STATUS_200;
import static cwms.cda.api.Controllers.TIMEZONE;
import static cwms.cda.api.Controllers.UNIT;
import static cwms.cda.api.Controllers.UPDATE;
import static cwms.cda.api.Controllers.VERSION;
import static cwms.cda.api.Controllers.addDeprecatedContentTypeWarning;
import static cwms.cda.api.Controllers.queryParamAsClass;
import static cwms.cda.api.Controllers.requiredParam;
import static cwms.cda.data.dao.JooqDao.getDslContext;

import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
Expand All @@ -67,7 +37,6 @@
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import cwms.cda.api.enums.UnitSystem;
import cwms.cda.api.errors.CdaError;
import cwms.cda.data.dao.LocationLevelsDao;
import cwms.cda.data.dao.LocationLevelsDaoImpl;
import cwms.cda.data.dto.LocationLevel;
Expand All @@ -89,13 +58,19 @@
import io.javalin.plugin.openapi.annotations.OpenApiParam;
import io.javalin.plugin.openapi.annotations.OpenApiRequestBody;
import io.javalin.plugin.openapi.annotations.OpenApiResponse;
import org.jetbrains.annotations.NotNull;
import org.jooq.DSLContext;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.jooq.DSLContext;

import static com.codahale.metrics.MetricRegistry.name;
import static cwms.cda.api.Controllers.*;
import static cwms.cda.data.dao.JooqDao.getDslContext;


public class LevelsController implements CrudHandler {
Expand Down Expand Up @@ -280,8 +255,7 @@ public void getAll(@NotNull Context ctx) {

boolean isLegacyVersion = version.equals("1");

if (format.isEmpty() && !isLegacyVersion)
{
if (format.isEmpty() && !isLegacyVersion) {
String cursor = ctx.queryParamAsClass(PAGE, String.class)
.getOrDefault("");
int pageSize = ctx.queryParamAsClass(PAGE_SIZE, Integer.class)
Expand Down Expand Up @@ -315,12 +289,10 @@ public void getAll(@NotNull Context ctx) {
ctx.status(HttpServletResponse.SC_OK);
ctx.result(results);
requestResultSize.update(results.length());
if (isLegacyVersion)
{
if (isLegacyVersion) {
ctx.contentType(contentType.toString());
}
else
{
else {
ctx.contentType(contentType.getType());
}
}
Expand All @@ -344,6 +316,12 @@ public void getAll(@NotNull Context ctx) {
+ "specified), as well as the time zone of any times in the response."
+ " If this field is not specified, the default time zone of UTC "
+ "shall be used."),
@OpenApiParam(name = START, type = Instant.class, description = "Specifies the start of the time "
+ "window for data to be included in the response. Both this field and the end field must be "
+ " specified, or no time window will be used."),
@OpenApiParam(name = END, type = Instant.class, description = "Specifies the end of the time "
+ "window for data to be included in the response. Both this field and the start field must be"
+ " specified, or no time window will be used."),
@OpenApiParam(name = UNIT, description = "Desired unit for "
+ "the values retrieved.")
},
Expand All @@ -362,16 +340,22 @@ public void getOne(@NotNull Context ctx, @NotNull String levelId) {
String dateString = queryParamAsClass(ctx, new String[]{EFFECTIVE_DATE, DATE},
String.class, null, metrics, name(LevelsController.class.getName(),
GET_ONE));
Instant start = ctx.queryParamAsClass(START, Long.class).getOrDefault(null) == null ? null
: Instant.ofEpochMilli(ctx.queryParamAsClass(START, Long.class).get());
Instant end = ctx.queryParamAsClass(END, Long.class).getOrDefault(null) == null ? null
: Instant.ofEpochMilli(ctx.queryParamAsClass(END, Long.class).getOrDefault(null));
String timezone = ctx.queryParamAsClass(TIMEZONE, String.class)
.getOrDefault("UTC");

try (final Timer.Context ignored = markAndTime(GET_ONE)) {
DSLContext dsl = getDslContext(ctx);
ZonedDateTime unmarshalledDateTime = DateUtils.parseUserDate(dateString, timezone);
ZonedDateTime startZdt = start == null ? null : ZonedDateTime.ofInstant(start, ZoneId.of(timezone));
ZonedDateTime endZdt = end == null ? null : ZonedDateTime.ofInstant(end, ZoneId.of(timezone));

LocationLevelsDao levelsDao = getLevelsDao(dsl);
LocationLevel locationLevel = levelsDao.retrieveLocationLevel(levelId,
units, unmarshalledDateTime, office);
units, unmarshalledDateTime, office, startZdt, endZdt);
ctx.json(locationLevel);
ctx.status(HttpServletResponse.SC_OK);
}
Expand Down Expand Up @@ -429,7 +413,7 @@ public void update(@NotNull Context ctx, @NotNull String oldLevelId) {
ZoneId.systemDefault().getId());
//retrieveLocationLevel will throw an error if level does not exist
LocationLevel existingLevelLevel = levelsDao.retrieveLocationLevel(oldLevelId,
UnitSystem.EN.getValue(), unmarshalledDateTime, officeId);
UnitSystem.EN.getValue(), unmarshalledDateTime, officeId, null, null);
existingLevelLevel = updatedClearedFields(ctx.body(), contentType.getType(),
existingLevelLevel);
//only store (update) if level does exist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import mil.army.usace.hec.metadata.Interval;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public interface LocationLevelsDao {
Expand All @@ -43,7 +42,7 @@ void deleteLocationLevel(String locationLevelName, ZonedDateTime date, String of
void renameLocationLevel(String oldLocationLevelName, String newLocationLevelName, String officeId);

LocationLevel retrieveLocationLevel(String locationLevelName, String unitSystem,
ZonedDateTime effectiveDate, String officeId);
ZonedDateTime effectiveDate, String officeId, ZonedDateTime start, ZonedDateTime end);

String getLocationLevels(String format, String names, String office, String unit,
String datum, String begin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
Expand All @@ -62,7 +63,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
Expand All @@ -75,6 +75,7 @@
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Result;
import org.jooq.SelectLimitPercentAfterOffsetStep;
import org.jooq.TableField;
import org.jooq.conf.ParamType;
Expand Down Expand Up @@ -242,13 +243,12 @@ private static SEASONAL_VALUE_TAB_T getSeasonalValues(LocationLevel locationLeve
List<SeasonalValueBean> seasonalValues = locationLevel.getSeasonalValues();

SEASONAL_VALUE_TAB_T pSeasonalValues = null;
if(seasonalValues != null && !seasonalValues.isEmpty()) {
if (seasonalValues != null && !seasonalValues.isEmpty()) {
pSeasonalValues = new SEASONAL_VALUE_TAB_T();
for(SeasonalValueBean seasonalValue : seasonalValues)
{
for (SeasonalValueBean seasonalValue : seasonalValues) {
SEASONAL_VALUE_T seasonalValueT = new SEASONAL_VALUE_T();
seasonalValueT.setOFFSET_MINUTES(toBigDecimal(seasonalValue.getOffsetMinutes()));
if(seasonalValue.getOffsetMonths() != null) {
if (seasonalValue.getOffsetMonths() != null) {
seasonalValueT.setOFFSET_MONTHS(seasonalValue.getOffsetMonths().byteValue());
}
seasonalValueT.setVALUE(toBigDecimal(seasonalValue.getValue()));
Expand Down Expand Up @@ -325,41 +325,101 @@ public void renameLocationLevel(String oldLocationLevelName, String newLocationL

@Override
public LocationLevel retrieveLocationLevel(String locationLevelName, String pUnits,
ZonedDateTime effectiveDate, String officeId) {
ZonedDateTime effectiveDate, String officeId, ZonedDateTime start, ZonedDateTime end) {
Timestamp date = Timestamp.from(effectiveDate.toInstant());
Timestamp startDate = start == null ? null : Timestamp.from(start.toInstant());
Timestamp endDate = end == null ? null : Timestamp.from(end.toInstant());
return connectionResult(dsl, c -> {
String units = pUnits;
Configuration configuration = getDslContext(c, officeId).configuration();
RETRIEVE_LOCATION_LEVEL3 level = CWMS_LEVEL_PACKAGE.call_RETRIEVE_LOCATION_LEVEL3(
configuration, locationLevelName, units, date,
"UTC", null, null, units,
"F", officeId);
List<SeasonalValueBean> seasonalValues = buildSeasonalValues(level);
if (units == null) {
String parameter = locationLevelName.split("\\.")[1];
logger.info("Getting default units for " + parameter);
String defaultUnits = CWMS_UTIL_PACKAGE.call_GET_DEFAULT_UNITS(
configuration, parameter, UnitSystem.SI.getValue());
logger.info("Default units are " + defaultUnits);
units = defaultUnits;

if (start != null && end != null) {

ZoneId tz = start.getZone();
if (units == null) {
String parameter = locationLevelName.split("\\.")[1];
logger.info("Getting default units for " + parameter);
String defaultUnits = CWMS_UTIL_PACKAGE.call_GET_DEFAULT_UNITS(
configuration, parameter, UnitSystem.SI.getValue());
logger.info("Default units are " + defaultUnits);
units = defaultUnits;
}

Result<?> result = dsl.select(AV_LOCATION_LEVEL.LOCATION_LEVEL_ID,
AV_LOCATION_LEVEL.OFFICE_ID, AV_LOCATION_LEVEL.LEVEL_DATE,
AV_LOCATION_LEVEL.CONSTANT_LEVEL, AV_LOCATION_LEVEL.LEVEL_UNIT)
.from(AV_LOCATION_LEVEL)
.where(AV_LOCATION_LEVEL.LOCATION_LEVEL_ID.eq(locationLevelName))
.and(AV_LOCATION_LEVEL.OFFICE_ID.eq(officeId))
.and(AV_LOCATION_LEVEL.LEVEL_DATE.ge(startDate))
.and(AV_LOCATION_LEVEL.LEVEL_DATE.le(endDate))
.and(AV_LOCATION_LEVEL.LEVEL_UNIT.eq(units))
.fetch();

if (result.isEmpty()) {
throw new NotFoundException("No location level found for " + locationLevelName + " at " + date);
}

boolean parentData = false;
String locLevelId = null;
Timestamp recentDate = null;
Double constantValue = null;
List<LocationLevel.ConstantValue> constantList = new ArrayList<>();
for (Record r : result) {
if (!parentData)
{
locLevelId = r.get(AV_LOCATION_LEVEL.LOCATION_LEVEL_ID);
}

if (recentDate == null || recentDate.before(r.get(AV_LOCATION_LEVEL.LEVEL_DATE))) {
recentDate = r.get(AV_LOCATION_LEVEL.LEVEL_DATE);
constantValue = r.get(AV_LOCATION_LEVEL.CONSTANT_LEVEL);
}
constantList.add(new LocationLevel.ConstantValue.Builder()
.withConstantValue(r.get(AV_LOCATION_LEVEL.CONSTANT_LEVEL))
.withLevelDate(ZonedDateTime.of(r.get(AV_LOCATION_LEVEL.LEVEL_DATE).toLocalDateTime(),
tz))
.build());
}

return new LocationLevel.Builder(locLevelId,
ZonedDateTime.of(recentDate.toLocalDateTime(), tz))
.withConstantValueList(constantList).withConstantValue(constantValue)
.withOfficeId(officeId).withAttributeUnitsId(units)
.build();

} else {
RETRIEVE_LOCATION_LEVEL3 level = CWMS_LEVEL_PACKAGE.call_RETRIEVE_LOCATION_LEVEL3(
configuration, locationLevelName, units, date,
"UTC", null, null, units,
"F", officeId);
List<SeasonalValueBean> seasonalValues = buildSeasonalValues(level);
if (units == null) {
String parameter = locationLevelName.split("\\.")[1];
logger.info("Getting default units for " + parameter);
String defaultUnits = CWMS_UTIL_PACKAGE.call_GET_DEFAULT_UNITS(
configuration, parameter, UnitSystem.SI.getValue());
logger.info("Default units are " + defaultUnits);
units = defaultUnits;
}
return new LocationLevel.Builder(locationLevelName, effectiveDate)
.withLevelUnitsId(units)
.withAttributeUnitsId(units)
.withInterpolateString(level.getP_INTERPOLATE())
.withIntervalMinutes(Optional.ofNullable(level.getP_INTERVAL_MINUTES())
.map(BigInteger::intValue).orElse(null))
.withIntervalMonths(Optional.ofNullable(level.getP_INTERVAL_MONTHS())
.map(BigInteger::intValue).orElse(null))
.withIntervalOrigin(level.getP_INTERVAL_ORIGIN(), effectiveDate)
.withLevelComment(level.getP_LEVEL_COMMENT())
.withOfficeId(officeId)
.withAttributeParameterId(level.get(RETRIEVE_LOCATION_LEVEL3.P_ATTRIBUTE_ID))
.withSeasonalTimeSeriesId(level.get(RETRIEVE_LOCATION_LEVEL3.P_TSID))
.withSeasonalValues(seasonalValues)
.withConstantValue(Optional.ofNullable(level.getP_LEVEL_VALUE())
.map(BigDecimal::doubleValue).orElse(null))
.build();
}
return new LocationLevel.Builder(locationLevelName, effectiveDate)
.withLevelUnitsId(units)
.withAttributeUnitsId(units)
.withInterpolateString(level.getP_INTERPOLATE())
.withIntervalMinutes(Optional.ofNullable(level.getP_INTERVAL_MINUTES())
.map(BigInteger::intValue).orElse(null))
.withIntervalMonths(Optional.ofNullable(level.getP_INTERVAL_MONTHS())
.map(BigInteger::intValue).orElse(null))
.withIntervalOrigin(level.getP_INTERVAL_ORIGIN(), effectiveDate)
.withLevelComment(level.getP_LEVEL_COMMENT())
.withOfficeId(officeId)
.withAttributeParameterId(level.get(RETRIEVE_LOCATION_LEVEL3.P_ATTRIBUTE_ID))
.withSeasonalTimeSeriesId(level.get(RETRIEVE_LOCATION_LEVEL3.P_TSID))
.withSeasonalValues(seasonalValues)
.withConstantValue(Optional.ofNullable(level.getP_LEVEL_VALUE())
.map(BigDecimal::doubleValue).orElse(null))
.build();
});
}

Expand Down
Loading

0 comments on commit dacc6fb

Please sign in to comment.