diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql index 61324b1e4182b74cfa27829e47d8a5e9a3d29c70..af14566f93648db3c69da1b95a1add75fce6f85e 100644 --- a/sql/init_data.h2.sql +++ b/sql/init_data.h2.sql @@ -55,11 +55,13 @@ INSERT INTO cellpra (cell, pra, ratio) -- period CREATE TEMPORARY TABLE IF NOT EXISTS tmp_period ( code VARCHAR, - phase VARCHAR + phase VARCHAR, + firstday VARCHAR, + lastday VARCHAR ); -INSERT INTO tmp_period (code, phase) SELECT * FROM CSVREAD('../sql/periods.csv'); -INSERT INTO period (code, name, phase) - SELECT t.code, k.id, t.phase +INSERT INTO tmp_period (code, phase, firstday, lastday) SELECT * FROM CSVREAD('../sql/periods.csv'); +INSERT INTO period (code, name, phase, firstday, lastday) + SELECT t.code, k.id, t.phase, t.firstday, t.lastday FROM tmp_period AS t JOIN i18nkey AS k ON k.string=t.code; @@ -90,7 +92,7 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_dailyvalue ( comparedvalue DOUBLE PRECISION ); INSERT INTO tmp_dailyvalue (indicator, period, cell, date, computedvalue, comparedvalue) - SELECT * FROM CSVREAD('../sql/dailyvalues.csv'); + SELECT * FROM CSVREAD('../sql/dailyvalues.csv'); INSERT INTO dailyvalue (indicator, cell, date, computedvalue, comparedvalue) SELECT i.id, t.cell, t.date, t.computedvalue, t.comparedvalue FROM tmp_dailyvalue AS t @@ -107,7 +109,7 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_normalvalue ( computedvalue DOUBLE PRECISION ); INSERT INTO tmp_normalvalue (indicator, period, cell, doy, computedvalue) - SELECT * FROM CSVREAD('../sql/normalvalues.csv'); + SELECT * FROM CSVREAD('../sql/normalvalues.csv'); INSERT INTO normalvalue (indicator, cell, doy, computedvalue) SELECT i.id, t.cell, t.doy, t.computedvalue FROM tmp_normalvalue AS t @@ -117,5 +119,5 @@ INSERT INTO normalvalue (indicator, cell, doy, computedvalue) -- simulation INSERT INTO simulation (date, simulationid, started, ended) VALUES - ('2024-02-19', 1, '2024-02-20 12:00:00', '2024-02-20 12:30:00'), - ('2024-02-20', 2, '2024-02-21 13:00:00', NULL); \ No newline at end of file + ('2024-02-19', 1, '2024-02-20 12:00:00', '2024-02-20 12:30:00'), + ('2024-02-20', 2, '2024-02-21 13:00:00', NULL); diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql index e5cd1990dd34469d2ae28130944dba91b777a768..84c10d4d57656e8742dfe0afa6f071bb3f71cf5c 100644 --- a/sql/init_data.postgresql.sql +++ b/sql/init_data.postgresql.sql @@ -63,11 +63,13 @@ INSERT INTO cell (id, lat, lon, lat1, lon1, lat2, lon2, lat3, lon3, lat4, lon4, -- period CREATE TEMPORARY TABLE IF NOT EXISTS tmp_period ( code VARCHAR, - phase VARCHAR + phase VARCHAR, + firstday VARCHAR, + lastday VARCHAR ); -\COPY tmp_period (code, phase) FROM periods.csv WITH DELIMITER ',' HEADER CSV; -INSERT INTO period (code, name, phase) - SELECT t.code, k.id, t.phase +\COPY tmp_period (code, phase, firstday, lastday) FROM periods.csv WITH DELIMITER ',' HEADER CSV; +INSERT INTO period (code, name, phase, firstday, lastday) + SELECT t.code, k.id, t.phase, t.firstday, t.lastday FROM tmp_period AS t JOIN i18nkey AS k ON k.string=t.code; diff --git a/sql/migration.sql b/sql/migration.sql index 83d6144c921cb3e3255775bce4ad9abf11071c8a..e4094c63ad430e8d20249848e7578c0c34474d32 100644 --- a/sql/migration.sql +++ b/sql/migration.sql @@ -36,21 +36,21 @@ BEGIN SELECT schemaversion() INTO schemaversion; RAISE INFO '% Apply migrations after %...', TIMEOFDAY(), schemaversion; FOR routine IN - SELECT - routine_name, - substring(routine_name, 8) AS migration_version - FROM information_schema.routines - WHERE routine_type = 'FUNCTION' AND - routine_name LIKE 'upgrade20%' AND - substring(routine_name, 8) NOT IN (SELECT version FROM dbmigration) AND - substring(routine_name, 8) > schemaversion - ORDER BY routine_name + SELECT + routine_name, + substring(routine_name, 8) AS migration_version + FROM information_schema.routines + WHERE routine_type = 'FUNCTION' AND + routine_name LIKE 'upgrade20%' AND + substring(routine_name, 8) NOT IN (SELECT version FROM dbmigration) AND + substring(routine_name, 8) > schemaversion + ORDER BY routine_name LOOP - RAISE INFO '% %() start', TIMEOFDAY(), routine.routine_name; - EXECUTE format('SELECT %I()', routine.routine_name); - INSERT INTO dbmigration (version) values(routine.migration_version); - RAISE INFO '% %() end', TIMEOFDAY(), routine.routine_name; - applied := applied + 1; + RAISE INFO '% %() start', TIMEOFDAY(), routine.routine_name; + EXECUTE format('SELECT %I()', routine.routine_name); + INSERT INTO dbmigration (version) values(routine.migration_version); + RAISE INFO '% %() end', TIMEOFDAY(), routine.routine_name; + applied := applied + 1; END LOOP; RAISE INFO '% % migrations were applied', TIMEOFDAY(), applied; RETURN applied; @@ -90,19 +90,19 @@ COMMENT ON FUNCTION drop_applied_migration_functions() IS 'Purge database from a --- CREATE OR REPLACE FUNCTION upgrade20231023() RETURNS boolean AS $BODY$ BEGIN - ALTER TABLE indicator DROP COLUMN colorsequence; - DROP TABLE colorsequence; - ALTER TABLE indicator ADD COLUMN colorsequencename VARCHAR; - ALTER TABLE indicator ADD COLUMN nbofclasses INTEGER; - CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES'); - ALTER TABLE indicator ADD COLUMN quantiletype QUANTILETYPE; - UPDATE indicator SET quantiletype='QUINTILES'; - UPDATE indicator SET colorsequencename='Precipitation' WHERE code='rainsum'; - UPDATE indicator SET colorsequencename='Blues' WHERE code='frostdaystmin'; - UPDATE indicator SET colorsequencename='Temperature' WHERE code IN ('mint', 'meant', 'maxt'); - UPDATE indicator SET colorsequencename='YlOrRd' WHERE code IN ('hdaystmax', 'hdaystmax1'); - ALTER TABLE indicator ADD CONSTRAINT "CK_indicator_colorsequence" CHECK (nbofclasses IS NOT NULL OR quantiletype IS NOT NULL); - RETURN true; + ALTER TABLE indicator DROP COLUMN colorsequence; + DROP TABLE colorsequence; + ALTER TABLE indicator ADD COLUMN colorsequencename VARCHAR; + ALTER TABLE indicator ADD COLUMN nbofclasses INTEGER; + CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES'); + ALTER TABLE indicator ADD COLUMN quantiletype QUANTILETYPE; + UPDATE indicator SET quantiletype='QUINTILES'; + UPDATE indicator SET colorsequencename='Precipitation' WHERE code='rainsum'; + UPDATE indicator SET colorsequencename='Blues' WHERE code='frostdaystmin'; + UPDATE indicator SET colorsequencename='Temperature' WHERE code IN ('mint', 'meant', 'maxt'); + UPDATE indicator SET colorsequencename='YlOrRd' WHERE code IN ('hdaystmax', 'hdaystmax1'); + ALTER TABLE indicator ADD CONSTRAINT "CK_indicator_colorsequence" CHECK (nbofclasses IS NOT NULL OR quantiletype IS NOT NULL); + RETURN true; END $BODY$ language plpgsql; @@ -112,23 +112,23 @@ language plpgsql; --- CREATE OR REPLACE FUNCTION upgrade20231030() RETURNS boolean AS $BODY$ BEGIN - ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE VARCHAR USING QUANTILETYPE::VARCHAR; - DROP TYPE QUANTILETYPE; - CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES', 'SEXTILES'); - ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE QUANTILETYPE USING quantiletype::quantiletype; - UPDATE indicator SET quantiletype='SEXTILES'; - --- #12 - CREATE TYPE AGGREGATIONTYPE AS ENUM('AVG', 'MAX'); - ALTER TABLE "indicator" ADD COLUMN aggregationtype AGGREGATIONTYPE; - UPDATE indicator SET unit='mm' WHERE code IN ('rainsum'); - UPDATE indicator SET unit='j' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1'); - UPDATE indicator SET unit='°C' WHERE code IN ('maxt', 'meant', 'mint'); - UPDATE indicator SET aggregationtype='MAX' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1', 'rainsum'); - UPDATE indicator SET aggregationtype='AVG' WHERE code IN ('maxt', 'meant', 'mint'); - ALTER TABLE "indicator" ALTER COLUMN aggregationtype SET NOT NULL; - --- #8 - UPDATE indicator SET colorsequencename='TemperatureReversed' WHERE code IN ('mint', 'meant', 'maxt'); - RETURN true; + ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE VARCHAR USING QUANTILETYPE::VARCHAR; + DROP TYPE QUANTILETYPE; + CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES', 'SEXTILES'); + ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE QUANTILETYPE USING quantiletype::quantiletype; + UPDATE indicator SET quantiletype='SEXTILES'; + --- #12 + CREATE TYPE AGGREGATIONTYPE AS ENUM('AVG', 'MAX'); + ALTER TABLE "indicator" ADD COLUMN aggregationtype AGGREGATIONTYPE; + UPDATE indicator SET unit='mm' WHERE code IN ('rainsum'); + UPDATE indicator SET unit='j' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1'); + UPDATE indicator SET unit='°C' WHERE code IN ('maxt', 'meant', 'mint'); + UPDATE indicator SET aggregationtype='MAX' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1', 'rainsum'); + UPDATE indicator SET aggregationtype='AVG' WHERE code IN ('maxt', 'meant', 'mint'); + ALTER TABLE "indicator" ALTER COLUMN aggregationtype SET NOT NULL; + --- #8 + UPDATE indicator SET colorsequencename='TemperatureReversed' WHERE code IN ('mint', 'meant', 'maxt'); + RETURN true; END $BODY$ language plpgsql; @@ -138,8 +138,8 @@ language plpgsql; -- CREATE OR REPLACE FUNCTION upgrade20231215() RETURNS boolean AS $BODY$ BEGIN - INSERT INTO department (id, "name", region) VALUES(75, '75 - Paris', 75); - RETURN true; + INSERT INTO department (id, "name", region) VALUES(75, '75 - Paris', 75); + RETURN true; END $BODY$ language plpgsql; @@ -149,9 +149,9 @@ language plpgsql; -- CREATE OR REPLACE FUNCTION upgrade20240220() RETURNS boolean AS $BODY$ BEGIN - INSERT INTO simulation (date, simulationid, started, ended) VALUES - ('2024-02-19', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - RETURN true; + INSERT INTO simulation (date, simulationid, started, ended) VALUES + ('2024-02-19', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + RETURN true; END $BODY$ language plpgsql; @@ -161,10 +161,27 @@ language plpgsql; -- CREATE OR REPLACE FUNCTION upgrade20240306() RETURNS boolean AS $BODY$ BEGIN - CREATE INDEX IF NOT EXISTS "IX_dailyvalue" ON dailyvalue (date, indicator); - ALTER TABLE indicator DROP COLUMN aggregationtype; - DROP TYPE AGGREGATIONTYPE; - RETURN true; + CREATE INDEX IF NOT EXISTS "IX_dailyvalue" ON dailyvalue USING btree (date, indicator); + ALTER TABLE indicator DROP COLUMN aggregationtype; + DROP TYPE AGGREGATIONTYPE; + RETURN true; +END +$BODY$ +language plpgsql; + +-- +-- #20 +-- +CREATE OR REPLACE FUNCTION upgrade20240313() RETURNS boolean AS $BODY$ +BEGIN + ALTER TABLE period ADD COLUMN firstday VARCHAR(10); + ALTER TABLE period ADD COLUMN lastday VARCHAR(10); + UPDATE period SET firstday='YYYY-01-01', lastday='YYYY-12-31' WHERE code='year'; + UPDATE period SET firstday='YYYY-04-01', lastday='YYYY-10-15' WHERE code='summer'; + UPDATE period SET firstday='XXXX-10-31', lastday='YYYY-08-31' WHERE code='winter'; + ALTER TABLE period ALTER COLUMN firstday SET NOT NULL; + ALTER TABLE period ALTER COLUMN lastday SET NOT NULL; + RETURN true; END $BODY$ language plpgsql; diff --git a/sql/periods.csv b/sql/periods.csv index 97291a0eb8fa20d846d1706b5b3694f5bd6c2d0b..099bcccacdbb8af9f3722ad1928dddb43c8019d2 100644 --- a/sql/periods.csv +++ b/sql/periods.csv @@ -1,4 +1,4 @@ -code,phase -year,s2s8 -summer,s4s7 -winter,s1s6 \ No newline at end of file +code,phase,firstday,lastday +year,s2s8,YYYY-01-01,YYYY-12-31 +summer,s4s7,YYYY-04-01,YYYY-10-15 +winter,s1s6,XXXX-10-31,YYYY-08-31 \ No newline at end of file diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql index 7d7074a10de1aa2edab7cf04faa6ab500deebf33..169877dae6dc4c2931ecee992b5dfe42fb4f309f 100644 --- a/sql/schema.tables.sql +++ b/sql/schema.tables.sql @@ -132,6 +132,8 @@ CREATE TABLE IF NOT EXISTS period ( code VARCHAR NOT NULL, name INTEGER NOT NULL, phase VARCHAR NOT NULL, + firstday VARCHAR(10) NOT NULL, + lastday VARCHAR(10) NOT NULL, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "PK_period" PRIMARY KEY (id), CONSTRAINT "FK_period_i18n" FOREIGN KEY (name) REFERENCES i18n (id), @@ -139,6 +141,8 @@ CREATE TABLE IF NOT EXISTS period ( ); COMMENT ON TABLE period IS 'Period when the indicator is computed.'; COMMENT ON COLUMN period.phase IS 'Related phase name from GETARI file.'; +COMMENT ON COLUMN period.firstday IS 'The first day of the agroclimatic phase.'; +COMMENT ON COLUMN period.lastday IS 'The last day of the agroclimatic phase.'; CREATE TABLE IF NOT EXISTS indicator ( id SERIAL, diff --git a/sql/test_sql_on_postgresql.sh b/sql/test_sql_on_postgresql.sh new file mode 100755 index 0000000000000000000000000000000000000000..a1a38023f0c9f449f235026c6d1b59d64b1f1be0 --- /dev/null +++ b/sql/test_sql_on_postgresql.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd $(dirname $0) +dropdb agrometinfotmp +createdb agrometinfotmp +psql agrometinfotmp -1 -f schema.types.postgresql.sql +psql agrometinfotmp -1 -f schema.tables.sql +psql agrometinfotmp -1 -f init_data.postgresql.sql +sleep 1 +dropdb agrometinfotmp +echo "done" diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Period.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Period.java index 2f4a63efbcd5e27b730f29ebbd029a601f573115..02921226389df685cc6d713607be670c4a438c2c 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Period.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Period.java @@ -36,6 +36,16 @@ public class Period { */ @Column(name = "code") private String code; + /** + * First day of agroclimatic phase. + */ + @Column(name = "firstday") + private String firstDay; + /** + * Last day of agroclimatic phase. + */ + @Column(name = "lastday") + private String lastDay; /** * Defined to use in @JoinTable. */ diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java index 465e6d6db6c951fb38c6edefb550277b2b4c5d21..907b1d21874e189cfd9ea3d2da758f159560d22a 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java @@ -3,7 +3,6 @@ package fr.agrometinfo.www.server.rs; import static fr.agrometinfo.www.server.util.GeometryUtils.toFeature; import java.time.LocalDate; -import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -62,6 +61,19 @@ import lombok.extern.log4j.Log4j2; @RequestScoped public class IndicatorResource implements IndicatorService { + /** + * Get the date related to the year. + * + * @param year year + * @param dateTemplate date template with YYYY for year and XXXX for previous year + * @return date from template. + */ + private static LocalDate getDate(final Integer year, final String dateTemplate) { + return LocalDate.parse(dateTemplate// + .replace("YYYY", String.valueOf(year))// + .replace("XXXX", String.valueOf(year - 1))); + } + private static String getTranslation(final Map<String, String> names, final Locale locale) { final var locales = List.of(locale, Locale.FRENCH, Locale.ENGLISH); for (final Locale l : locales) { @@ -304,6 +316,8 @@ public class IndicatorResource implements IndicatorService { .build()); } + final var firstDay = getDate(year, indicator.getPeriod().getFirstDay()); + final var lastDay = getDate(year, indicator.getPeriod().getLastDay()); final var date = praDailyValueDao.findLastDate(indicator, year); final Double averageValue; final Double comparedValue; @@ -311,7 +325,6 @@ public class IndicatorResource implements IndicatorService { // By default, all key-value pairs in TreeMap are sorted in their natural // ordering. final Map<Date, Float> dailyValues = new TreeMap<>(); - final LocalDate yearAgo = date.minusYears(1); final SimpleFeature parentFeature; final String featureName; if (level == FeatureLevel.REGION) { @@ -332,12 +345,12 @@ public class IndicatorResource implements IndicatorService { featureName = null; averageValue = praDailyValueDao.findAverageComputedValue(indicator, date); comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date); - tmpDailyValues = praDailyValueDao.findDailyValues(indicator, yearAgo, date); + tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay); parentFeature = null; } else { averageValue = praDailyValueDao.findAverageComputedValue(indicator, date, regionId); comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date, regionId); - tmpDailyValues = praDailyValueDao.findDailyValues(indicator, yearAgo, date, region); + tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, region); featureName = region.getName(); parentFeature = null; } @@ -346,7 +359,7 @@ public class IndicatorResource implements IndicatorService { final PraDailyValue value = praDailyValueDao.find(indicator, date, pra); averageValue = value.getComputedValue().doubleValue(); comparedValue = value.getComparedValue().doubleValue(); - tmpDailyValues = praDailyValueDao.findDailyValues(indicator, yearAgo, date, pra); + tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, pra); featureName = pra.getName(); parentFeature = new SimpleFeature(); parentFeature.setId(String.valueOf(pra.getDepartment().getRegion().getId())); @@ -371,7 +384,7 @@ public class IndicatorResource implements IndicatorService { if (comparedValue != null) { dto.setComparedValue(comparedValue.floatValue()); } - dto.setDate(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant())); + dto.setDate(DateUtils.toDate(date)); dto.setDailyValues(dailyValues); if (featureName != null) { dto.setFeatureName(featureName);