-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 693 - Rating curve retrieval #909
base: develop
Are you sure you want to change the base?
Changes from 3 commits
142fc4f
1f3a956
6d92679
e300f64
412716e
7b0124e
b64a83a
edc5efb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ | |
import static cwms.cda.api.Controllers.DATE_FORMAT; | ||
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.EXAMPLE_DATE; | ||
import static cwms.cda.api.Controllers.FORMAT; | ||
|
@@ -296,19 +297,16 @@ public void getAll(@NotNull Context ctx) { | |
|
||
ContentType contentType = Formats.parseHeaderAndQueryParm(header, format, RatingAliasMarker.class); | ||
|
||
if (format.isEmpty()) | ||
{ | ||
if (format.isEmpty()) { | ||
//Use the full content type here (i.e. application/json;version=2) | ||
ctx.contentType(contentType.toString()); | ||
} | ||
else | ||
{ | ||
} else { | ||
//Legacy content type only includes the basic type (i.e. application/json) | ||
ctx.contentType(contentType.getType()); | ||
} | ||
|
||
//At the moment, we still use the legacy formatting here, since we don't have a newer API for serializing/deserializing | ||
//a collection of rating sets - unlike getOne. | ||
//At the moment, we still use the legacy formatting here, since we don't have a newer API for | ||
// serializing/deserializing a collection of rating sets - unlike getOne. | ||
String legacyFormat = Formats.getLegacyTypeFromContentType(contentType); | ||
String results = ratingDao.retrieveRatings(legacyFormat, names, unit, datum, office, start, | ||
end, timezone); | ||
|
@@ -322,7 +320,8 @@ public void getAll(@NotNull Context ctx) { | |
|
||
@OpenApi( | ||
pathParams = { | ||
@OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective dates to be retrieve. "), | ||
@OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective " | ||
+ "dates to be retrieve. "), | ||
}, | ||
queryParams = { | ||
@OpenApiParam(name = OFFICE, required = true, description = | ||
|
@@ -339,6 +338,10 @@ public void getAll(@NotNull Context ctx) { | |
+ "otherwise 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 = EFFECTIVE_DATE, description = "Specifies the " | ||
+ "date to find the closest match to for retrieving a specific rating curve. This date is used " | ||
+ "instead of the time window specified by start and end. " | ||
+ "The format for this field is ISO 8601 extended."), | ||
@OpenApiParam(name = METHOD, description = "Specifies " | ||
+ "the retrieval method used. If no method is provided EAGER will be used.", | ||
type = RatingSet.DatabaseLoadMethod.class), | ||
|
@@ -354,8 +357,13 @@ public void getAll(@NotNull Context ctx) { | |
public void getOne(@NotNull Context ctx, @NotNull String rating) { | ||
|
||
try (final Timer.Context ignored = markAndTime(GET_ONE)) { | ||
String officeId = ctx.queryParam(OFFICE); | ||
|
||
String timezone = ctx.queryParamAsClass(TIMEZONE, String.class).getOrDefault("UTC"); | ||
Instant date = null; | ||
String specificDate = ctx.queryParam(EFFECTIVE_DATE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to use effectiveDate and effectiveDateParam for consistency There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While consistency is good, I think this illuminates that the parameter is confusing on this endpoint. I recommend using a separate endpoint. I might be missing a use case where a the closest effective date to a parameter is needed, but given temporal interpolation and transitional dates, that seems to be too complicated. |
||
if (specificDate != null) { | ||
date = DateUtils.parseUserDate(specificDate, timezone).toInstant(); | ||
} | ||
|
||
Instant beginInstant = null; | ||
String begin = ctx.queryParam(BEGIN); | ||
|
@@ -369,11 +377,13 @@ public void getOne(@NotNull Context ctx, @NotNull String rating) { | |
endInstant = DateUtils.parseUserDate(end, timezone).toInstant(); | ||
} | ||
|
||
String officeId = ctx.queryParam(OFFICE); | ||
|
||
RatingSet.DatabaseLoadMethod method = ctx.queryParamAsClass(METHOD, | ||
RatingSet.DatabaseLoadMethod.class) | ||
.getOrDefault(RatingSet.DatabaseLoadMethod.EAGER); | ||
|
||
String body = getRatingSetString(ctx, method, officeId, rating, beginInstant, endInstant); | ||
String body = getRatingSetString(ctx, method, officeId, rating, beginInstant, endInstant, date); | ||
if (body != null) { | ||
ctx.result(body); | ||
ctx.status(HttpCode.OK); | ||
|
@@ -385,7 +395,7 @@ public void getOne(@NotNull Context ctx, @NotNull String rating) { | |
@Nullable | ||
private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod method, | ||
String officeId, String rating, Instant begin, | ||
Instant end) { | ||
Instant end, Instant date) { | ||
String retval = null; | ||
|
||
try (final Timer.Context ignored = markAndTime("getRatingSetString")) { | ||
|
@@ -398,7 +408,7 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth | |
if (isJson || isXml) { | ||
ctx.contentType(contentType.toString()); | ||
try { | ||
RatingSet ratingSet = getRatingSet(ctx, method, officeId, rating, begin, end); | ||
RatingSet ratingSet = getRatingSet(ctx, method, officeId, rating, begin, end, date); | ||
if (ratingSet != null) { | ||
if (isJson) { | ||
retval = JsonRatingUtils.toJson(ratingSet); | ||
|
@@ -424,8 +434,7 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth | |
} else { | ||
CdaError re = new CdaError("Currently supporting only: " + Formats.JSONV2 | ||
+ " and " + Formats.XMLV2); | ||
logger.log(Level.WARNING, "Provided accept header not recognized:" | ||
+ acceptHeader, re); | ||
logger.log(Level.WARNING, String.format("Provided accept header not recognized: %s", acceptHeader), re); | ||
ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED); | ||
ctx.json(CdaError.notImplemented()); | ||
} | ||
|
@@ -437,13 +446,13 @@ private String getRatingSetString(Context ctx, RatingSet.DatabaseLoadMethod meth | |
|
||
private RatingSet getRatingSet(Context ctx, RatingSet.DatabaseLoadMethod method, | ||
String officeId, String rating, Instant begin, | ||
Instant end) throws IOException, RatingException { | ||
Instant end, Instant date) throws IOException, RatingException { | ||
RatingSet ratingSet; | ||
try (final Timer.Context ignored = markAndTime("getRatingSet")) { | ||
DSLContext dsl = getDslContext(ctx); | ||
|
||
RatingDao ratingDao = getRatingDao(dsl); | ||
ratingSet = ratingDao.retrieve(method, officeId, rating, begin, end); | ||
ratingSet = ratingDao.retrieve(method, officeId, rating, begin, end, date); | ||
} | ||
|
||
return ratingSet; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,17 +29,20 @@ | |
import com.fasterxml.jackson.dataformat.xml.XmlMapper; | ||
import hec.data.RatingException; | ||
import hec.data.cwmsRating.RatingSet; | ||
import java.io.IOException; | ||
import java.sql.Connection; | ||
import java.sql.Timestamp; | ||
import java.time.Instant; | ||
import java.time.ZoneId; | ||
import java.util.List; | ||
import mil.army.usace.hec.cwms.rating.io.jdbc.ConnectionProvider; | ||
import mil.army.usace.hec.cwms.rating.io.jdbc.RatingJdbcFactory; | ||
import org.jooq.DSLContext; | ||
import org.jooq.Result; | ||
import org.jooq.exception.DataAccessException; | ||
import usace.cwms.db.jooq.codegen.packages.CWMS_RATING_PACKAGE; | ||
import usace.cwms.db.jooq.codegen.tables.AV_RATING_LOCAL; | ||
|
||
import java.io.IOException; | ||
import java.sql.Connection; | ||
import java.sql.Timestamp; | ||
import java.time.Instant; | ||
import java.util.List; | ||
|
||
public class RatingSetDao extends JooqDao<RatingSet> implements RatingDao { | ||
|
||
|
@@ -57,8 +60,7 @@ public void create(String ratingSetXml, boolean storeTemplate) throws IOExceptio | |
DSLContext context = getDslContext(c, office); | ||
String errs = CWMS_RATING_PACKAGE.call_STORE_RATINGS_XML__5(context.configuration(), | ||
ratingSetXml, "T", storeTemplate ? "T" : "F"); | ||
if (errs != null && !errs.isEmpty()) | ||
{ | ||
if (errs != null && !errs.isEmpty()) { | ||
throw new DataAccessException(errs); | ||
} | ||
}); | ||
|
@@ -85,8 +87,9 @@ private static String extractOfficeId(String ratingSet) throws JsonProcessingExc | |
|
||
@Override | ||
public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, | ||
String specificationId, Instant startZdt, Instant endZdt | ||
String specificationId, Instant startZdt, Instant endZdt, Instant date | ||
) throws IOException, RatingException { | ||
AV_RATING_LOCAL view = AV_RATING_LOCAL.AV_RATING_LOCAL; | ||
|
||
final RatingSet[] retval = new RatingSet[1]; | ||
try { | ||
|
@@ -110,9 +113,43 @@ public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, | |
|
||
RatingSet.DatabaseLoadMethod finalMethod = method; | ||
|
||
connection(dsl, c -> retval[0] = | ||
RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId, | ||
specificationId, start, end, false)); | ||
if (date != null) { | ||
|
||
String ratingId = null; | ||
Long difference = null; | ||
Instant startInstant = null; | ||
Instant endInstant = null; | ||
Result<?> results = connectionResult(dsl, c -> | ||
dsl.select(view.RATING_ID, view.EFFECTIVE_DATE, view.RATING_CODE).from(view) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be possible to have this query do the limit rather than pulling all records and do tinghe limit in the CDA code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rewrote the query and removed the difference logic from the DAO |
||
.where(view.OFFICE_ID.eq(officeId) | ||
.and(view.RATING_ID.eq(specificationId)) | ||
).fetch() | ||
); | ||
for (int i = 0; i < results.size(); i++) { | ||
Timestamp effectiveDate = results.getValue(i, view.EFFECTIVE_DATE); | ||
long effectiveDifference = Math.abs(effectiveDate.toInstant().toEpochMilli() - date.toEpochMilli()); | ||
if (difference == null || effectiveDifference < difference) { | ||
difference = effectiveDifference; | ||
ratingId = results.getValue(i, view.RATING_ID); | ||
startInstant = effectiveDate.toInstant().atZone(ZoneId.of("UTC")).toInstant(); | ||
endInstant = effectiveDate.toInstant().atZone(ZoneId.of("UTC")).toInstant(); | ||
} | ||
} | ||
if (ratingId == null) { | ||
return null; | ||
} | ||
final Instant finalStartInstant = startInstant; | ||
final String ratingIdFinal = ratingId; | ||
final Instant finalEndInstant = endInstant; | ||
connection(dsl, c -> retval[0] = | ||
RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId, | ||
ratingIdFinal, finalStartInstant.toEpochMilli(), | ||
finalEndInstant.toEpochMilli(), false)); | ||
} else { | ||
connection(dsl, c -> retval[0] = | ||
RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId, | ||
specificationId, start, end, false)); | ||
} | ||
|
||
} catch (DataAccessException ex) { | ||
Throwable cause = ex.getCause(); | ||
|
@@ -151,7 +188,7 @@ public void store(String ratingSetXml, boolean includeTemplate) throws IOExcepti | |
public void delete(String officeId, String specificationId, Instant start, Instant end) { | ||
Timestamp startDate = new Timestamp(start.toEpochMilli()); | ||
Timestamp endDate = new Timestamp(end.toEpochMilli()); | ||
dsl.connection(c-> | ||
dsl.connection(c -> | ||
CWMS_RATING_PACKAGE.call_DELETE_RATINGS( | ||
getDslContext(c,officeId).configuration(), specificationId, startDate, | ||
endDate, "UTC", officeId | ||
|
@@ -170,15 +207,15 @@ public String retrieveRatings(String format, String names, String unit, String d | |
} | ||
|
||
private static final class RatingConnectionProvider implements ConnectionProvider { | ||
private final Connection c; | ||
private final Connection conn; | ||
|
||
private RatingConnectionProvider(Connection c) { | ||
this.c = c; | ||
private RatingConnectionProvider(Connection conn) { | ||
this.conn = conn; | ||
} | ||
|
||
@Override | ||
public Connection getConnection() { | ||
return c; | ||
return conn; | ||
} | ||
|
||
@Override | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very confusing parameter.
Additionally, you are passing this confusion into the DAO by expanding the existing DAO method instead of creating a new one, so now you have three dates in the DAO function. This makes test cases far more complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved functionality to its own endpoint to reduce confusion. New endpoint returns closest effective date rating where the date is not beyond the current date.