DecisionFlowChartManagerImpl.java
/*
* Copyright 2010-2025 James Pether Sörling
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id$
* $HeadURL$
*/
package com.hack23.cia.web.impl.ui.application.views.common.chartfactory.impl;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hack23.cia.model.internal.application.data.committee.impl.ViewRiksdagenCommittee;
import com.hack23.cia.web.impl.ui.application.views.common.chartfactory.api.DecisionFlowChartManager;
import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.DecisionDataFactory;
import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.ProposalCommitteeeSummary;
import com.hack23.cia.web.widgets.charts.SankeyChart;
import com.vaadin.ui.TextArea;
/**
* The Class DecisionFlowChartManagerImpl.
*/
@Service
public final class DecisionFlowChartManagerImpl implements DecisionFlowChartManager {
/** The decision data factory. */
@Autowired
private DecisionDataFactory decisionDataFactory;
/**
* Instantiates a new decision flow chart manager impl.
*/
public DecisionFlowChartManagerImpl() {
super();
}
/**
* Gets the org proposal map.
*
* @param reportMonth the report month
* @return the org proposal map
*/
// Core helper methods for common operations
private Map<String, List<ProposalCommitteeeSummary>> getOrgProposalMap(final String reportMonth) {
return decisionDataFactory.createCommitteeSummary(reportMonth)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(
ProposalCommitteeeSummary::org,
Collectors.mapping(
Function.identity(),
Collectors.toList()
)
));
}
/**
* Find committee.
*
* @param committeeSummaryMap the committee summary map
* @param orgKey the org key
* @return the optional
*/
private Optional<ViewRiksdagenCommittee> findCommittee(
final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
final String orgKey) {
return Optional.ofNullable(committeeSummaryMap.get(orgKey))
.flatMap(list -> list.stream().findFirst());
}
/**
* Creates the all decision flow.
*
* @param committeeSummaryMap the committee summary map
* @param reportMonth the report month
* @return the sankey chart
*/
@Override
public SankeyChart createAllDecisionFlow(
final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
final String reportMonth) {
Objects.requireNonNull(committeeSummaryMap, "Committee summary map cannot be null");
Objects.requireNonNull(reportMonth, "Report month cannot be null");
final SankeyChart sankeyChart = new SankeyChart();
getOrgProposalMap(reportMonth).entrySet().stream()
.filter(entry -> entry.getKey() != null && !entry.getValue().isEmpty())
.forEach(entry ->
findCommittee(committeeSummaryMap, entry.getKey())
.ifPresent(committee ->
addChartData(sankeyChart, entry.getValue(), committee)));
sankeyChart.drawChart();
return sankeyChart;
}
/**
* Creates the committee decision flow.
*
* @param viewRiksdagenCommittee the view riksdagen committee
* @param committeeSummaryMap the committee summary map
* @param reportMonth the report month
* @return the sankey chart
*/
@Override
public SankeyChart createCommitteeDecisionFlow(
final ViewRiksdagenCommittee viewRiksdagenCommittee,
final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
final String reportMonth) {
Objects.requireNonNull(viewRiksdagenCommittee, "Committee cannot be null");
final SankeyChart sankeyChart = new SankeyChart();
final String targetOrg = viewRiksdagenCommittee.getEmbeddedId().getOrgCode().toUpperCase(Locale.ENGLISH);
getOrgProposalMap(reportMonth).entrySet().stream()
.filter(entry -> targetOrg.equals(entry.getKey().toUpperCase(Locale.ENGLISH)))
.forEach(entry -> addDocTypeDecisionDataRows(sankeyChart, entry.getValue()));
sankeyChart.drawChart();
return sankeyChart;
}
/**
* Creates the committeee decision summary.
*
* @param committeeSummaryMap the committee summary map
* @param reportMonth the report month
* @return the text area
*/
@Override
public TextArea createCommitteeeDecisionSummary(
final Map<String, List<ViewRiksdagenCommittee>> committeeSummaryMap,
final String reportMonth) {
final TextArea area = new TextArea("Summary");
final StringBuilder builder = new StringBuilder();
final Map<String, List<ProposalCommitteeeSummary>> orgProposalMap = getOrgProposalMap(reportMonth);
orgProposalMap.forEach((orgKey, proposals) -> {
final String orgKeyUpper = orgKey.toUpperCase(Locale.ENGLISH);
findCommittee(committeeSummaryMap, orgKeyUpper)
.ifPresent(committee -> addCommiteeSummary(builder, proposals, committee));
});
area.setValue(builder.toString());
return area;
}
/**
* Creates the committeee decision summary.
*
* @param viewRiksdagenCommittee the view riksdagen committee
* @param reportMonth the report month
* @return the text area
*/
@Override
public TextArea createCommitteeeDecisionSummary(
final ViewRiksdagenCommittee viewRiksdagenCommittee,
final String reportMonth) {
final TextArea area = new TextArea("Summary");
final StringBuilder builder = new StringBuilder();
final String targetOrg = viewRiksdagenCommittee.getEmbeddedId().getOrgCode().toUpperCase(Locale.ENGLISH);
final List<ProposalCommitteeeSummary> proposals =
getOrgProposalMap(reportMonth).get(targetOrg);
addCommiteeSummary(builder, proposals, viewRiksdagenCommittee);
area.setValue(builder.toString());
return area;
}
/**
* Adds the chart data.
*
* @param sankeyChart the sankey chart
* @param proposals the proposals
* @param committee the committee
*/
private void addChartData(
final SankeyChart sankeyChart,
final List<ProposalCommitteeeSummary> proposals,
final ViewRiksdagenCommittee committee) {
addDocTypeDataRows(sankeyChart, proposals, committee);
addDecisionDataRows(sankeyChart, proposals, committee);
}
/**
* Adds the doc type data rows.
*
* @param sankeyChart the sankey chart
* @param proposals the proposals
* @param committee the committee
*/
private static void addDocTypeDataRows(
final SankeyChart sankeyChart,
final List<ProposalCommitteeeSummary> proposals,
final ViewRiksdagenCommittee committee) {
final String committeeName = committee.getEmbeddedId().getDetail();
Optional.ofNullable(proposals)
.stream()
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(
ProposalCommitteeeSummary::docType,
Collectors.collectingAndThen(
Collectors.toList(),
docProposals -> {
if (!docProposals.get(0).docType().isEmpty()) {
sankeyChart.addDataRow(
docProposals.get(0).docType(),
committeeName,
docProposals.size()
);
}
return docProposals;
}
)
));
}
/**
* Adds the decision data rows.
*
* @param sankeyChart the sankey chart
* @param proposals the proposals
* @param committee the committee
*/
private static void addDecisionDataRows(
final SankeyChart sankeyChart,
final List<ProposalCommitteeeSummary> proposals,
final ViewRiksdagenCommittee committee) {
final String committeeName = committee.getEmbeddedId().getDetail();
proposals.stream()
.collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
.forEach((decision, decisionProposals) -> {
if (!decision.isEmpty()) {
sankeyChart.addDataRow(committeeName, decision, decisionProposals.size());
}
});
}
/**
* Adds the doc type decision data rows.
*
* @param sankeyChart the sankey chart
* @param proposals the proposals
*/
private static void addDocTypeDecisionDataRows(
final SankeyChart sankeyChart,
final List<ProposalCommitteeeSummary> proposals) {
Optional.ofNullable(proposals)
.stream()
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(
ProposalCommitteeeSummary::docType,
Collectors.collectingAndThen(
Collectors.toList(),
docTypeProposals -> {
if (!docTypeProposals.get(0).docType().isEmpty()) {
addDecisionRowsForDocType(
sankeyChart,
docTypeProposals.get(0).docType(),
docTypeProposals
);
}
return docTypeProposals;
}
)
));
}
/**
* Adds the decision rows for doc type.
*
* @param sankeyChart the sankey chart
* @param docType the doc type
* @param proposals the proposals
*/
private static void addDecisionRowsForDocType(
final SankeyChart sankeyChart,
final String docType,
final List<ProposalCommitteeeSummary> proposals) {
proposals.stream()
.collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
.forEach((decision, decisionProposals) -> {
if (!decision.isEmpty()) {
sankeyChart.addDataRow(docType, decision, decisionProposals.size());
}
});
}
/**
* Adds the commitee summary.
*
* @param builder the builder
* @param proposals the proposals
* @param committee the committee
*/
private static void addCommiteeSummary(
final StringBuilder builder,
final List<ProposalCommitteeeSummary> proposals,
final ViewRiksdagenCommittee committee) {
if (committee == null || proposals == null || proposals.isEmpty()) {
return;
}
builder.append('\n').append(committee.getEmbeddedId().getDetail());
proposals.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(
ProposalCommitteeeSummary::docType,
Collectors.collectingAndThen(
Collectors.toList(),
docProposals -> {
addSummaryEntry(builder, docProposals.get(0).docType(), docProposals);
return docProposals;
}
)
));
}
/**
* Adds the summary entry.
*
* @param builder the builder
* @param docType the doc type
* @param docTypeList the doc type list
*/
private static void addSummaryEntry(
final StringBuilder builder,
final String docType,
final List<ProposalCommitteeeSummary> docTypeList) {
builder.append("\n ( ")
.append(docTypeList.size())
.append(' ')
.append(docType)
.append(" -> ");
docTypeList.stream()
.collect(Collectors.groupingBy(ProposalCommitteeeSummary::decision))
.forEach((decision, decisionProposals) ->
addDecisionDetails(builder, decision, decisionProposals));
builder.append(')');
}
/**
* Adds the decision details.
*
* @param builder the builder
* @param decision the decision
* @param summaries the summaries
*/
private static void addDecisionDetails(
final StringBuilder builder,
final String decision,
final List<ProposalCommitteeeSummary> summaries) {
if (!decision.isEmpty()) {
builder.append("\n ")
.append(summaries.size())
.append(' ')
.append(decision)
.append(' ');
summaries.stream()
.filter(Objects::nonNull)
.forEach(summary ->
builder.append("\n ")
.append(summary.decision())
.append(':')
.append(summary.wording())
.append(' ')
.append(summary.wording2())
.append(' '));
}
}
}