branch feature/5923 created (now 1322a83)
This is an automated email from the git hooks/post-receive script. New change to branch feature/5923 in repository wao. See http://git.codelutin.com/wao.git at 1322a83 Remaniement de la génération du graphique du planifié vs réalisé, on introduit un pattern Template Method et trois implémentations (une par programme) This branch includes the following new commits: new 1322a83 Remaniement de la génération du graphique du planifié vs réalisé, on introduit un pattern Template Method et trois implémentations (une par programme) The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 1322a83b593828cef55008860037c3b311392711 Author: Brendan Le Ny <bleny@codelutin.com> Date: Fri Feb 20 11:07:23 2015 +0100 Remaniement de la génération du graphique du planifié vs réalisé, on introduit un pattern Template Method et trois implémentations (une par programme) -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch feature/5923 in repository wao. See http://git.codelutin.com/wao.git commit 1322a83b593828cef55008860037c3b311392711 Author: Brendan Le Ny <bleny@codelutin.com> Date: Fri Feb 20 11:07:23 2015 +0100 Remaniement de la génération du graphique du planifié vs réalisé, on introduit un pattern Template Method et trois implémentations (une par programme) --- .../fr/ifremer/wao/entity/ContactTopiaDao.java | 32 +- .../wao/services/service/SynthesisService.java | 411 +++++++++++++++++---- 2 files changed, 342 insertions(+), 101 deletions(-) diff --git a/wao-persistence/src/main/java/fr/ifremer/wao/entity/ContactTopiaDao.java b/wao-persistence/src/main/java/fr/ifremer/wao/entity/ContactTopiaDao.java index efc04b7..ba9d427 100644 --- a/wao-persistence/src/main/java/fr/ifremer/wao/entity/ContactTopiaDao.java +++ b/wao-persistence/src/main/java/fr/ifremer/wao/entity/ContactTopiaDao.java @@ -21,7 +21,6 @@ package fr.ifremer.wao.entity; * #L% */ -import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; @@ -29,7 +28,6 @@ import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; import fr.ifremer.wao.ContactsFilter; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.Range; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,7 +44,6 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.SortedMap; import java.util.TreeMap; public class ContactTopiaDao extends AbstractContactTopiaDao<Contact> { @@ -207,35 +204,10 @@ public class ContactTopiaDao extends AbstractContactTopiaDao<Contact> { return query.findFirstOrNull(); } - public SortedMap<Date, Integer> getActualObservationsByMonths(Date periodFromMonth, Date periodToMonth, ContactsFilter filter, Function<Date, Date> truncateToTimePeriodFunction) { - + public List<Contact> getContactsWithObservationDoneState(ContactsFilter filter) { HqlAndParametersBuilder<Contact> query = toContactHqlAndParametersBuilder(filter); query.addEquals(Contact.PROPERTY_CONTACT_STATE, ContactState.OBSERVATION_DONE); - List<Contact> contacts = findAll(query.getHql(), query.getHqlParameters()); - - SortedMap<Date, Integer> actualObservationsByPeriods = new TreeMap<>(); - Range<Date> range = Range.between(periodFromMonth, periodToMonth); - - for (Contact contact : contacts) { - Date period = truncateToTimePeriodFunction.apply(contact.getObservationBeginDate()); - if (range.contains(period)) { - Integer count = actualObservationsByPeriods.get(period); - if (count == null) { - count = 0; - } - if (filter.getSampleRowFilter().getObsProgram().isSclerochronology()) { - // le réalisé est le nombre d'individus échantilonnés - count += contact.getSampleSize(); - } else { - // le réalisé est le nombre d'observation, on compte 1 observation pour 1 navire donc 1 pour chaque contact - count += 1; - } - actualObservationsByPeriods.put(period, count); - } - } - - return actualObservationsByPeriods; - + return findAll(query.getHql(), query.getHqlParameters()); } public Map<Boat, LinkedHashMap<String, List<Date>>> getBoardingBoats(ContactsFilter filter) { diff --git a/wao-services/src/main/java/fr/ifremer/wao/services/service/SynthesisService.java b/wao-services/src/main/java/fr/ifremer/wao/services/service/SynthesisService.java index 71fd2fe..c37c12b 100644 --- a/wao-services/src/main/java/fr/ifremer/wao/services/service/SynthesisService.java +++ b/wao-services/src/main/java/fr/ifremer/wao/services/service/SynthesisService.java @@ -21,20 +21,25 @@ package fr.ifremer.wao.services.service; * #L% */ -import com.google.common.base.Function; +import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.cache.Cache; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import fr.ifremer.wao.ContactsFilter; +import fr.ifremer.wao.SampleRowsFilter; import fr.ifremer.wao.WaoUtils; import fr.ifremer.wao.entity.Boat; import fr.ifremer.wao.entity.Company; +import fr.ifremer.wao.entity.Contact; import fr.ifremer.wao.entity.ContactState; import fr.ifremer.wao.entity.ContactTopiaDao; import fr.ifremer.wao.entity.ObsProgram; -import fr.ifremer.wao.entity.SampleRowTopiaDao; +import fr.ifremer.wao.entity.SampleMonth; +import fr.ifremer.wao.entity.SampleRow; import fr.ifremer.wao.entity.SynthesisId; import fr.ifremer.wao.services.AuthenticatedWaoUser; +import org.apache.commons.lang3.Range; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -214,80 +219,49 @@ public class SynthesisService extends WaoServiceSupport { Preconditions.checkArgument(filter.isFilterOnObservationBeginDate()); - Locale locale = serviceContext.getLocale(); - - ObsProgram obsProgram = filter.getSampleRowFilter().getObsProgram(); + SampleRowsFilter sampleRowFilter = filter.getSampleRowFilter(); + ObsProgram obsProgram = sampleRowFilter.getObsProgram(); - Function<Date, Date> truncateToTimePeriodFunction; - Function<Date, String> formatPeriodFunction; - if (obsProgram.isObsMer() || obsProgram.isObsVente()) { - truncateToTimePeriodFunction = WaoUtils.truncateToMonthFunction(); - formatPeriodFunction = WaoUtils.formatMonthFunction(locale); + Locale locale = serviceContext.getLocale(); + Date periodFrom = filter.getPeriodFrom(); + Date periodTo = filter.getPeriodTo(); + Collection<SampleRow> sampleRows = getSampleRowDao().findAll(sampleRowFilter); + Collection<Contact> contacts = getContactDao().getContactsWithObservationDoneState(filter); + boolean realVsEstimated = filter.getRealVsEstimated(); + + // selon le programme, on applique la ou les bons templates de génération du graphe + ExpectedVsActualChartTemplateMethod method; + if (obsProgram.isObsMer()) { + method = ExpectedVsActualChartTemplateMethod.forObsMerInObservations( + locale, + periodFrom, periodTo, + sampleRows, + contacts, + realVsEstimated); + synthesis.setExpectedVsActualObservationsByMonthsChart(method.getChart()); + } else if (obsProgram.isObsVente()) { + method = ExpectedVsActualChartTemplateMethod.forObsVenteInObservations( + locale, + periodFrom, periodTo, + sampleRows, + contacts, + realVsEstimated); + synthesis.setExpectedVsActualObservationsByMonthsChart(method.getChart()); } else if (obsProgram.isSclerochronology()) { - truncateToTimePeriodFunction = WaoUtils.truncateToTrimesterFunction(); - formatPeriodFunction = WaoUtils.formatTrimesterFunction(locale); + method = ExpectedVsActualChartTemplateMethod.forSclerochronologyInObservations( + locale, + periodFrom, periodTo, + sampleRows, + contacts, + realVsEstimated); + synthesis.setExpectedVsActualObservationsByMonthsChart(method.getChart()); } else { - throw new UnsupportedOperationException("unsupported " + obsProgram); + throw new UnsupportedOperationException("obsProgram = " + obsProgram); } - // définition de la fenêtre - Date periodFromMonth = truncateToTimePeriodFunction.apply(filter.getPeriodFrom()); - Date periodToMonth = truncateToTimePeriodFunction.apply(filter.getPeriodTo()); - - // Calcul du programmé - SampleRowTopiaDao dao = getSampleRowDao(); - SortedMap<Date, Integer> expectedObservationsByMonths = - dao.getExpectedObservationsByMonths(periodFromMonth, periodToMonth, filter.getSampleRowFilter(), truncateToTimePeriodFunction); - - // Calcul du réalisé - ContactTopiaDao contactDao = getContactDao(); - SortedMap<Date, Integer> actualObservationsMyMonths = - contactDao.getActualObservationsByMonths(periodFromMonth, periodToMonth, filter, truncateToTimePeriodFunction); - - DefaultCategoryDataset dataset = new DefaultCategoryDataset(); - - for (Map.Entry<Date, Integer> entry : expectedObservationsByMonths.entrySet()) { - Date period = entry.getKey(); - Integer expected = entry.getValue(); - dataset.setValue(expected, I18n.l(locale, "wao.synthesis.planned"), formatPeriodFunction.apply(period)); - } - String rowKey; - if (filter.getRealVsEstimated()) { - rowKey = I18n.l(locale, "wao.ui.samplingPlan.Actual"); - } else { - rowKey = I18n.l(locale, "wao.synthesis.estimated"); - } - for (Map.Entry<Date, Integer> entry : actualObservationsMyMonths.entrySet()) { - Date period = entry.getKey(); - Integer actual = entry.getValue(); - dataset.setValue(actual, rowKey, formatPeriodFunction.apply(period)); - } - - // Axises - CategoryAxis categoryAxis = new CategoryAxis(""); - - String valueAxisLabel; - if (obsProgram.isSclerochronology()) { - valueAxisLabel = I18n.l(locale, "wao.synthesis.observationsCount.sclerochronology"); - } else { - valueAxisLabel = I18n.l(locale, "wao.synthesis.observationsCount"); - } - ValueAxis valueAxis = new NumberAxis(valueAxisLabel); - valueAxis.setUpperMargin(0.15); - - // Renderer for Category - AbstractCategoryItemRenderer renderer = new BarRenderer(); - // Show labels on each element - renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); - renderer.setBaseItemLabelsVisible(Boolean.TRUE); - - CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); - plot.setOrientation(PlotOrientation.VERTICAL); - plot.setAxisOffset(RectangleInsets.ZERO_INSETS); - - JFreeChart expectedVsActualObservationsByMonthsChart = - new JFreeChart(I18n.l(locale, SynthesisId.GRAPH_SAMPLING.getI18nKey()), JFreeChart.DEFAULT_TITLE_FONT, plot, true); - synthesis.setExpectedVsActualObservationsByMonthsChart(expectedVsActualObservationsByMonthsChart); + Preconditions.checkNotNull( + synthesis.getExpectedVsActualObservationsByMonthsChart(), + "all synthesis must provide expectedVsActualObservationsByMonthsChart"); } @@ -426,4 +400,299 @@ public class SynthesisService extends WaoServiceSupport { synthesis.setBoardingBoatsChart(boardingBoatsChart); } + + /** + * Représente l'algorithme de génération d'un graphique qui permet de comparer le planifié + * au réalisé. + * + * On utilise le patron de conception Template Method car on doit pouvoir décomposer en mois + * ou en trimestres, varier les libellés et les modes de calculs selon le {@link fr.ifremer.wao.entity.ObsProgram} + */ + protected abstract static class ExpectedVsActualChartTemplateMethod { + + /** + * La locale, utile pour les libellés + */ + protected Locale locale; + + protected Date periodFrom; + + protected Date periodTo; + + protected ImmutableSet<SampleRow> sampleRows; + + /** + * Les contacts réalisés ({@link fr.ifremer.wao.entity.ContactState#OBSERVATION_DONE}) pour + * les lignes {@link #sampleRows} + */ + protected ImmutableSet<Contact> contacts; + + protected boolean realVsEstimated; + + public static ExpectedVsActualChartTemplateMethod forObsMerInObservations(Locale locale, Date periodFrom, Date periodTo, Collection<SampleRow> sampleRows, Collection<Contact> contacts, boolean realVsEstimated) { + ExpectedVsActualChartTemplateMethod method = new ExpectedVsActualObsMerObservationsChartMethod(); + return buildMethod(method, locale, periodFrom, periodTo, sampleRows, contacts, realVsEstimated); + } + + public static ExpectedVsActualChartTemplateMethod forObsVenteInObservations(Locale locale, Date periodFrom, Date periodTo, Collection<SampleRow> sampleRows, Collection<Contact> contacts, boolean realVsEstimated) { + ExpectedVsActualChartTemplateMethod method = new ExpectedVsActualObsVenteObservationsChartMethod(); + return buildMethod(method, locale, periodFrom, periodTo, sampleRows, contacts, realVsEstimated); + } + + public static ExpectedVsActualChartTemplateMethod forSclerochronologyInObservations(Locale locale, Date periodFrom, Date periodTo, Collection<SampleRow> sampleRows, Collection<Contact> contacts, boolean realVsEstimated) { + ExpectedVsActualChartTemplateMethod method = new ExpectedVsActualSclerochronologyObservationsChartMethod(); + return buildMethod(method, locale, periodFrom, periodTo, sampleRows, contacts, realVsEstimated); + } + + protected static ExpectedVsActualChartTemplateMethod buildMethod(ExpectedVsActualChartTemplateMethod method, Locale locale, Date periodFrom, Date periodTo, Collection<SampleRow> sampleRows, Collection<Contact> contacts, boolean realVsEstimated) { + method.locale = locale; + method.periodFrom = periodFrom; + method.periodTo = periodTo; + method.sampleRows = ImmutableSet.copyOf(sampleRows); + method.contacts = ImmutableSet.copyOf(contacts); + method.realVsEstimated = realVsEstimated; + return method; + } + + public JFreeChart getChart() { + + // définition de la fenêtre + Date periodFromMonth = truncateToTimePeriodFunction(periodFrom); + Date periodToMonth = truncateToTimePeriodFunction(periodTo); + + Range<Date> periodRange = Range.between(periodFromMonth, periodToMonth); + + // Calcul du programmé + SortedMap<Date, Integer> expectedEffortByPeriods = new TreeMap<>(); + for (SampleRow sampleRow : sampleRows) { + for (SampleMonth sampleMonth : sampleRow.getSampleMonth()) { + Date month = sampleMonth.getPeriodDate(); + Date period = truncateToTimePeriodFunction(month); + if (periodRange.contains(period)) { + Integer expected = MoreObjects.firstNonNull( + expectedEffortByPeriods.get(period), + 0); + expected += getExpected(sampleMonth); + expectedEffortByPeriods.put(period, expected); + } + } + } + + if (log.isDebugEnabled()) { + log.debug("expected effort by periods is " + expectedEffortByPeriods); + } + + // Calcul du réalisé + SortedMap<Date, Integer> actualObservationsByPeriods = new TreeMap<>(); + for (Contact contact : contacts) { + Preconditions.checkState(sampleRows.contains(contact.getSampleRow())); + Date period = truncateToTimePeriodFunction(contact.getObservationBeginDate()); + if (periodRange.contains(period)) { + Integer count = MoreObjects.firstNonNull( + actualObservationsByPeriods.get(period), + 0); + count += getActual(contact); + actualObservationsByPeriods.put(period, count); + } + } + + if (log.isDebugEnabled()) { + log.debug("actual effort by periods is " + actualObservationsByPeriods); + } + + // Création du graphique + DefaultCategoryDataset dataset = new DefaultCategoryDataset(); + + for (Map.Entry<Date, Integer> entry : expectedEffortByPeriods.entrySet()) { + Date period = entry.getKey(); + Integer expected = entry.getValue(); + dataset.setValue(expected, I18n.l(locale, "wao.synthesis.planned"), formatPeriod(period)); + } + String rowKey; + if (realVsEstimated) { + rowKey = I18n.l(locale, "wao.ui.samplingPlan.Actual"); + } else { + rowKey = I18n.l(locale, "wao.synthesis.estimated"); + } + for (Map.Entry<Date, Integer> entry : actualObservationsByPeriods.entrySet()) { + Date period = entry.getKey(); + Integer actual = entry.getValue(); + dataset.setValue(actual, rowKey, formatPeriod(period)); + } + + // Axises + CategoryAxis categoryAxis = new CategoryAxis(""); + + String valueAxisLabel = getValueAxisLabel(); + ValueAxis valueAxis = new NumberAxis(valueAxisLabel); + valueAxis.setUpperMargin(0.15); + + // Renderer for Category + AbstractCategoryItemRenderer renderer = new BarRenderer(); + // Show labels on each element + renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); + renderer.setBaseItemLabelsVisible(Boolean.TRUE); + + CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); + plot.setOrientation(PlotOrientation.VERTICAL); + plot.setAxisOffset(RectangleInsets.ZERO_INSETS); + + JFreeChart chart = new JFreeChart( + I18n.l(locale, SynthesisId.GRAPH_SAMPLING.getI18nKey()), + JFreeChart.DEFAULT_TITLE_FONT, plot, true); + + return chart; + + } + + /** + * Combien il faut compter de réalisé pour ce contact. + */ + protected abstract int getActual(Contact contact); + + protected abstract String getValueAxisLabel(); + + /** + * Combien il faut compter d'effort planifié pour ce mois. + */ + protected abstract int getExpected(SampleMonth sampleMonth); + + /** + * Pour une date, indique dans quelle période de temps elle se trouve (mois ? trimestre ?) + */ + protected abstract Date truncateToTimePeriodFunction(Date period); + + protected abstract String formatPeriod(Date period); + + /** + * Pour ObsMer en nombre d'observation. + */ + protected static class ExpectedVsActualObsMerObservationsChartMethod extends ExpectedVsActualChartTemplateMethod { + + /** + * On est sur un découpage mensuel. + */ + @Override + protected String formatPeriod(Date period) { + return WaoUtils.formatMonth(locale, period); + } + + /** + * On est sur un découpage mensuel. + */ + @Override + protected Date truncateToTimePeriodFunction(Date period) { + return WaoUtils.truncateToMonth(period); + } + + /** + * Le nombre de sorties en mer. + */ + @Override + protected int getExpected(SampleMonth sampleMonth) { + return sampleMonth.getExpectedTidesValue(); + } + + @Override + protected String getValueAxisLabel() { + return I18n.l(locale, "wao.synthesis.observationsCount"); + } + + /** + * Le réalisé est le nombre d'observation, on compte 1 observation pour 1 navire donc 1 pour chaque contact. + */ + @Override + protected int getActual(Contact contact) { + return 1; + } + } + + /** + * Pour ObsVente en nombre d'observation. + */ + protected static class ExpectedVsActualObsVenteObservationsChartMethod extends ExpectedVsActualChartTemplateMethod { + + /** + * On est sur un découpage mensuel. + */ + @Override + protected String formatPeriod(Date period) { + return WaoUtils.formatMonth(locale, period); + } + + /** + * On est sur un découpage mensuel. + */ + @Override + protected Date truncateToTimePeriodFunction(Date period) { + return WaoUtils.truncateToMonth(period); + } + + /** + * Pour ObsVente, le nombre de sortie × le nombre de navires qu'il est demandé d'observer par sortie. + */ + @Override + protected int getExpected(SampleMonth sampleMonth) { + int averageObservationsCount = sampleMonth.getSampleRow().getAverageObservationsCount(); + return sampleMonth.getExpectedTidesValue() * averageObservationsCount; + } + + @Override + protected String getValueAxisLabel() { + return I18n.l(locale, "wao.synthesis.observationsCount"); + } + + /** + * Le réalisé est le nombre d'observation, on compte 1 observation pour 1 navire donc 1 pour chaque contact. + */ + @Override + protected int getActual(Contact contact) { + return 1; + } + } + + /** + * Pour Sclerochronologie en nombre d'observation. + */ + protected static class ExpectedVsActualSclerochronologyObservationsChartMethod extends ExpectedVsActualChartTemplateMethod { + + /** + * On est sur un découpage en trimestres. + */ + @Override + protected String formatPeriod(Date period) { + return WaoUtils.formatTrimester(locale, period); + } + + /** + * On est sur un découpage en trimestres. + */ + @Override + protected Date truncateToTimePeriodFunction(Date period) { + return WaoUtils.truncateToTrimester(period); + } + + /** + * Le nombre d'individu à observer. + */ + @Override + protected int getExpected(SampleMonth sampleMonth) { + return sampleMonth.getExpectedTidesValue(); + } + + @Override + protected String getValueAxisLabel() { + return I18n.l(locale, "wao.synthesis.observationsCount.sclerochronology"); + } + + /** + * Le réalisé est le nombre d'individus échantilonnés. + */ + @Override + protected int getActual(Contact contact) { + return contact.getSampleSize(); + } + } + + } } -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
participants (1)
-
codelutin.com scm