DecisionFlowChartManagerImpl.java

  1. /*
  2.  * Copyright 2010-2025 James Pether Sörling
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *   http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  *
  16.  *  $Id$
  17.  *  $HeadURL$
  18. */
  19. package com.hack23.cia.web.impl.ui.application.views.common.chartfactory.impl;

  20. import java.util.Collection;
  21. import java.util.List;
  22. import java.util.Locale;
  23. import java.util.Map;
  24. import java.util.Objects;
  25. import java.util.Optional;
  26. import java.util.function.Function;
  27. import java.util.stream.Collectors;

  28. import org.springframework.beans.factory.annotation.Autowired;
  29. import org.springframework.stereotype.Service;

  30. import com.hack23.cia.model.internal.application.data.committee.impl.ViewRiksdagenCommittee;
  31. import com.hack23.cia.web.impl.ui.application.views.common.chartfactory.api.DecisionFlowChartManager;
  32. import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.DecisionDataFactory;
  33. import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.ProposalCommitteeeSummary;
  34. import com.hack23.cia.web.widgets.charts.SankeyChart;
  35. import com.vaadin.ui.TextArea;

  36. /**
  37.  * The Class DecisionFlowChartManagerImpl.
  38.  */
  39. @Service
  40. public final class DecisionFlowChartManagerImpl implements DecisionFlowChartManager {

  41.     /** The decision data factory. */
  42.     @Autowired
  43.     private DecisionDataFactory decisionDataFactory;

  44.     /**
  45.      * Instantiates a new decision flow chart manager impl.
  46.      */
  47.     public DecisionFlowChartManagerImpl() {
  48.         super();
  49.     }

  50.     /**
  51.      * Gets the org proposal map.
  52.      *
  53.      * @param reportMonth the report month
  54.      * @return the org proposal map
  55.      */
  56.     // Core helper methods for common operations
  57.     private Map<String, List<ProposalCommitteeeSummary>> getOrgProposalMap(final String reportMonth) {
  58.         return decisionDataFactory.createCommitteeSummary(reportMonth)
  59.                 .stream()
  60.                 .filter(Objects::nonNull)
  61.                 .collect(Collectors.groupingBy(
  62.                     ProposalCommitteeeSummary::org,
  63.                     Collectors.mapping(
  64.                         Function.identity(),
  65.                         Collectors.toList()
  66.                     )
  67.                 ));
  68.     }

  69.     /**
  70.      * Find committee.
  71.      *
  72.      * @param committeeSummaryMap the committee summary map
  73.      * @param orgKey the org key
  74.      * @return the optional
  75.      */
  76.     private Optional<ViewRiksdagenCommittee> findCommittee(
  77.             final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
  78.             final String orgKey) {
  79.         return Optional.ofNullable(committeeSummaryMap.get(orgKey))
  80.                 .flatMap(list -> list.stream().findFirst());
  81.     }

  82.     /**
  83.      * Creates the all decision flow.
  84.      *
  85.      * @param committeeSummaryMap the committee summary map
  86.      * @param reportMonth the report month
  87.      * @return the sankey chart
  88.      */
  89.     @Override
  90.     public SankeyChart createAllDecisionFlow(
  91.             final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
  92.             final String reportMonth) {

  93.         Objects.requireNonNull(committeeSummaryMap, "Committee summary map cannot be null");
  94.         Objects.requireNonNull(reportMonth, "Report month cannot be null");

  95.         final SankeyChart sankeyChart = new SankeyChart();

  96.         getOrgProposalMap(reportMonth).entrySet().stream()
  97.             .filter(entry -> entry.getKey() != null && !entry.getValue().isEmpty())
  98.             .forEach(entry ->
  99.                 findCommittee(committeeSummaryMap, entry.getKey())
  100.                     .ifPresent(committee ->
  101.                         addChartData(sankeyChart, entry.getValue(), committee)));

  102.         sankeyChart.drawChart();
  103.         return sankeyChart;
  104.     }

  105.     /**
  106.      * Creates the committee decision flow.
  107.      *
  108.      * @param viewRiksdagenCommittee the view riksdagen committee
  109.      * @param committeeSummaryMap the committee summary map
  110.      * @param reportMonth the report month
  111.      * @return the sankey chart
  112.      */
  113.     @Override
  114.     public SankeyChart createCommitteeDecisionFlow(
  115.             final ViewRiksdagenCommittee viewRiksdagenCommittee,
  116.             final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
  117.             final String reportMonth) {

  118.         Objects.requireNonNull(viewRiksdagenCommittee, "Committee cannot be null");

  119.         final SankeyChart sankeyChart = new SankeyChart();
  120.         final String targetOrg = viewRiksdagenCommittee.getEmbeddedId().getOrgCode().toUpperCase(Locale.ENGLISH);

  121.         getOrgProposalMap(reportMonth).entrySet().stream()
  122.             .filter(entry -> targetOrg.equals(entry.getKey().toUpperCase(Locale.ENGLISH)))
  123.             .forEach(entry -> addDocTypeDecisionDataRows(sankeyChart, entry.getValue()));

  124.         sankeyChart.drawChart();
  125.         return sankeyChart;
  126.     }

  127.     /**
  128.      * Creates the committeee decision summary.
  129.      *
  130.      * @param committeeSummaryMap the committee summary map
  131.      * @param reportMonth the report month
  132.      * @return the text area
  133.      */
  134.     @Override
  135.     public TextArea createCommitteeeDecisionSummary(
  136.             final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
  137.             final String reportMonth) {

  138.         final TextArea area = new TextArea("Summary");
  139.         final StringBuilder builder = new StringBuilder();
  140.         final Map<String, List<ProposalCommitteeeSummary>> orgProposalMap = getOrgProposalMap(reportMonth);

  141.         orgProposalMap.forEach((orgKey, proposals) -> {
  142.             final String orgKeyUpper = orgKey.toUpperCase(Locale.ENGLISH);
  143.             findCommittee(committeeSummaryMap, orgKeyUpper)
  144.                 .ifPresent(committee -> addCommiteeSummary(builder, proposals, committee));
  145.         });

  146.         area.setValue(builder.toString());
  147.         return area;
  148.     }

  149.     /**
  150.      * Creates the committeee decision summary.
  151.      *
  152.      * @param viewRiksdagenCommittee the view riksdagen committee
  153.      * @param reportMonth the report month
  154.      * @return the text area
  155.      */
  156.     @Override
  157.     public TextArea createCommitteeeDecisionSummary(
  158.             final ViewRiksdagenCommittee viewRiksdagenCommittee,
  159.             final String reportMonth) {

  160.         final TextArea area = new TextArea("Summary");
  161.         final StringBuilder builder = new StringBuilder();
  162.         final String targetOrg = viewRiksdagenCommittee.getEmbeddedId().getOrgCode().toUpperCase(Locale.ENGLISH);

  163.         final List<ProposalCommitteeeSummary> proposals =
  164.             getOrgProposalMap(reportMonth).get(targetOrg);

  165.         addCommiteeSummary(builder, proposals, viewRiksdagenCommittee);
  166.         area.setValue(builder.toString());
  167.         return area;
  168.     }

  169.     /**
  170.      * Adds the chart data.
  171.      *
  172.      * @param sankeyChart the sankey chart
  173.      * @param proposals the proposals
  174.      * @param committee the committee
  175.      */
  176.     private void addChartData(
  177.             final SankeyChart sankeyChart,
  178.             final List<ProposalCommitteeeSummary> proposals,
  179.             final ViewRiksdagenCommittee committee) {

  180.         addDocTypeDataRows(sankeyChart, proposals, committee);
  181.         addDecisionDataRows(sankeyChart, proposals, committee);
  182.     }

  183.     /**
  184.      * Adds the doc type data rows.
  185.      *
  186.      * @param sankeyChart the sankey chart
  187.      * @param proposals the proposals
  188.      * @param committee the committee
  189.      */
  190.     private static void addDocTypeDataRows(
  191.             final SankeyChart sankeyChart,
  192.             final List<ProposalCommitteeeSummary> proposals,
  193.             final ViewRiksdagenCommittee committee) {

  194.         final String committeeName = committee.getEmbeddedId().getDetail();

  195.         Optional.ofNullable(proposals)
  196.             .stream()
  197.             .flatMap(Collection::stream)
  198.             .filter(Objects::nonNull)
  199.             .collect(Collectors.groupingBy(
  200.                 ProposalCommitteeeSummary::docType,
  201.                 Collectors.collectingAndThen(
  202.                     Collectors.toList(),
  203.                     docProposals -> {
  204.                         if (!docProposals.get(0).docType().isEmpty()) {
  205.                             sankeyChart.addDataRow(
  206.                                 docProposals.get(0).docType(),
  207.                                 committeeName,
  208.                                 docProposals.size()
  209.                             );
  210.                         }
  211.                         return docProposals;
  212.                     }
  213.                 )
  214.             ));
  215.     }


  216.     /**
  217.      * Adds the decision data rows.
  218.      *
  219.      * @param sankeyChart the sankey chart
  220.      * @param proposals the proposals
  221.      * @param committee the committee
  222.      */
  223.     private static void addDecisionDataRows(
  224.             final SankeyChart sankeyChart,
  225.             final List<ProposalCommitteeeSummary> proposals,
  226.             final ViewRiksdagenCommittee committee) {

  227.         final String committeeName = committee.getEmbeddedId().getDetail();

  228.         proposals.stream()
  229.             .collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
  230.             .forEach((decision, decisionProposals) -> {
  231.                 if (!decision.isEmpty()) {
  232.                     sankeyChart.addDataRow(committeeName, decision, decisionProposals.size());
  233.                 }
  234.             });
  235.     }

  236.     /**
  237.      * Adds the doc type decision data rows.
  238.      *
  239.      * @param sankeyChart the sankey chart
  240.      * @param proposals the proposals
  241.      */
  242.     private static void addDocTypeDecisionDataRows(
  243.             final SankeyChart sankeyChart,
  244.             final List<ProposalCommitteeeSummary> proposals) {

  245.         Optional.ofNullable(proposals)
  246.             .stream()
  247.             .flatMap(Collection::stream)
  248.             .filter(Objects::nonNull)
  249.             .collect(Collectors.groupingBy(
  250.                 ProposalCommitteeeSummary::docType,
  251.                 Collectors.collectingAndThen(
  252.                     Collectors.toList(),
  253.                     docTypeProposals -> {
  254.                         if (!docTypeProposals.get(0).docType().isEmpty()) {
  255.                             addDecisionRowsForDocType(
  256.                                 sankeyChart,
  257.                                 docTypeProposals.get(0).docType(),
  258.                                 docTypeProposals
  259.                             );
  260.                         }
  261.                         return docTypeProposals;
  262.                     }
  263.                 )
  264.             ));
  265.     }

  266.     /**
  267.      * Adds the decision rows for doc type.
  268.      *
  269.      * @param sankeyChart the sankey chart
  270.      * @param docType the doc type
  271.      * @param proposals the proposals
  272.      */
  273.     private static void addDecisionRowsForDocType(
  274.             final SankeyChart sankeyChart,
  275.             final String docType,
  276.             final List<ProposalCommitteeeSummary> proposals) {

  277.         proposals.stream()
  278.             .collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
  279.             .forEach((decision, decisionProposals) -> {
  280.                 if (!decision.isEmpty()) {
  281.                     sankeyChart.addDataRow(docType, decision, decisionProposals.size());
  282.                 }
  283.             });
  284.     }

  285.     /**
  286.      * Adds the commitee summary.
  287.      *
  288.      * @param builder the builder
  289.      * @param proposals the proposals
  290.      * @param committee the committee
  291.      */
  292.     private static void addCommiteeSummary(
  293.             final StringBuilder builder,
  294.             final List<ProposalCommitteeeSummary> proposals,
  295.             final ViewRiksdagenCommittee committee) {

  296.         if (committee == null || proposals == null || proposals.isEmpty()) {
  297.             return;
  298.         }

  299.         builder.append('\n').append(committee.getEmbeddedId().getDetail());

  300.         proposals.stream()
  301.             .filter(Objects::nonNull)
  302.             .collect(Collectors.groupingBy(
  303.                 ProposalCommitteeeSummary::docType,
  304.                 Collectors.collectingAndThen(
  305.                     Collectors.toList(),
  306.                     docProposals -> {
  307.                         addSummaryEntry(builder, docProposals.get(0).docType(), docProposals);
  308.                         return docProposals;
  309.                     }
  310.                 )
  311.             ));
  312.     }

  313.     /**
  314.      * Adds the summary entry.
  315.      *
  316.      * @param builder the builder
  317.      * @param docType the doc type
  318.      * @param docTypeList the doc type list
  319.      */
  320.     private static void addSummaryEntry(
  321.             final StringBuilder builder,
  322.             final String docType,
  323.             final List<ProposalCommitteeeSummary> docTypeList) {

  324.         builder.append("\n ( ")
  325.                .append(docTypeList.size())
  326.                .append(' ')
  327.                .append(docType)
  328.                .append(" -> ");

  329.         docTypeList.stream()
  330.             .collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
  331.             .forEach((decision, decisionProposals) ->
  332.                 addDecisionDetails(builder, decision, decisionProposals));

  333.         builder.append(')');
  334.     }

  335.     /**
  336.      * Adds the decision details.
  337.      *
  338.      * @param builder the builder
  339.      * @param decision the decision
  340.      * @param summaries the summaries
  341.      */
  342.     private static void addDecisionDetails(
  343.             final StringBuilder builder,
  344.             final String decision,
  345.             final List<ProposalCommitteeeSummary> summaries) {

  346.         if (!decision.isEmpty()) {
  347.             builder.append("\n   ")
  348.                    .append(summaries.size())
  349.                    .append(' ')
  350.                    .append(decision)
  351.                    .append(' ');

  352.             summaries.stream()
  353.                 .filter(Objects::nonNull)
  354.                 .forEach(summary ->
  355.                     builder.append("\n    ")
  356.                           .append(summary.decision())
  357.                           .append(':')
  358.                           .append(summary.wording())
  359.                           .append(' ')
  360.                           .append(summary.wording2())
  361.                           .append(' '));
  362.         }
  363.     }

  364. }