CommitteeDecisionFlowPageModContentFactoryImpl.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.user.committee.pagemode;

import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Component;

import com.hack23.cia.model.internal.application.data.committee.impl.ViewRiksdagenCommittee;
import com.hack23.cia.model.internal.application.system.impl.ApplicationEventGroup;
import com.hack23.cia.web.impl.ui.application.action.ViewAction;
import com.hack23.cia.web.impl.ui.application.views.common.chartfactory.api.DecisionFlowChartManager;
import com.hack23.cia.web.impl.ui.application.views.common.menufactory.api.pagecommands.PageCommandCommitteeConstants;
import com.hack23.cia.web.impl.ui.application.views.common.pagemode.CardInfoRowUtil;
import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentRatio;
import com.hack23.cia.web.impl.ui.application.views.pageclicklistener.DecisionFlowValueChangeListener;
import com.hack23.cia.web.widgets.charts.SankeyChart;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Layout;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.VerticalLayout;

/**
 * The Class ParliamentDecisionFlowPageModContentFactoryImpl.
 */
@Component
public final class CommitteeDecisionFlowPageModContentFactoryImpl extends AbstractCommitteePageModContentFactoryImpl {

    /** The Constant DEFAULT_YEAR. */
    private static final String DEFAULT_YEAR = "2023/24";

    /** The decision flow chart manager. */
    @Autowired
    private DecisionFlowChartManager decisionFlowChartManager;

    /**
     * Creates the content.
     *
     * @param parameters the parameters
     * @param menuBar the menu bar
     * @param panel the panel
     * @return the layout
     */
    @Secured({ "ROLE_ANONYMOUS", "ROLE_USER", "ROLE_ADMIN" })
    @Override
    public Layout createContent(final String parameters, final MenuBar menuBar, final Panel panel) {
        final VerticalLayout panelContent = createPanelContent();
        final String pageId = getPageId(parameters);
        final ViewRiksdagenCommittee committee = getItem(parameters);

        setupMenuAndHeader(menuBar, panel, panelContent, pageId, committee);
        final String selectedYear = extractSelectedYear(parameters);
        final Map<String, List<ViewRiksdagenCommittee>> committeeMap = loadCommitteeMap();

        addYearSelector(panelContent, selectedYear, pageId);
        addDecisionFlowChart(panelContent, committee, committeeMap, selectedYear);
        addDecisionSummary(panelContent, committee, selectedYear);

        recordPageVisit(parameters, pageId);

        return panelContent;
    }

    /**
     * Creates list of available years for selection.
     * Goes from 2010/11 up to (currentYear+1)/(currentYear+2).
     *
     * @return Unmodifiable list of year strings in format "YYYY/YY"
     */
    private static List<String> createAvailableYears() {
        final int currentYear = LocalDate.now((ZoneOffset.UTC)).getYear();
        return IntStream.rangeClosed(2010, currentYear + 1)
            .mapToObj(year -> String.format(Locale.ENGLISH,"%d/%02d", year, (year + 1) % 100))
            .sorted(Comparator.reverseOrder()) // Most recent years first
            .collect(Collectors.collectingAndThen(
                Collectors.toList(),
                Collections::unmodifiableList
            ));
    }

    /**
     * Setup menu and header.
     *
     * @param menuBar the menu bar
     * @param panel the panel
     * @param panelContent the panel content
     * @param pageId the page id
     * @param committee the committee
     */
    private void setupMenuAndHeader(final MenuBar menuBar, final Panel panel, final VerticalLayout panelContent,
            final String pageId, final ViewRiksdagenCommittee committee) {
        getCommitteeMenuItemFactory().createCommitteeeMenuBar(menuBar, pageId);
        CardInfoRowUtil.createPageHeader(panel, panelContent,
            CommitteeViewConstants.DF_TITLE_HEADER + committee.getEmbeddedId().getDetail(),
            CommitteeViewConstants.DF_TITLE,
            CommitteeViewConstants.DF_DESCRIPTION
        );
    }

    /**
     * Extract selected year.
     *
     * @param parameters the parameters
     * @return the string
     */
    private String extractSelectedYear(final String parameters) {
        if (parameters != null && parameters.contains("[") && parameters.contains("]")) {
            return parameters.substring(
                parameters.indexOf('[') + 1,
                parameters.lastIndexOf(']')
            );
        }
        return DEFAULT_YEAR;
    }

    /**
     * Load committee map.
     *
     * @return the map
     */
    private Map<String, List<ViewRiksdagenCommittee>> loadCommitteeMap() {
        return getApplicationManager()
            .getDataContainer(ViewRiksdagenCommittee.class)
            .getAll()
            .stream()
            .collect(Collectors.groupingBy(
                committee -> committee.getEmbeddedId().getOrgCode().toUpperCase(Locale.ENGLISH)
            ));
    }

    /**
     * Adds the year selector.
     *
     * @param panelContent the panel content
     * @param selectedYear the selected year
     * @param pageId the page id
     */
    private void addYearSelector(final VerticalLayout panelContent, final String selectedYear, final String pageId) {
        final ComboBox<String> yearSelector = new ComboBox<>(CommitteeViewConstants.DF_YEAR_SELECTOR, createAvailableYears());
        yearSelector.setWidth("200px");
        yearSelector.setEmptySelectionAllowed(false);
        yearSelector.setSelectedItem(selectedYear);
        yearSelector.addValueChangeListener(new DecisionFlowValueChangeListener(NAME, pageId));

        panelContent.addComponent(yearSelector);
        panelContent.setExpandRatio(yearSelector, ContentRatio.SMALL2);
    }

    /**
     * Adds the decision flow chart.
     *
     * @param panelContent the panel content
     * @param committee the committee
     * @param committeeMap the committee map
     * @param selectedYear the selected year
     */
    private void addDecisionFlowChart(final VerticalLayout panelContent, final ViewRiksdagenCommittee committee,
            final Map<String, List<ViewRiksdagenCommittee>> committeeMap, final String selectedYear) {
        final SankeyChart chart = decisionFlowChartManager.createCommitteeDecisionFlow(
            committee, committeeMap, selectedYear
        );
        chart.setWidth("100%");

        panelContent.addComponent(chart);
        panelContent.setExpandRatio(chart, ContentRatio.LARGE);
    }

    /**
     * Adds the decision summary.
     *
     * @param panelContent the panel content
     * @param committee the committee
     * @param selectedYear the selected year
     */
    private void addDecisionSummary(final VerticalLayout panelContent,
            final ViewRiksdagenCommittee committee, final String selectedYear) {
        final TextArea summaryArea = decisionFlowChartManager.createCommitteeeDecisionSummary(
            committee, selectedYear
        );
        summaryArea.setSizeFull();
        summaryArea.setReadOnly(true);

        panelContent.addComponent(summaryArea);
        panelContent.setExpandRatio(summaryArea, ContentRatio.SMALL_GRID);
    }

    /**
     * Record page visit.
     *
     * @param parameters the parameters
     * @param pageId the page id
     */
    private void recordPageVisit(final String parameters, final String pageId) {
        getPageActionEventHelper().createPageEvent(
            ViewAction.VISIT_COMMITTEE_VIEW,
            ApplicationEventGroup.USER,
            NAME,
            parameters,
            pageId
        );
    }

    /**
     * Matches.
     *
     * @param page the page
     * @param parameters the parameters
     * @return true, if successful
     */
    @Override
    public boolean matches(final String page, final String parameters) {
    	return PageCommandCommitteeConstants.COMMAND_CHARTS_DECISION_FLOW.matches(page, parameters);
    }
}