Skip to content

Commit

Permalink
Issue 693 - Rating curve retrieval (#909)
Browse files Browse the repository at this point in the history
Fixes #693 - Adds support for retrieving a rating closest to a specified
date.
  • Loading branch information
zack-rma authored Dec 19, 2024
1 parent c2cdf26 commit 6448c34
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 42 deletions.
2 changes: 2 additions & 0 deletions cwms-data-api/src/main/java/cwms/cda/ApiServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import cwms.cda.api.ProjectController;
import cwms.cda.api.PropertyController;
import cwms.cda.api.RatingController;
import cwms.cda.api.RatingLatestController;
import cwms.cda.api.RatingMetadataController;
import cwms.cda.api.RatingSpecController;
import cwms.cda.api.RatingTemplateController;
Expand Down Expand Up @@ -572,6 +573,7 @@ protected void configureRoutes() {
new RatingSpecController(metrics), requiredRoles,5, TimeUnit.MINUTES);
cdaCrudCache("/ratings/metadata/{rating-id}",
new RatingMetadataController(metrics), requiredRoles,5, TimeUnit.MINUTES);
get("/ratings/{rating-id}/latest", new RatingLatestController(metrics));
cdaCrudCache("/ratings/{rating-id}",
new RatingController(metrics), requiredRoles,5, TimeUnit.MINUTES);
cdaCrudCache("/catalog/{dataset}",
Expand Down
129 changes: 129 additions & 0 deletions cwms-data-api/src/main/java/cwms/cda/api/RatingLatestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
*
* 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.api;

import static cwms.cda.api.Controllers.GET_ONE;
import static cwms.cda.api.Controllers.OFFICE;
import static cwms.cda.api.Controllers.RATING_ID;
import static cwms.cda.api.Controllers.STATUS_200;
import static cwms.cda.data.dao.JooqDao.getDslContext;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.fasterxml.jackson.core.JsonProcessingException;
import cwms.cda.data.dao.JsonRatingUtils;
import cwms.cda.data.dao.RatingDao;
import cwms.cda.data.dao.RatingSetDao;
import cwms.cda.formatters.ContentType;
import cwms.cda.formatters.Formats;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import io.javalin.http.HttpCode;
import io.javalin.plugin.openapi.annotations.OpenApi;
import io.javalin.plugin.openapi.annotations.OpenApiContent;
import io.javalin.plugin.openapi.annotations.OpenApiParam;
import io.javalin.plugin.openapi.annotations.OpenApiResponse;
import org.jetbrains.annotations.NotNull;
import org.jooq.DSLContext;


public class RatingLatestController implements Handler {
private static final String TAG = "Ratings";
private final MetricRegistry metrics;

public RatingLatestController(MetricRegistry metrics) {
this.metrics = metrics;
}

private Timer.Context markAndTime(String subject) {
return Controllers.markAndTime(metrics, getClass().getName(), subject);
}

@NotNull
protected RatingDao getRatingDao(DSLContext dsl) {
return new RatingSetDao(dsl);
}

@OpenApi(
pathParams = {
@OpenApiParam(name = RATING_ID, required = true, description = "The rating-id of the effective "
+ "dates to be retrieve. "),
},
queryParams = {
@OpenApiParam(name = OFFICE, required = true, description =
"Specifies the owning office of the ratingset to be included in the "
+ "response."),
},
responses = {
@OpenApiResponse(status = STATUS_200, content = {
@OpenApiContent(type = Formats.JSONV2),
@OpenApiContent(type = Formats.XMLV2)})
},
description = "Returns CWMS Rating Data",
tags = {TAG})
@Override
public void handle(@NotNull Context ctx) throws Exception {
try (final Timer.Context ignored = markAndTime(GET_ONE)) {
String rating = ctx.pathParam(RATING_ID);

ContentType contentType = new ContentType(ctx.contentType() != null ? ctx.contentType() : Formats.JSONV2);

String officeId = ctx.queryParam(OFFICE);

if (!contentType.toString().equals(Formats.JSONV2) && !contentType.toString().equals(Formats.XMLV2)) {
ctx.status(HttpCode.UNSUPPORTED_MEDIA_TYPE);
}

String body = getLatestRatingSet(ctx, officeId, rating, contentType);
ctx.contentType(contentType.toString());
if (body != null) {
ctx.result(body);
ctx.status(HttpCode.OK);
} else {
ctx.status(HttpCode.NOT_FOUND);
}
}
}

private String getLatestRatingSet(Context ctx, String officeId, String rating, ContentType contentType)
throws JsonProcessingException {
String ratingSet = null;
try (final Timer.Context ignored = markAndTime("getLatestRatingSet")) {
DSLContext dsl = getDslContext(ctx);

RatingDao ratingDao = getRatingDao(dsl);

ratingSet = ratingDao.retrieveLatestXML(officeId, rating);

if (contentType.toString().equals(Formats.JSONV2)) {
ratingSet = JsonRatingUtils.xmlToJson(ratingSet);
}
}

return ratingSet;
}
}
7 changes: 4 additions & 3 deletions cwms-data-api/src/main/java/cwms/cda/data/dao/RatingDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@

import hec.data.RatingException;
import hec.data.cwmsRating.RatingSet;

import java.io.IOException;
import java.time.Instant;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public interface RatingDao {

static final Pattern officeMatcher = Pattern.compile(".*office-id=\"(.*?)\"");
Pattern officeMatcher = Pattern.compile(".*office-id=\"(.*?)\"");

void create(String ratingSet, boolean storeTemplate) throws IOException, RatingException;

RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, String specificationId,
Instant start, Instant end) throws IOException, RatingException;

String retrieveLatestXML(String officeId, String specificationId);

String retrieveRatings(String format, String names, String unit, String datum, String office,
String start, String end, String timezone);

Expand All @@ -52,7 +53,7 @@ String retrieveRatings(String format, String names, String unit, String datum, S
static String extractOfficeFromXml(String xml) {
Matcher officeMatch = officeMatcher.matcher(xml);

if(officeMatch.find()) {
if (officeMatch.find()) {
return officeMatch.group(1);
} else {
throw new RuntimeException("Unable to determine office for data set");
Expand Down
37 changes: 22 additions & 15 deletions cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +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.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.exception.DataAccessException;
import usace.cwms.db.jooq.codegen.packages.CWMS_RATING_PACKAGE;

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 {


public RatingSetDao(DSLContext dsl) {
super(dsl);
}
Expand All @@ -57,8 +56,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);
}
});
Expand All @@ -83,11 +81,19 @@ private static String extractOfficeId(String ratingSet) throws JsonProcessingExc
return office;
}

@Override
public String retrieveLatestXML(String officeId, String specificationId) {
return connectionResult(dsl, c -> {
DSLContext context = getDslContext(c, officeId);
return CWMS_RATING_PACKAGE.call_RETRIEVE_EFF_RATINGS_XML_F(context.configuration(), specificationId,
Timestamp.from(Instant.now()), Timestamp.from(Instant.now()), null, officeId);
});
}

@Override
public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId,
String specificationId, Instant startZdt, Instant endZdt
) throws IOException, RatingException {

final RatingSet[] retval = new RatingSet[1];
try {
final Long start;
Expand All @@ -112,7 +118,8 @@ public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId,

connection(dsl, c -> retval[0] =
RatingJdbcFactory.ratingSet(finalMethod, new RatingConnectionProvider(c), officeId,
specificationId, start, end, false));
specificationId, start, end, false));


} catch (DataAccessException ex) {
Throwable cause = ex.getCause();
Expand Down Expand Up @@ -151,7 +158,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
Expand All @@ -170,15 +177,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
Expand Down
Loading

0 comments on commit 6448c34

Please sign in to comment.