diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java index 997413fe510066455709b61d9b7a68353523be1b..4016583017d2d2656ec0f3a4c2d570fa019be1d9 100644 --- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java +++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LayoutPresenter.java @@ -9,6 +9,9 @@ import org.dominokit.rest.shared.request.FailedResponseBean; import com.google.gwt.core.client.GWT; +import fr.agrometinfo.www.client.App; +import fr.agrometinfo.www.client.event.LoadingEvent; +import fr.agrometinfo.www.client.event.LoadingEvent.LoadingEventType; import fr.agrometinfo.www.client.i18n.AppConstants; import fr.agrometinfo.www.client.view.BaseView; import fr.agrometinfo.www.client.view.LayoutView; @@ -89,6 +92,11 @@ public final class LayoutPresenter implements Presenter { */ private final Map<String, String> regions = new HashMap<>(); + /** + * Year chosen by the user. + */ + private Integer chosenYear; + /** * @see https://github.com/gwtproject/gwt/issues/7631#issuecomment-110876116 * @return if the application is running in (Super) DevMode. @@ -116,6 +124,7 @@ public final class LayoutPresenter implements Presenter { if ("0".equals(choice.getFeatureId())) { choice.setFeatureId(null); } + chosenYear = choice.getYear(); final String regionName = regions.getOrDefault(choice.getFeatureId(), CSTS.metropolitanFrance()); mapPresenter.loadValues(choice, chosenIndicator, periodName, regionName); rightPanelPresenter.loadValues(choice, periodName); @@ -130,10 +139,12 @@ public final class LayoutPresenter implements Presenter { this.periods = list; view.setPeriods(list); - IndicatorServiceFactory.INSTANCE.getRegions() // + IndicatorServiceFactory.INSTANCE.getRegions(chosenYear) // .onSuccess(this::setRegions) // .onFailed(view::failureNotification) // + .onComplete(() -> App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.END))) // .send(); + App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.START)); } /** @@ -143,11 +154,6 @@ public final class LayoutPresenter implements Presenter { regions.clear(); regions.putAll(list); view.setRegions(list); - - IndicatorServiceFactory.INSTANCE.getYears() // - .onSuccess(this::setYears) // - .onFailed(view::failureNotification) // - .send(); } /** @@ -155,6 +161,16 @@ public final class LayoutPresenter implements Presenter { */ private void setYears(final List<Integer> list) { view.setYears(list); + + if (!list.isEmpty()) { + chosenYear = list.get(list.size() - 1); + IndicatorServiceFactory.INSTANCE.getPeriods(chosenYear) // + .onSuccess(this::setPeriods) // + .onFailed(view::failureNotification) // + .onComplete(() -> App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.END))) // + .send(); + App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.START)); + } } /** @@ -180,10 +196,11 @@ public final class LayoutPresenter implements Presenter { new WelcomePresenter().start(); - IndicatorServiceFactory.INSTANCE.getPeriods() // - .onSuccess(this::setPeriods) // + IndicatorServiceFactory.INSTANCE.getYears() // + .onSuccess(this::setYears) // .onFailed(view::failureNotification) // + .onComplete(() -> App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.END))) // .send(); - + App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.START)); } } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDao.java index e3357b74a74dfc90635f1b96c4d052df5be3bde5..7a8e2056744fa2b6d6eca43b2c5dd3237c185064 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDao.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDao.java @@ -90,6 +90,12 @@ public interface DailyValueDao { */ List<Region> findRegions(); + /** + * @param year the year of indicator values + * @return the regions related to computed values for a year + */ + List<Region> findRegionsByYear(Integer year); + /** * @return the years related to computed values */ diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java index 34845a8e1c30d40f5250b95a97233ea5aec15b98..7453bf0c7d1eacde57f75700e17fda87887611d8 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java @@ -107,6 +107,19 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements return super.findAllByJPQL(jpql, null, Region.class); } + @Override + public final List<Region> findRegionsByYear(final Integer year) { + final var jpql = """ + SELECT DISTINCT r + FROM DailyValue AS t + JOIN t.cell AS c + JOIN c.department AS d + JOIN d.region AS r + WHERE EXTRACT(YEAR FROM t.date) = :year + ORDER BY r.name"""; + return super.findAllByJPQL(jpql, Map.of("year", year), Region.class); + } + @Override public final List<Integer> findYears() { final var jpql = "SELECT DISTINCT EXTRACT(YEAR FROM date) AS year FROM DailyValue AS t ORDER BY year"; diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java index d35b0b9976a7e4305b879ed567e6b9bea8f1601d..2545bae33f3ef449b846defe0643c2933f919865 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java @@ -167,6 +167,28 @@ public abstract class DaoHibernate<T> { return new ArrayList<>(); } } + /** + * Find all matching entities using SQL and parameters for entities of the + * specified class. + * + * + * @param <U> entity class + * @param sql SQL statement + * @param params parameters for the SQL statement + * @return the found entities instance or empty list if no entity matches. + */ + @SuppressWarnings("unchecked") + protected final <U> List<U> findAllBySQL(final String sql, final Map<String, Object> params) { + try (ScopedEntityManager em = getScopedEntityManager()) { + final Query query = em.createNativeQuery(sql); + if (params != null) { + params.forEach(query::setParameter); + } + return query.getResultList(); + } catch (final NoResultException e) { + return new ArrayList<>(); + } + } /** * Find the first result. diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java index f83e54db558409eced8cd690ad8bb335f1daa1b2..105d1acf37a73cf8da7404b7d02acf627f638399 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java @@ -119,21 +119,9 @@ public interface PraDailyValueDao { Map<LocalDate, Float> findDailyValues(Indicator indicator, LocalDate start, LocalDate end, Region region); /** + * @param year the year of indicator values * @return the indicators (with period) related to computed values */ - List<Indicator> findIndicators(); + List<Indicator> findIndicators(Integer year); - /** - * The last {@link DailyValue#getDate()} for the indicator on a year. - * - * @param indicator indicator to search - * @param year year - * @return last date or null - */ - LocalDate findLastDate(Indicator indicator, Integer year); - - /** - * @return the years related to computed values - */ - List<Integer> findYears(); } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java index 4c5b76313b897f0d1d5247ab19fc492a71ddb5e5..9d96a3b0b4412da6782576cb5a8e39ae96a0edbb 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java @@ -18,6 +18,7 @@ import jakarta.persistence.NoResultException; import jakarta.persistence.Query; import jakarta.persistence.Tuple; import jakarta.persistence.TypedQuery; +import lombok.NonNull; /** * Hibernate implementation of {@link PraDailyValueDao}. @@ -220,18 +221,12 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple } @Override - public final List<Indicator> findIndicators() { - final var jpql = "SELECT DISTINCT i FROM PraDailyValue AS t JOIN t.indicator AS i JOIN t.indicator.period"; - return super.findAllByJPQL(jpql, null, Indicator.class); - } - - @Override - public final LocalDate findLastDate(final Indicator indicator, final Integer year) { - final var jpql = """ - SELECT MAX(t.date) - FROM PraDailyValue AS t - WHERE t.indicator=:indicator AND EXTRACT(YEAR FROM t.date) = :year"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "year", year), LocalDate.class); + public final List<Indicator> findIndicators(@NonNull final Integer year) { + final var sql = "SELECT DISTINCT indicator FROM dailyvalue WHERE EXTRACT(YEAR FROM date) = :year"; + final List<Long> ids = super.findAllBySQL(sql, Map.of("year", year)).stream() // + .map(i -> Long.valueOf((int) i)).toList(); + final var jpql = "SELECT i FROM Indicator AS i JOIN i.period WHERE i.id IN (:ids)"; + return super.findAllByJPQL(jpql, Map.of("ids", ids), Indicator.class); } private List<PraDailyValue> findPraDailyValues(final String sql, final Map<String, Object> params) { @@ -248,10 +243,4 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple return values; } - @Override - public final List<Integer> findYears() { - final var jpql = "SELECT DISTINCT EXTRACT(YEAR FROM date) AS year FROM PraDailyValue AS t ORDER BY year"; - return super.findAllByJPQL(jpql, null, Integer.class); - } - } 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 907b1d21874e189cfd9ea3d2da758f159560d22a..73edaba2ccc2e891a0df654fd9589cb4ee0778a1 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 @@ -16,6 +16,7 @@ import org.geojson.Feature; import org.geojson.FeatureCollection; import fr.agrometinfo.www.server.dao.CellDao; +import fr.agrometinfo.www.server.dao.DailyValueDao; import fr.agrometinfo.www.server.dao.IndicatorDao; import fr.agrometinfo.www.server.dao.PraDailyValueDao; import fr.agrometinfo.www.server.dao.PraDao; @@ -49,6 +50,7 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Request; import jakarta.ws.rs.core.Response; +import lombok.NonNull; import lombok.extern.log4j.Log4j2; /** @@ -136,6 +138,12 @@ public class IndicatorResource implements IndicatorService { @Inject private CellDao cellDao; + /** + * DAO for {@link dailyValue}. + */ + @Inject + private DailyValueDao dailyValueDao; + /** * DAO for {@link PraDailyValue}. */ @@ -210,19 +218,16 @@ public class IndicatorResource implements IndicatorService { return DateUtils.toDate(cacheService.getLastModification().toLocalDate()); } - /** - * @return indicator categories with their indicators - */ @SuppressWarnings("unchecked") @GET @Path(IndicatorService.PATH_LIST) @Produces(MediaType.APPLICATION_JSON) @Override - public List<PeriodDTO> getPeriods() { + public List<PeriodDTO> getPeriods(@QueryParam(value = "year") @NonNull final Integer year) { LOGGER.traceEntry(); final var locale = LocaleUtils.getLocale(httpServletRequest); // HTTP cache headers - cacheService.setCacheKey(IndicatorService.PATH, IndicatorService.PATH_LIST, locale); + cacheService.setCacheKey(IndicatorService.PATH, IndicatorService.PATH_LIST, year, locale); cacheService.setHeaders(httpHeaders); if (!cacheService.needsResponse(request)) { return List.of(); @@ -232,7 +237,7 @@ public class IndicatorResource implements IndicatorService { return (List<PeriodDTO>) cacheService.getCache(); } // - final var indicators = praDailyValueDao.findIndicators(); + final var indicators = praDailyValueDao.findIndicators(year); final Map<Long, PeriodDTO> dtos = new LinkedHashMap<>(); for (final Indicator indicator : indicators) { final var p = indicator.getPeriod(); @@ -262,9 +267,9 @@ public class IndicatorResource implements IndicatorService { @Path(IndicatorService.PATH_REGIONS) @Produces(MediaType.APPLICATION_JSON) @Override - public Map<String, String> getRegions() { + public Map<String, String> getRegions(@QueryParam(value = "year") @NonNull final Integer year) { // HTTP cache headers - cacheService.setCacheKey(IndicatorService.PATH, IndicatorService.PATH_REGIONS); + cacheService.setCacheKey(IndicatorService.PATH, IndicatorService.PATH_REGIONS, year); cacheService.setHeaders(httpHeaders); if (!cacheService.needsResponse(request)) { return Map.of(); @@ -274,7 +279,7 @@ public class IndicatorResource implements IndicatorService { return (Map<String, String>) cacheService.getCache(); } // - final Map<String, String> result = regionDao.findAll().stream()// + final Map<String, String> result = dailyValueDao.findRegionsByYear(year).stream()// .collect(LinkedHashMap::new, // (map, item) -> map.put(String.valueOf(item.getId()), item.getName()), // Map::putAll); @@ -318,7 +323,7 @@ public class IndicatorResource implements IndicatorService { final var firstDay = getDate(year, indicator.getPeriod().getFirstDay()); final var lastDay = getDate(year, indicator.getPeriod().getLastDay()); - final var date = praDailyValueDao.findLastDate(indicator, year); + final var date = dailyValueDao.findLastDate(indicator, year); final Double averageValue; final Double comparedValue; final Map<LocalDate, Float> tmpDailyValues; @@ -421,7 +426,7 @@ public class IndicatorResource implements IndicatorService { // final FeatureCollection collection = new FeatureCollection(); final Indicator indicator = indicatorDao.findByCodeAndPeriod(indicatorUid, periodCode); - final LocalDate date = praDailyValueDao.findLastDate(indicator, year); + final LocalDate date = dailyValueDao.findLastDate(indicator, year); final Region region; if (regionId == null) { region = null; @@ -465,7 +470,7 @@ public class IndicatorResource implements IndicatorService { return (List<Integer>) cacheService.getCache(); } // - final List<Integer> result = praDailyValueDao.findYears(); + final List<Integer> result = dailyValueDao.findYears(); cacheService.setCache(result); return result; } diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernateTest.java index 8b7ffa6663ddb7eb889551dc08df60c88c1d61d1..299f14ed47ae3cc4be23d6c8827d28d7cb183a0a 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernateTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernateTest.java @@ -11,6 +11,11 @@ import fr.agrometinfo.www.server.model.Indicator; * Test DailyValueDao Hibernate implementation. */ class DailyValueDaoHibernateTest { + /** + * The year with data. + */ + static final int YEAR = 2023; + /** * DAO to test. */ @@ -24,7 +29,7 @@ class DailyValueDaoHibernateTest { @Test void findAverageComputedValue() { final var indicator = indicatorDao.findByCodeAndPeriod("rainsum", "year"); - final var date = dao.findLastDate(indicator, 2023); + final var date = dao.findLastDate(indicator, YEAR); final var region = dao.findRegions().get(0); final var actual = dao.findAverageComputedValue(indicator, date, (int) region.getId()); final Double expected = 22.d; @@ -43,7 +48,7 @@ class DailyValueDaoHibernateTest { @Test void findLastDate() { final var indicator = indicatorDao.findByCodeAndPeriod("rainsum", "year"); - final var actual = dao.findLastDate(indicator, 2023); + final var actual = dao.findLastDate(indicator, YEAR); assertNotNull(actual); } @@ -54,6 +59,13 @@ class DailyValueDaoHibernateTest { assertEquals(Integer.valueOf(1), actual.size()); } + @Test + void findRegionsByYear() { + final var actual = dao.findRegionsByYear(YEAR); + assertNotNull(actual); + assertEquals(Integer.valueOf(1), actual.size()); + } + @Test void findYears() { final var actual = dao.findYears(); diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernateTest.java similarity index 51% rename from www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueHibernateTest.java rename to www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernateTest.java index 9813042ad1d9089c1d20903d832b9a318abd8d72..448a19facbb6e14250d3796aa85e97c3ec4eabf0 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueHibernateTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernateTest.java @@ -1,5 +1,6 @@ package fr.agrometinfo.www.server.dao; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; @@ -9,7 +10,13 @@ import fr.agrometinfo.www.server.model.Indicator; /** * Test PraDailyValueDao Hibernate implementation. */ -public class PraDailyValueHibernateTest { +class PraDailyValueDaoHibernateTest { + + /** + * DAO to test. + */ + private final DailyValueDao dailyValueDao = new DailyValueDaoHibernate(); + /** * DAO to test. */ @@ -23,8 +30,17 @@ public class PraDailyValueHibernateTest { @Test void find() { final var indicator = indicatorDao.findByCodeAndPeriod("rainsum", "year"); - final var date = dao.findLastDate(indicator, 2023); + final var date = dailyValueDao.findLastDate(indicator, DailyValueDaoHibernateTest.YEAR); final var actual = dao.find(indicator, date); assertNotNull(actual); } + + @Test + void findIndicators() { + final var actual = dao.findIndicators(DailyValueDaoHibernateTest.YEAR); + assertNotNull(actual); + assertEquals(Integer.valueOf(1), actual.size()); + assertEquals("rainsum", actual.get(0).getCode()); + assertEquals("year", actual.get(0).getPeriod().getCode()); + } } diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java index 7892f8c64a4fd6cc164e2a7c7eaf8e15409aa205..86ef704bcad6b6456c0ff86616cd6f27a21ed086 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Test; import fr.agrometinfo.www.server.dao.CellDao; import fr.agrometinfo.www.server.dao.CellDaoHibernate; +import fr.agrometinfo.www.server.dao.DailyValueDao; +import fr.agrometinfo.www.server.dao.DailyValueDaoHibernate; import fr.agrometinfo.www.server.dao.IndicatorDao; import fr.agrometinfo.www.server.dao.IndicatorDaoHibernate; import fr.agrometinfo.www.server.dao.PraDailyValueDao; @@ -65,6 +67,7 @@ class IndicatorResourceTest extends JerseyTest { @Override protected final Application configure() { final CellDao cellDao = new CellDaoHibernate(); + final DailyValueDao dailyValueDao = new DailyValueDaoHibernate(); final PraDailyValueDao praDailyValueDao = new PraDailyValueDaoHibernate(); final PraDao praDao = new PraDaoHibernate(); final IndicatorDao indicatorDao = new IndicatorDaoHibernate(); @@ -77,6 +80,7 @@ class IndicatorResourceTest extends JerseyTest { @Override public void configure() { bind(cellDao).to(CellDao.class); + bind(dailyValueDao).to(DailyValueDao.class); bind(praDailyValueDao).to(PraDailyValueDao.class); bind(praDao).to(PraDao.class); bind(indicatorDao).to(IndicatorDao.class); diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java index 711b5168ba973c077c09edcca648bfe188e1960b..e0fec0132ec016426f75d2e223cca0c2547bc596 100644 --- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java +++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java @@ -62,18 +62,20 @@ public interface IndicatorService { Date getLastModification(); /** - * @return list of years of computed indicators. + * @param year the year of indicator values + * @return list of periods with computed indicators. */ @GET @Path(PATH_LIST) - List<PeriodDTO> getPeriods(); + List<PeriodDTO> getPeriods(@QueryParam(value = "year") Integer year); /** + * @param year the year of indicator values * @return list of regions (id : name) of computed indicators. */ @GET @Path(PATH_REGIONS) - Map<String, String> getRegions(); + Map<String, String> getRegions(@QueryParam(value = "year") Integer year); /** * @param indicator indicator code