/** * This software was developed and / or modified by Raytheon Company, * pursuant to Contract DG133W-05-CQ-1067 with the US Government. * * U.S. EXPORT CONTROLLED TECHNICAL DATA * This software product contains export-restricted data whose * export/transfer/disclosure is restricted by U.S. law. Dissemination * to non-U.S. persons whether in the United States or abroad requires * an export license or other authorization. * * Contractor Name: Raytheon Company * Contractor Address: 6825 Pine Street, Suite 340 * Mail Stop B8 * Omaha, NE 68106 * 402.291.0100 * * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ package com.raytheon.viz.hydro.timeseries; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import org.apache.commons.lang.time.DateUtils; import com.raytheon.uf.common.dataplugin.persist.PersistableDataObject; import com.raytheon.uf.common.dataplugin.shef.tables.Fcstheight; import com.raytheon.uf.common.dataplugin.shef.tables.FcstheightId; import com.raytheon.uf.common.dataplugin.shef.tables.Rejecteddata; import com.raytheon.uf.common.dataplugin.shef.tables.RejecteddataId; import com.raytheon.uf.common.dataquery.db.QueryResult; import com.raytheon.uf.common.ohd.AppsDefaults; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.viz.core.catalog.DirectDbQuery; import com.raytheon.uf.viz.core.catalog.DirectDbQuery.QueryLanguage; import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.localization.LocalizationManager; import com.raytheon.viz.hydro.timeseries.table.DataRecord; import com.raytheon.viz.hydro.timeseries.table.SiteInfo; import com.raytheon.viz.hydro.timeseries.table.TabularData; import com.raytheon.viz.hydro.timeseries.util.TimeSeriesUtil; import com.raytheon.viz.hydrocommon.HydroConstants; import com.raytheon.viz.hydrocommon.data.ForecastData; import com.raytheon.viz.hydrocommon.datamanager.HydroDataManager; import com.raytheon.viz.hydrocommon.datamanager.SqlBuilder; import com.raytheon.viz.hydrocommon.util.HydroUtils; /** * Class for managing database query calls. TimeSeriesDataManager.java * *
 * SOFTWARE HISTORY
 *
 * Date          Ticket#     Engineer     Description
 * ------------- ----------- ------------ --------------------------
 * 03Sept2008    1509        dhladky      Initial Creation.
 * Oct 20, 2008  1520        mpduff       Add Timeseries queries.
 * Nov 04, 2009  2639        mpduff       Fixed some queries, code cleanup.
 * Jul 21, 2010  5964        lbousaidi    Added contingencyValue table name to
 *                                        addDataRecord routine.
 * Oct 25, 2010  2640        lbousaidi    changed obstime to productime in
 *                                        getTabularData select query
 * Apr 05, 2011  8732        jpiatt       Added product_id to getGraphData
 *                                        query.
 * Jun 01, 2011  9499        djingtao     add dur in getGraphData()
 * Jul 25, 2011  10082       djingtao     modify edit()
 * May 30, 2012  14967       wkwock       overload insertRejectedData method
 * Feb 22, 2013  14676       lbousaidi    check when producttime is null
 * Mar 25, 2013  1781        mpduff       Constrain time series table query with
 *                                        a start time.
 * May 12, 2014  16705       lbousaidi    update revision and shef_qual_code in
 *                                        edit routine.
 * Dec 14, 2014  16388       xwei         updated the insertion of rejecteddata
 *                                        table.
 * Jul 21, 2015  4500        rjpeter      Use Number in blind cast.
 * Aug 05, 2015  4486        rjpeter      Changed Timestamp to Date.
 * Aug 18, 2015  4793        rjpeter      Use Number in blind cast.
 * Nov 06, 2015  17846       lbousaidi    modify edit and insertRejectedData so
 *                                        that quality_code. is reset from Bad
 *                                        to Good after data QC.
 * Feb 16, 2016  5342        bkowal       Ensure two rejected records with the
 *                                        same key values are not inserted at
 *                                        the same second.
 * Mar 08, 2017  17643       jdeng        Fix errors when deleting/setting data
 *                                        to missing
 * Apr 12, 2018  6619        randerso     Code cleanup.
 * Jun 27, 2018  6748        randerso     Use UFStatus instead of slf4j Logger
 *                                        in CAVE. Fixed query in getDataFromDB
 *                                        to correctly format dates in GMT
 *                                        regardless of local time zone setting.
 * Sep 21, 2018  7379        mduff        Moved for PDC Refactor.
 *
 * 
* * @author dhladky */ public class TimeSeriesDataManager extends HydroDataManager { private final IUFStatusHandler statusHandler = UFStatus .getHandler(getClass()); private static final String TIME_SERIES_DATA_QUERY = "select lid,obstime,lid,product_id from latestobsvalue"; private static final String INGEST_FILTER_QUERY = "select lid,pe,ts,extremum,dur from ingestfilter where lid=':lid' and ingest = 'T' order by pe asc,ts_rank asc,ts asc,dur asc,extremum asc"; private static final String SHEF_PE_QUERY = "select name||' '|| eng_unit from shefpe where pe=':pe'"; private static final String SHEF_PE_GENERIC_UNITS = "Generic Units"; /** Quality control value for manual "Good" */ private static final int QC_MANUAL_PASSED = 121; private static SimpleDateFormat dateFormat; static { dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); } private static TimeSeriesDataManager manager = null; private Map stationData; private boolean sortChanged = false; /** * Map holding the location id and display class. */ private Map stnDisplayMap = null; /** * private constructor */ private TimeSeriesDataManager() { } /** * Get an instance of this singleton object * * @return instance of this class */ public static synchronized TimeSeriesDataManager getInstance() { if (manager == null) { manager = new TimeSeriesDataManager(); } return manager; } /** * Get TimeSeriesData from the DB * * @return Object[] */ public Object[] getTimeSeriesData() { List data; try { data = DirectDbQuery.executeQuery(TIME_SERIES_DATA_QUERY, HydroConstants.IHFS, QueryLanguage.SQL); if (data != null) { return data.get(0); } } catch (VizException e) { statusHandler.error("Error retrieving time series data", e); } return null; } /** * Get TimeSeriesStationData from the DB * * @return map */ public Map getTimeSeriesStationData() { return getTimeSeriesStationData(false); } /** * Get the station list data sorted by lid. * * @param sortByName * boolean * @return map */ public Map getTimeSeriesStationData(boolean sortByName) { boolean stnDisplayMapSet = true; StringBuilder sql = new StringBuilder("Select "); sql.append("distinct location.lid, ingestfilter.pe, "); sql.append("upper(location.name) as uppername, stnclass.disp_Class "); sql.append("from ingestfilter, location, stnclass "); sql.append("where ingest='T' and location.lid = stnclass.lid "); sql.append("and location.lid=ingestfilter.lid "); if (sortByName) { sql.append("order by uppername asc"); } else { sql.append("order by lid asc"); } // if the sort changed then requery for the data if (sortChanged != sortByName) { sortChanged = true; } if (stnDisplayMap == null) { stnDisplayMap = new HashMap<>(); stnDisplayMapSet = false; } if (stationData == null || sortChanged) { stationData = new LinkedHashMap<>(); List data; try { data = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); for (Object[] rowData : data) { if (rowData[2] == null) { rowData[2] = ""; } stationData.put((String) rowData[0], (String) rowData[2]); if (!stnDisplayMapSet) { stnDisplayMap.put((String) rowData[0], (String) rowData[3]); } } } catch (VizException e) { statusHandler.error("Error retrieving station data", e); } } return stationData; } /** * Get the site's PE data for graphical display * * @param lid * The lid to query for * @return ArrayList of Object[] of data * @throws VizException */ public List getSitePEData(String lid) throws VizException { return DirectDbQuery.executeQuery( INGEST_FILTER_QUERY.replace(":lid", lid), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Get the site's PE data for tabular display * * @param lid * The lid to query for * @return List of SiteInfo objects * @throws VizException */ public List getTabularPEData(String lid) throws VizException { StringBuilder sql = new StringBuilder("select "); sql.append("lid, pe, dur, ts, extremum from Ingestfilter "); sql.append("where lid = '").append(lid).append("' and ingest = 'T' "); sql.append("order by pe, dur, ts, extremum asc"); List results = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); List returnData = new ArrayList<>(); for (int i = 0; i < results.size(); i++) { Object[] oa = results.get(i); SiteInfo si = new SiteInfo(); si.setLid((String) oa[0]); si.setPe((String) oa[1]); si.setDur(((Number) oa[2]).intValue()); si.setTs((String) oa[3]); si.setExt((String) oa[4]); returnData.add(si); } return returnData; } /** * Checks the record count in the Location and Stnclass tables. * * @return true if same count, otherwise false * @throws VizException */ public boolean checkLidCount() throws VizException { /* Set up a location query */ String locationTable = "locview"; String stnClassTable = "stnclass"; StringBuilder errMessage = null; errMessage = new StringBuilder( "Error querying the Location table, no data returned"); /* Execute the location query */ long locationCount = recordCount(locationTable, ""); if (locationCount <= 0) { throw new VizException(errMessage.toString()); } errMessage.setLength(0); errMessage.setLength(0); errMessage .append("Error querying the Stnclass table, no data returned"); /* Execute the Stnclass query */ long stnClassCount = recordCount(stnClassTable, ""); if (stnClassCount <= 0) { throw new VizException(errMessage.toString()); } if (stnClassCount == locationCount) { return true; } return false; } /** * Get the shef pe data from IHFS. * * @param pe * The PE value * @return The units of the data * @throws VizException */ public String getShefPE(String pe) throws VizException { List data = DirectDbQuery.executeQuery( SHEF_PE_QUERY.replace(":pe", pe), HydroConstants.IHFS, QueryLanguage.SQL); if (data != null && !data.isEmpty()) { Object[] sa = data.get(0); return (String) sa[0]; } return SHEF_PE_GENERIC_UNITS; } /** * Query the table. * * @param tablename * The table to query * @param lid * The location id * @param pe * The physical element * @param ts * The type source * @param dur * The duration * @param extremum * The extremum * @param startTime * The start time * @param endTime * The end time * @return The List of Object[] data * @throws VizException * @throws ClassNotFoundException */ public List getGraphData(String tablename, String lid, String pe, String ts, int dur, String extremum, Date startTime, Date endTime) throws VizException { StringBuilder graphQuery = new StringBuilder( "select lid,obstime,value,product_id from "); graphQuery.append(tablename + " where lid = '" + lid + "' and pe = '" + pe + "' " + "and dur = '" + dur + "' "); graphQuery.append("and ts = '" + ts + "' and extremum = '" + extremum.toUpperCase() + "' and obstime "); graphQuery.append("between '" + HydroConstants.DATE_FORMAT.format(startTime) + "' "); graphQuery.append( "and '" + HydroConstants.DATE_FORMAT.format(endTime) + "' "); graphQuery.append("order by obstime asc"); return DirectDbQuery.executeQuery(graphQuery.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Query the Riverstat table for the flood/action stage/flow. * * @param lid * The Location Id * @return The list of flood stage values * @throws VizException */ public List getFloodStage(String lid) throws VizException { /* Query the floodCat table for the flood stage data */ String floodQuerySql = "select lid,fs,fq,wstg,action_Flow from riverstat where lid = '" + lid + "'"; return DirectDbQuery.executeQuery(floodQuerySql, HydroConstants.IHFS, QueryLanguage.SQL); } /** * Query the floodcat table for the flood stage. * * @param lid * The Location Id * @return The flood stage value as a String * @throws VizException */ public List getFloodCategories(String lid) throws VizException { String floodQuerySql = "select " + HydroConstants.LID + "," + HydroConstants.MINOR_STAGE + "," + HydroConstants.MODERATE_STAGE + "," + HydroConstants.MAJOR_STAGE + "," + HydroConstants.MINOR_FLOW + "," + HydroConstants.MODERATE_FLOW + "," + HydroConstants.MAJOR_FLOW + " from floodcat where lid = '" + lid + "'"; return DirectDbQuery.executeQuery(floodQuerySql, HydroConstants.IHFS, QueryLanguage.SQL); } /** * Get the Station name and river name. * * @param lid * Location Id of the station * @return List of Object[] containing the data * @throws VizException */ public String[] getStnRiverName(String lid) throws VizException { StringBuilder sql = new StringBuilder("select "); sql.append("name "); sql.append("from location "); sql.append(" where lid = '" + lid + "'"); List result1 = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); sql.setLength(0); sql.append("select stream from riverstat where lid = '" + lid + "'"); List result2 = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); List sa = new ArrayList<>(); if (result1 != null && !result1.isEmpty()) { sa.add((String) result1.get(0)[0]); } else { sa.add(HydroConstants.UNDEFINED); } if (result2 != null && !result2.isEmpty()) { sa.add((String) result2.get(0)[0]); } else { sa.add(HydroConstants.UNDEFINED); } return sa.toArray(new String[sa.size()]); } /** * Gets a unique list of basis times. * * @param table * The table to query * @param lid * The lid to query on * @param pe * The pe to query on * @param dur * The duration to query on * @param ts * The type source to query on * @param ext * The extremum to query on * @param begin * The beginning time * @param end * The ending time * @return List of Object arrays * @throws VizException * @throws ClassNotFoundException */ public List getUniqueList(String table, String lid, String pe, int dur, String ts, String ext, Date begin, Date end) throws VizException, ClassNotFoundException { StringBuilder sql = new StringBuilder("select "); sql.append("distinct(basistime) from " + table + " "); sql.append("where lid = '" + lid + "' and ts = '" + ts + "' and "); sql.append("dur = '" + dur + "' and extremum = '" + ext + "' and "); sql.append("validtime > '" + dateFormat.format(begin) + "' and "); sql.append("validtime < '" + dateFormat.format(end) + "' "); sql.append("order by basistime desc"); return DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Query for the data records * * @param tablename * The table to query * @param lid * The lid to query on * @param pe * The pe to query on * @param ts * The type source to query on * @param dur * The duration to query on * @param ext * The extremum to query on * @param startTime * The start time of the data * @param endTime * The end time of the data * @param basisTime * The forecast basis time, null if not forecast data * @param forecastData * true if forecast data * @return ArrayList of TabularData * @throws VizException */ public List getTabularData(String tablename, String lid, String pe, String ts, String dur, String ext, Date startTime, Date endTime, String basisTime, boolean forecastData) throws VizException { StringBuilder sql = new StringBuilder("select "); if (forecastData) { sql.append("lid, validtime, value, revision, shef_qual_code, "); sql.append("quality_code, product_id, producttime, postingtime, "); sql.append("basistime, probability "); sql.append("from ").append(tablename); sql.append(" where lid = '").append(lid).append("'"); sql.append(" and pe = '").append(pe).append("'"); sql.append(" and ts = '").append(ts).append("'"); sql.append(" and dur = ").append(dur); sql.append(" and extremum = '").append(ext).append("'"); sql.append(" and validtime > '") .append(dateFormat.format(startTime)).append("'"); sql.append(" and validtime < '").append(dateFormat.format(endTime)) .append("'"); sql.append(" and basistime = '").append(basisTime).append("'"); sql.append(" order by validtime desc"); } else { sql.append("lid, obstime, value, revision, shef_qual_code, "); sql.append("quality_code, product_id, producttime, postingtime "); sql.append("from ").append(tablename); sql.append(" where lid = '").append(lid).append("'"); sql.append(" and pe = '").append(pe).append("'"); sql.append(" and ts = '").append(ts).append("'"); sql.append(" and dur = ").append(dur); sql.append(" and extremum = '").append(ext).append("'"); sql.append(" and obstime > '").append(dateFormat.format(startTime)) .append("'"); sql.append(" and obstime < '").append(dateFormat.format(endTime)) .append("'"); sql.append(" order by obstime desc"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { /* * Since debug is set by Apps Defaults and not via logger * configuration, these message will be logged as info messages to * ensure that they are output even if debug messages have been * disabled at the logger level. */ statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql.toString()); } List tabularData = new ArrayList<>(); List results = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); for (int i = 0; i < results.size(); i++) { Object[] oa = results.get(i); TabularData td = new TabularData(); td.setLid((String) oa[0]); td.setObsTime((Date) oa[1]); td.setValue((Double) oa[2]); td.setRevision(((Number) oa[3]).intValue()); td.setShefQualCode((String) oa[4]); td.setQualityCode(((Number) oa[5]).intValue()); td.setProductId((String) oa[6]); td.setProductTime((Date) oa[7]); td.setPostingTime((Date) oa[8]); if (forecastData) { td.setValidTime((Date) oa[9]); td.setProbability(((Number) oa[10]).floatValue()); } tabularData.add(td); } return tabularData; } /** * Get the record count of the table. * * @param table * The table to query * @param where * The where statement * @return Number of rows in the table, or -1 if error * @throws VizException */ public long recordCount(String table, String where) throws VizException { if (table == null || table.isEmpty()) { return -1; } String sql = "select count(*) from " + table + where; AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } List results = DirectDbQuery.executeQuery(sql, HydroConstants.IHFS, QueryLanguage.SQL); if (results == null || results.get(0) == null) { return -1; } return ((Number) results.get(0)[0]).longValue(); } /** * Delete the record. * * @param tablename * The tablename to query * @param where * The where statement * @throws VizException */ public void deleteRecord(String tablename, String where) throws VizException { String sql = "delete from " + tablename + " " + where; AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } DirectDbQuery.executeStatement(sql, HydroConstants.IHFS, QueryLanguage.SQL); } /** * Delete a list of items. * * @param queryList * list of queries * @throws VizException */ public void deleteRecords(List queryList) throws VizException { StringBuilder sb = new StringBuilder(); for (String query : queryList) { sb.append(query); } DirectDbQuery.executeStatement(sb.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Add a data record. * * @param tablename * The tablename to query * @param dr * The data record to add * @return int * @throws VizException */ public int addDataRecord(String tablename, DataRecord dr) throws VizException { StringBuilder sb = new StringBuilder(); Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); if (tablename.startsWith("Fcst") || tablename.startsWith("fcst") || tablename.startsWith("Contingency")) { sb.append("insert into " + tablename + "(lid, pe, dur, ts, extremum, "); sb.append( "probability, validtime, basistime, value, quality_code, "); sb.append( "shef_qual_code, revision, product_id, producttime, postingtime) "); sb.append("values ('" + dr.getLid() + "', '" + dr.getPe().toUpperCase() + "', "); sb.append(dr.getDur() + ", '" + dr.getTs().toUpperCase() + "', '"); sb.append(dr.getExt().toUpperCase() + "', -1, "); sb.append("'" + dateFormat.format(dr.getObsTime()) + "', '"); sb.append(dr.getBasisTime() + "', " + dr.getValue() + ", "); sb.append( dr.getQualityCode() + ", '" + dr.getShefQualCode() + "', "); sb.append(dr.getRevision() + ", '" + dr.getProductId() + "', "); sb.append("'" + dateFormat.format(dr.getProductTime()) + "', '"); sb.append(dateFormat.format(now) + "')"); } else { sb.append("insert into " + tablename + "(lid, pe, dur, ts, extremum, "); sb.append( "obstime, postingtime, value, quality_code, shef_qual_code, productTime, "); sb.append("revision, product_Id) "); sb.append("values ('" + dr.getLid() + "', '" + dr.getPe().toUpperCase() + "', "); sb.append(dr.getDur() + ", '" + dr.getTs().toUpperCase() + "', '"); sb.append(dr.getExt().toUpperCase() + "', "); sb.append("'" + dateFormat.format(dr.getObsTime()) + "', '"); sb.append(dateFormat.format(now) + "', "); sb.append(dr.getValue() + ", "); sb.append(dr.getQualityCode() + ", '"); sb.append(dr.getShefQualCode() + "', '"); sb.append(dr.getProductTime() + "', "); sb.append("0, '" + dr.getProductId() + "')"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sb.toString()); } return DirectDbQuery.executeStatement(sb.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Update sql. * * @param sql * The sql string * @return int * @throws VizException */ public int update(String sql) throws VizException { AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } return DirectDbQuery.executeStatement(sql, HydroConstants.IHFS, QueryLanguage.SQL); } /** * Insert rejected data record. * * @param dr * The data record * @return int * @throws VizException */ public int insertRejectedData(DataRecord dr) throws VizException { Rejecteddata rd = new Rejecteddata(); RejecteddataId rdid = new RejecteddataId(); Date d = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); /* set basistime to obstime for observed data */ if (dr.getBasisTime() != null) { try { rdid.setBasistime( HydroConstants.DATE_FORMAT.parse(dr.getBasisTime())); } catch (ParseException e) { rdid.setBasistime(dr.getObsTime()); } } else { rdid.setBasistime(dr.getObsTime()); } rdid.setDur((short) dr.getDur()); rdid.setExtremum(dr.getExt()); rdid.setLid(dr.getLid()); rdid.setPe(dr.getPe()); /* set postingtime to current time */ rdid.setPostingtime(d); rdid.setProbability(-1); rdid.setTs(dr.getTs()); rdid.setValidtime(dr.getValidTime()); rd.setId(rdid); rd.setProductId(dr.getProductId()); rd.setProducttime(dr.getProductTime()); rd.setQualityCode((int) dr.getQualityCode()); /* set reject_type to M for Manual */ rd.setRejectType("M"); rd.setRevision((short) dr.getRevision()); rd.setShefQualCode(dr.getShefQualCode()); rd.setUserid(LocalizationManager.getInstance().getCurrentUser()); rd.setValue(dr.getValue()); /* set validtime for observed data */ if (rdid.getValidtime() == null) { rdid.setValidtime(dr.getObsTime()); } return DirectDbQuery.saveOrUpdate(rd, HydroConstants.IHFS); } /** * Insert a list of items into the rejected table * * @param recordList * List of DataRecord objects * @param origVal * @return number of rows inserted * @throws VizException */ public int insertRejectedData(List recordList, double origVal) throws VizException { StringBuilder sb = new StringBuilder(); Date d = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); for (DataRecord dr : recordList) { sb.append("insert into rejecteddata(lid, pe, dur, ts, extremum, "); sb.append( "probability, validtime, basistime, postingtime, value, "); sb.append( "revision, shef_qual_code, product_id, producttime, quality_code, "); sb.append("reject_type, userid) VALUES("); sb.append("'" + dr.getLid() + "', "); sb.append("'" + dr.getPe() + "', "); sb.append(dr.getDur() + ", "); sb.append("'" + dr.getTs() + "', "); sb.append("'" + dr.getExt() + "', "); sb.append(-1 + ", "); /* set validtime for observed data */ if (dr.getValidTime() != null) { sb.append("'" + HydroConstants.DATE_FORMAT.format(dr.getValidTime()) + "', "); } else { sb.append( "'" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "', "); } if (dr.getBasisTime() != null) { try { sb.append("'" + HydroConstants.DATE_FORMAT .parse(dr.getBasisTime()) + "', "); } catch (ParseException e) { sb.append("'" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "', "); } } else { sb.append( "'" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "', "); } sb.append("'" + HydroConstants.DATE_FORMAT.format(d) + "', "); sb.append(origVal + ", "); sb.append(dr.getRevision() + ", "); sb.append("'" + dr.getShefQualCode() + "', "); sb.append("'" + dr.getProductId() + "', "); sb.append( "'" + HydroConstants.DATE_FORMAT.format(dr.getProductTime()) + "', "); sb.append(dr.getQualityCode() + ", "); sb.append("'M', "); sb.append("'" + LocalizationManager.getInstance().getCurrentUser() + "');"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sb.toString()); } return DirectDbQuery.executeStatement(sb.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } private Object getDataFromDB(ForecastData dr, String field) { StringBuilder sql = new StringBuilder("select " + field + " from "); String tablename = HydroUtils.getTableName(dr.getPe(), dr.getTs()); sql.append(tablename + " where "); sql.append("lid = '" + dr.getLid() + "' "); sql.append("and pe = '" + dr.getPe().toUpperCase() + "' "); sql.append("and dur =" + dr.getDur() + " "); sql.append("and ts = '" + dr.getTs().toUpperCase() + "' "); sql.append("and extremum = '" + dr.getExtremum().toUpperCase() + "' "); if (dr.getTs().toUpperCase().startsWith("F") || dr.getTs().toUpperCase().startsWith("C")) { sql.append("and validtime = '" + HydroConstants.DATE_FORMAT.format(dr.getValidTime()) + "' "); sql.append("and basistime = '" + HydroConstants.DATE_FORMAT.format(dr.getBasisTime()) + "';"); } else { // obs data sql.append("and obstime = '" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "';"); } List sqlResult; try { sqlResult = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); if (sqlResult != null && !sqlResult.isEmpty() && sqlResult.get(0)[0] != null) { return sqlResult.get(0)[0]; } } catch (VizException e) { statusHandler.error( "Failed to retrieve data from table: " + tablename + ".", e); } return null; } /** * @param editList * @param rejectedSecondsMap * @param insertStartTime * @return number of rows inserted * @throws VizException */ public int insertSetMRejectedData(List editList, Map rejectedSecondsMap, Date insertStartTime) throws VizException { StringBuilder sb = new StringBuilder(); /* * part of the primary key. So, should be unique with each record * insert. */ for (ForecastData dr : editList) { /* * This will ensure that records that would normally have the same * id will at least be offset by a second. This is needed because * the dynamic time field that is part of the primary key only has a * resolution out to the seconds field. The insert of a record does * not take an entire second. This implementation is limited by the * fact that only sixty of a particular record can be inserted * without conflicting with a potential future insert less than a * minute later. The inserts are mapped by id to reduce the amount * of data manipulation that would occur when there is a lack of * duplicate records. The ideal scenario would be to alter the data * record so that it would not be necessary to offset the posting * time by a second to ensure uniqueness; however, a changeset that * large is far outside the scope of DR #5342. */ RejecteddataId id = new RejecteddataId(); id.setLid(dr.getLid()); id.setPe(dr.getPe()); id.setDur((short) dr.getDur()); id.setTs(dr.getTs()); id.setExtremum(dr.getExtremum()); id.setProbability((float) dr.getProbability()); id.setValidtime(dr.getValidTime()); id.setBasistime(dr.getBasisTime()); id.setPostingtime(insertStartTime); Integer offsetSeconds = rejectedSecondsMap.get(id); if (offsetSeconds == null) { offsetSeconds = 0; } else { offsetSeconds += 1; } rejectedSecondsMap.put(id, offsetSeconds); final Date postingTime = DateUtils.addSeconds(insertStartTime, offsetSeconds); int probability = -1; int revision = 1; if (dr.getTs().toUpperCase().startsWith("F") || dr.getTs().toUpperCase().startsWith("C")) { probability = 0; } Date productTime = dr.getProductTime(); if (productTime == null) { productTime = (Date) getDataFromDB(dr, "producttime"); } String productID = (String) getDataFromDB(dr, "product_id"); if (productID == null) { productID = dr.getProductID(); } dr.setQualityCode(TimeSeriesUtil.setQcCode(QC_MANUAL_PASSED, dr.getQualityCode())); sb.append("insert into rejecteddata(lid, pe, dur, ts, extremum, "); sb.append( "probability, validtime, basistime, postingtime, value, "); sb.append( "revision, shef_qual_code, product_id, producttime, quality_code, "); sb.append("reject_type, userid) VALUES("); sb.append("'" + dr.getLid() + "', "); sb.append("'" + dr.getPe().toUpperCase() + "', "); sb.append(dr.getDur() + ", "); sb.append("'" + dr.getTs().toUpperCase() + "', "); sb.append("'" + dr.getExtremum().toUpperCase() + "', "); sb.append(probability + ", "); /* set validtime for observed data */ if (dr.getValidTime() != null) { sb.append("'" + HydroConstants.DATE_FORMAT.format(dr.getValidTime()) + "', "); } else { sb.append( "'" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "', "); } if (dr.getBasisTime() != null) { sb.append("'" + dr.getBasisTime() + "', "); } else { sb.append( "'" + HydroConstants.DATE_FORMAT.format(dr.getObsTime()) + "', "); } sb.append("'" + HydroConstants.DATE_FORMAT.format(postingTime) + "', "); sb.append(dr.getPreviousValue() + ", "); sb.append(revision + ", "); // shef_qual_code always M sb.append("'M', "); sb.append("'" + productID + "', "); sb.append("'" + HydroConstants.DATE_FORMAT.format(productTime) + "', "); sb.append(dr.getQualityCode() + ", "); sb.append("'M', "); sb.append("'" + LocalizationManager.getInstance().getCurrentUser() + "');"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sb.toString()); } return DirectDbQuery.executeStatement(sb.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * @param deleteList * @param rejectedSecondsMap * @param insertStartTime * @return number of rows inserted * @throws VizException */ public int insertDelRejectedData(List deleteList, Map rejectedSecondsMap, Date insertStartTime) throws VizException { StringBuilder sb = new StringBuilder(); /* * part of the primary key. So, should be unique with each record * insert. */ for (ForecastData dr : deleteList) { /* * This will ensure that records that would normally have the same * id will at least be offset by a second. This is needed because * the dynamic time field that is part of the primary key only has a * resolution out to the seconds field. The insert of a record does * not take an entire second. This implementation is limited by the * fact that only sixty of a particular record can be inserted * without conflicting with a potential future insert less than a * minute later. The inserts are mapped by id to reduce the amount * of data manipulation that would occur when there is a lack of * duplicate records. The ideal scenario would be to alter the data * record so that it would not be necessary to offset the posting * time by a second to ensure uniqueness; however, a changeset that * large is far outside the scope of DR #5342. */ RejecteddataId id = new RejecteddataId(); id.setLid(dr.getLid()); id.setPe(dr.getPe()); id.setDur((short) dr.getDur()); id.setTs(dr.getTs()); id.setExtremum(dr.getExtremum()); id.setProbability((float) dr.getProbability()); id.setValidtime(dr.getValidTime()); id.setBasistime(dr.getBasisTime()); id.setPostingtime(insertStartTime); Integer offsetSeconds = rejectedSecondsMap.get(id); if (offsetSeconds == null) { offsetSeconds = 0; } else { offsetSeconds += 1; } rejectedSecondsMap.put(id, offsetSeconds); final Date postingTime = DateUtils.addSeconds(insertStartTime, offsetSeconds); int probability = -1; if (dr.getTs().toUpperCase().startsWith("F") || dr.getTs().toUpperCase().startsWith("C")) { probability = 0; } Date productTime = dr.getProductTime(); if (productTime == null) { productTime = (Date) getDataFromDB(dr, "producttime"); } String productID = (String) getDataFromDB(dr, "product_id"); if (productID == null) { productID = dr.getProductID(); } Integer qualityCode = ((Number) getDataFromDB(dr, "quality_code")) .intValue(); String shefQualCode = (String) getDataFromDB(dr, "shef_qual_code"); sb.append("insert into rejecteddata(lid, pe, dur, ts, extremum, "); sb.append( "probability, validtime, basistime, postingtime, value, "); sb.append( "revision, shef_qual_code, product_id, producttime, quality_code, "); sb.append("reject_type, userid) VALUES("); sb.append("'").append(dr.getLid()).append("', "); sb.append("'").append(dr.getPe().toUpperCase()).append("', "); sb.append(dr.getDur()).append(", "); sb.append("'").append(dr.getTs().toUpperCase()).append("', "); sb.append("'").append(dr.getExtremum().toUpperCase()).append("', "); sb.append(probability).append(", "); /* set validtime for observed data */ if (dr.getValidTime() != null) { sb.append("'").append( HydroConstants.DATE_FORMAT.format(dr.getValidTime())) .append("', "); } else { sb.append("'").append( HydroConstants.DATE_FORMAT.format(dr.getObsTime())) .append("', "); } if (dr.getBasisTime() != null) { sb.append("'").append(dr.getBasisTime()).append("', "); } else { sb.append("'").append( HydroConstants.DATE_FORMAT.format(dr.getObsTime())) .append("', "); } sb.append("'") .append(HydroConstants.DATE_FORMAT.format(postingTime)) .append("', "); sb.append(dr.getPreviousValue()).append(", "); sb.append(dr.getRevision()).append(", "); sb.append("'").append(shefQualCode).append("', "); sb.append("'").append(productID).append("', "); sb.append("'") .append(HydroConstants.DATE_FORMAT.format(productTime)) .append("', "); sb.append(qualityCode).append(", "); sb.append("'M', "); sb.append("'") .append(LocalizationManager.getInstance().getCurrentUser()) .append("');"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sb.toString()); } return DirectDbQuery.executeStatement(sb.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } /** * Deletes a list of Observations. * * @param deleteList * List of Observations to delete. * @return The number of rows modified * @throws VizException */ public int delete(List deleteList) throws VizException { int status = -1; for (int i = 0; i < deleteList.size(); i++) { ForecastData data = deleteList.get(i); String tablename = HydroUtils.getTableName(data.getPe(), data.getTs()); StringBuilder sql = new StringBuilder("delete from "); sql.append(tablename + " where "); sql.append("lid = '" + data.getLid() + "' "); sql.append("and pe = '" + data.getPe().toUpperCase() + "' "); sql.append("and dur = " + data.getDur() + " "); sql.append("and ts = '" + data.getTs().toUpperCase() + "' "); sql.append("and extremum = '" + data.getExtremum().toUpperCase() + "' "); if (data.getValidTime() != null) { sql.append("and validtime = '" + dbFormat.format(data.getValidTime()) + "'"); } else { sql.append("and obstime = '" + dbFormat.format(data.getObsTime()) + "'"); } AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } status = DirectDbQuery.executeStatement(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } return status; } /** * Inserts a list of Observations. * * @param insertList * List of Observations to insert. * @return The number of rows modified * @throws VizException */ public int insert(List insertList) throws VizException { int status = -1; Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); for (int i = 0; i < insertList.size(); i++) { ForecastData data = insertList.get(i); String tablename = HydroUtils.getTableName(data.getPe(), data.getTs()); SqlBuilder sql = new SqlBuilder(tablename); sql.setSqlType(SqlBuilder.INSERT); sql.addString("lid", data.getLid()); sql.addString("pe", data.getPe().toUpperCase()); sql.addInt("dur", data.getDur()); sql.addString("ts", data.getTs().toUpperCase()); sql.addString("extremum", data.getExtremum().toUpperCase()); sql.addString("product_id", data.getProductID().toUpperCase()); if (data.getValidTime() != null) { sql.addString("validTime", dateFormat.format(data.getValidTime())); sql.addInt("probability", -1); sql.addString("basisTime", dateFormat.format(data.getBasisTime())); if (data.getProductTime() == null) { sql.addString("producttime", dateFormat.format(Calendar .getInstance(TimeZone.getTimeZone("GMT")) .getTime())); } else { sql.addString("producttime", dateFormat.format(data.getProductTime())); } } else { sql.addString("obstime", dateFormat.format(data.getObsTime())); sql.addString("producttime", dateFormat.format(data.getObsTime())); } sql.addDouble("value", data.getValue()); sql.addString("shef_qual_code", "M"); sql.addInt("quality_code", TimeSeriesUtil.DEFAULT_QC_VALUE); sql.addInt("revision", 0); sql.addString("postingtime", dateFormat.format(now)); AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql.toString()); } status = DirectDbQuery.executeStatement(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } return status; } /** * Edits a list of Observations. * * @param editList * List of Observations to edit. * @return The number of rows modified * @throws VizException */ public int edit(List editList) throws VizException { int status = -1; Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime(); for (int i = 0; i < editList.size(); i++) { ForecastData data = editList.get(i); String tablename = HydroUtils.getTableName(data.getPe(), data.getTs()); // set the QC to GOOD when you set data to missing. data.setQualityCode(TimeSeriesUtil.setQcCode(QC_MANUAL_PASSED, data.getQualityCode())); SqlBuilder sql = new SqlBuilder(tablename); sql.setSqlType(SqlBuilder.UPDATE); sql.addDouble("value", data.getValue()); sql.addString("shef_qual_code", "M"); sql.addInt("quality_code", data.getQualityCode()); sql.addInt("revision", 1); sql.addString("postingTime", HydroConstants.DATE_FORMAT.format(now)); if (data.getProductTime() == null) { sql.addString("producttime", HydroConstants.DATE_FORMAT.format(Calendar .getInstance(TimeZone.getTimeZone("GMT")) .getTime())); } StringBuilder where = new StringBuilder(); where.append(" where lid = '" + data.getLid().toUpperCase() + "' "); where.append("and dur = " + data.getDur() + " "); where.append("and ts = '" + data.getTs().toUpperCase() + "' "); where.append("and extremum = '" + data.getExtremum().toUpperCase() + "' "); if (data.getValidTime() != null) { where.append("and validtime = '" + dateFormat.format(data.getValidTime()) + "'"); where.append(" and basistime = '" + dateFormat.format(data.getBasisTime()) + "'"); } else { where.append("and obstime = '" + dateFormat.format(data.getObsTime()) + "'"); } sql.setWhereClause(where.toString()); AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } DirectDbQuery.executeStatement(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); } return status; } /** * Get the product's productId and productTime. * * @param where * The where statement * @param table * The table to get the data from * @return List of String[] productId, productTime * @throws VizException */ public String[] getProductIdTime(final String where, final String table) throws VizException { StringBuilder sql = new StringBuilder(); sql.append("select product_id, productTime from " + table); sql.append(where); AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + sql); } List rs = DirectDbQuery.executeQuery(sql.toString(), HydroConstants.IHFS, QueryLanguage.SQL); String[] sa = null; if (rs != null && !rs.isEmpty()) { Object[] item = rs.get(0); sa = new String[2]; sa[0] = (String) item[0]; sa[1] = dateFormat.format((Date) item[1]); } return sa; } /** * Get the list of forecast data. * * @param where * The where statement * @param table * The table to get the data from * @return List of String[] productId, productTime * @throws VizException */ public List getForecast(final String where, final String table) throws VizException { StringBuilder query = new StringBuilder("select "); query.append("lid, pe, dur, ts, extremum, probability, validtime, "); query.append("basistime, value, shef_qual_code, quality_code, "); query.append("revision, product_id, producttime, postingtime "); query.append("from " + table + " " + where); AppsDefaults ad = AppsDefaults.getInstance(); boolean debug = ad.getBoolean(HydroConstants.DEBUG_HYDRO_DB_TOKEN, false); if (debug) { statusHandler.info(ad.getToken(HydroConstants.PGHOST) + ":" + ad.getToken(HydroConstants.PGPORT) + ":" + ad.getToken(HydroConstants.DB_NAME)); statusHandler.info("Query: " + query.toString()); } QueryResult results = DirectDbQuery.executeMappedQuery(query.toString(), HydroConstants.IHFS, QueryLanguage.SQL); if (results == null || results.getResultCount() == 0) { return Collections.emptyList(); } List returnList = new ArrayList<>(results.getResultCount()); for (int i = 0; i < results.getResultCount(); i++) { Fcstheight fh = new Fcstheight(); FcstheightId fhid = new FcstheightId(); fhid.setLid((String) results.getRowColumnValue(i, "lid")); fhid.setPe((String) results.getRowColumnValue(i, "pe")); fhid.setDur(((Number) results.getRowColumnValue(i, "dur")) .shortValue()); fhid.setTs((String) results.getRowColumnValue(i, "ts")); fhid.setExtremum((String) results.getRowColumnValue(i, "extremum")); fhid.setProbability( ((Number) results.getRowColumnValue(i, "probability")) .floatValue()); fhid.setValidtime((Date) results.getRowColumnValue(i, "validtime")); fhid.setBasistime((Date) results.getRowColumnValue(i, "basistime")); fh.setId(fhid); fh.setValue((Double) results.getRowColumnValue(i, "value")); fh.setShefQualCode( (String) results.getRowColumnValue(i, "shef_qual_code")); fh.setQualityCode( ((Number) results.getRowColumnValue(i, "quality_code")) .intValue()); fh.setRevision(((Number) results.getRowColumnValue(i, "revision")) .shortValue()); fh.setProductId( (String) results.getRowColumnValue(i, "product_id")); fh.setProducttime( (Date) results.getRowColumnValue(i, "producttime")); fh.setPostingtime( (Date) results.getRowColumnValue(i, "postingtime")); returnList.add(fh); } return returnList; } /** * Insert/Update the forecast tables. * * @param dataObj * The persistent data object to insert/update * @return The number of objects inserted/updated * @throws VizException */ public int putForecast(PersistableDataObject dataObj) throws VizException { int rv = 0; rv = DirectDbQuery.saveOrUpdate(dataObj, HydroConstants.IHFS); return rv; } /** * Get the station display map. * * @return The map of station displays */ public Map getStationDisplayMap() { return stnDisplayMap; } }