PartyOverviewPageModContentFactoryImpl.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.user.party.pagemode;

  20. import java.util.Locale;

  21. import org.springframework.security.access.annotation.Secured;
  22. import org.springframework.stereotype.Component;

  23. import com.hack23.cia.model.internal.application.data.party.impl.ViewRiksdagenParty;
  24. import com.hack23.cia.model.internal.application.data.party.impl.ViewRiksdagenPartySummary;
  25. import com.hack23.cia.model.internal.application.system.impl.ApplicationEventGroup;
  26. import com.hack23.cia.service.api.DataContainer;
  27. import com.hack23.cia.web.impl.ui.application.action.ViewAction;
  28. import com.hack23.cia.web.impl.ui.application.views.common.menufactory.api.pagecommands.PageCommandPartyConstants;
  29. import com.hack23.cia.web.impl.ui.application.views.common.pagemode.CardInfoRowUtil;
  30. import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentRatio;
  31. import com.vaadin.icons.VaadinIcons;
  32. import com.vaadin.server.Responsive;
  33. import com.vaadin.ui.HorizontalLayout;
  34. import com.vaadin.ui.Layout;
  35. import com.vaadin.ui.Link;
  36. import com.vaadin.ui.MenuBar;
  37. import com.vaadin.ui.Panel;
  38. import com.vaadin.ui.VerticalLayout;

  39. /**
  40.  * The Class PartyOverviewPageModContentFactoryImpl.
  41.  */
  42. @Component
  43. public final class PartyOverviewPageModContentFactoryImpl extends AbstractPartyPageModContentFactoryImpl {

  44.     /**
  45.      * Instantiates a new party overview page mod content factory impl.
  46.      */
  47.     public PartyOverviewPageModContentFactoryImpl() {
  48.         super();
  49.     }

  50.     /**
  51.      * Creates the content.
  52.      *
  53.      * @param parameters the parameters
  54.      * @param menuBar the menu bar
  55.      * @param panel the panel
  56.      * @return the layout
  57.      */
  58.     @Secured({ "ROLE_ANONYMOUS", "ROLE_USER", "ROLE_ADMIN" })
  59.     @Override
  60.     public Layout createContent(final String parameters, final MenuBar menuBar, final Panel panel) {
  61.         final VerticalLayout panelContent = createPanelContent();
  62.         final ViewRiksdagenParty viewRiksdagenParty = getItem(parameters);
  63.         final String pageId = getPageId(parameters);

  64.         getPartyMenuItemFactory().createPartyMenuBar(menuBar, pageId);

  65.         CardInfoRowUtil.createPageHeader(panel, panelContent,
  66.             PartyViewConstants.OVERVIEW_HEADER + " " + viewRiksdagenParty.getPartyName(),
  67.             PartyViewConstants.GENERAL_SUBTITLE,
  68.             PartyViewConstants.OVERVIEW_DESC);

  69.         panel.setContent(panelContent);

  70.         final Link addPartyPageLink = getPageLinkFactory().addPartyPageLink(viewRiksdagenParty);
  71.         panelContent.addComponent(addPartyPageLink);
  72.         panelContent.setExpandRatio(addPartyPageLink, ContentRatio.SMALL);

  73.         // Load summary if available
  74.         final DataContainer<ViewRiksdagenPartySummary, String> partySummaryDataContainer = getApplicationManager()
  75.                 .getDataContainer(ViewRiksdagenPartySummary.class);
  76.         final ViewRiksdagenPartySummary viewRiksdagenPartySummary = partySummaryDataContainer.load(pageId);

  77.         // Create a card panel similar to the politician overview
  78.         final Panel cardPanel = new Panel();
  79.         cardPanel.addStyleName("politician-overview-card");
  80.         cardPanel.setWidth("100%");
  81.         cardPanel.setHeightUndefined();
  82.         Responsive.makeResponsive(cardPanel);

  83.         final VerticalLayout cardContent = new VerticalLayout();
  84.         cardContent.setMargin(true);
  85.         cardContent.setSpacing(true);
  86.         cardContent.setWidth("100%");
  87.         cardPanel.setContent(cardContent);

  88.         panelContent.addComponent(cardPanel);
  89.         panelContent.setExpandRatio(cardPanel, ContentRatio.SMALL_GRID);

  90.         CardInfoRowUtil.createCardHeader(cardContent,viewRiksdagenParty.getPartyName());

  91.         // Create single row for four sections
  92.         final HorizontalLayout sectionsLayout = new HorizontalLayout();
  93.         sectionsLayout.setSpacing(true);
  94.         sectionsLayout.setWidth("100%");
  95.         cardContent.addComponent(sectionsLayout);

  96.         // 1. Political Influence & Position
  97.         final VerticalLayout politicalInfluenceLayout = CardInfoRowUtil.createSectionLayout("Political Influence & Position");
  98.         addPoliticalInfluenceMetrics(politicalInfluenceLayout, viewRiksdagenParty, viewRiksdagenPartySummary);
  99.         sectionsLayout.addComponent(politicalInfluenceLayout);
  100.         sectionsLayout.setExpandRatio(politicalInfluenceLayout, 1.0f);

  101.         // 2. Parliamentary Engagement
  102.         final VerticalLayout parliamentaryEngagementLayout = CardInfoRowUtil.createSectionLayout("Parliamentary Engagement");
  103.         addParliamentaryEngagementMetrics(parliamentaryEngagementLayout, viewRiksdagenParty, viewRiksdagenPartySummary);
  104.         sectionsLayout.addComponent(parliamentaryEngagementLayout);
  105.         sectionsLayout.setExpandRatio(parliamentaryEngagementLayout, 1.0f);

  106.         // 3. Legislative Impact
  107.         final VerticalLayout legislativeImpactLayout = CardInfoRowUtil.createSectionLayout("Legislative Impact");
  108.         addLegislativeImpactMetrics(legislativeImpactLayout, viewRiksdagenParty, viewRiksdagenPartySummary);
  109.         sectionsLayout.addComponent(legislativeImpactLayout);
  110.         sectionsLayout.setExpandRatio(legislativeImpactLayout, 1.0f);

  111.         // 4. Member Performance
  112.         final VerticalLayout memberPerformanceLayout = CardInfoRowUtil.createSectionLayout("Member Performance");
  113.         addMemberPerformanceMetrics(memberPerformanceLayout, viewRiksdagenParty, viewRiksdagenPartySummary);
  114.         sectionsLayout.addComponent(memberPerformanceLayout);
  115.         sectionsLayout.setExpandRatio(memberPerformanceLayout, 1.0f);

  116.         // After the card, add the overview layout
  117.         final VerticalLayout overviewLayout = new VerticalLayout();
  118.         overviewLayout.setSizeFull();
  119.         panelContent.addComponent(overviewLayout);
  120.         panelContent.setExpandRatio(overviewLayout, ContentRatio.LARGE_FORM);

  121.         getPartyMenuItemFactory().createOverviewPage(overviewLayout, pageId);

  122.         getPageActionEventHelper().createPageEvent(ViewAction.VISIT_PARTY_VIEW, ApplicationEventGroup.USER, NAME, parameters,
  123.                 pageId);
  124.         return panelContent;
  125.     }


  126.     /**
  127.      * Adds the political influence metrics.
  128.      *
  129.      * @param layout the layout
  130.      * @param party the party
  131.      * @param summary the summary
  132.      */
  133.     // 1. Political Influence & Position
  134.     private void addPoliticalInfluenceMetrics(final VerticalLayout layout,
  135.             final ViewRiksdagenParty party,
  136.             final ViewRiksdagenPartySummary summary) {

  137.         if (summary != null) {
  138.             // Government Influence
  139.             layout.addComponent(CardInfoRowUtil.createInfoRow("Government Position:",
  140.                 summary.isActiveGovernment() ? "In Government" : "Opposition",
  141.                 VaadinIcons.INSTITUTION,
  142.                 "Current position in government"));

  143.             layout.addComponent(CardInfoRowUtil.createInfoRow("Ministers:",
  144.                 String.valueOf(summary.getCurrentMinistryAssignments()),
  145.                 VaadinIcons.GROUP,
  146.                 "Current ministerial positions"));

  147.             // Parliamentary Strength
  148.             layout.addComponent(CardInfoRowUtil.createInfoRow("Parliament Members:",
  149.                 String.valueOf(party.getHeadCount()),
  150.                 VaadinIcons.USERS,
  151.                 "Total number of parliament members"));

  152.             layout.addComponent(CardInfoRowUtil.createInfoRow("Committee Positions:",
  153.                 String.valueOf(summary.getCurrentCommitteeAssignments()),
  154.                 VaadinIcons.CLIPBOARD_USER,
  155.                 "Current committee assignments"));

  156.             // Leadership Roles
  157.             layout.addComponent(CardInfoRowUtil.createInfoRow("Leadership Positions:",
  158.                 String.valueOf(summary.getCurrentCommitteeLeadershipAssignments()),
  159.                 VaadinIcons.STAR,
  160.                 "Current committee leadership roles"));
  161.         }
  162.     }

  163.     /**
  164.      * Adds the parliamentary engagement metrics.
  165.      *
  166.      * @param layout the layout
  167.      * @param party the party
  168.      * @param summary the summary
  169.      */
  170.     // 2. Parliamentary Engagement
  171.     private void addParliamentaryEngagementMetrics(final VerticalLayout layout,
  172.             final ViewRiksdagenParty party,
  173.             final ViewRiksdagenPartySummary summary) {

  174.         if (summary != null) {
  175.             // Active Participation
  176.             layout.addComponent(CardInfoRowUtil.createInfoRow("Parliament Activity:",
  177.                 String.format(Locale.ENGLISH,"%.1f%%", calculateActivityRate(summary.getTotalActiveParliament(), party.getHeadCount())),
  178.                 VaadinIcons.CHART_LINE,
  179.                 "Percentage of active members in parliament"));

  180.             // Committee Engagement
  181.             layout.addComponent(CardInfoRowUtil.createInfoRow("Committee Involvement:",
  182.                 String.valueOf(summary.getTotalActiveCommittee()),
  183.                 VaadinIcons.USERS,
  184.                 "Members active in committees"));

  185.             // Historical Presence
  186.             layout.addComponent(CardInfoRowUtil.createInfoRow("Days in Government:",
  187.                 String.format(Locale.ENGLISH,"%,d", summary.getTotalDaysServedGovernment()),
  188.                 VaadinIcons.CLOCK,
  189.                 "Total days served in government"));

  190.             layout.addComponent(CardInfoRowUtil.createInfoRow("Parliamentary Experience:",
  191.                 String.format(Locale.ENGLISH,"%,d", summary.getTotalDaysServedParliament()),
  192.                 VaadinIcons.CALENDAR_CLOCK,
  193.                 "Total days served in parliament"));
  194.         }
  195.     }

  196.     /**
  197.      * Adds the legislative impact metrics.
  198.      *
  199.      * @param layout the layout
  200.      * @param party the party
  201.      * @param summary the summary
  202.      */
  203.     // 3. Legislative Impact
  204.     private void addLegislativeImpactMetrics(final VerticalLayout layout,
  205.             final ViewRiksdagenParty party,
  206.             final ViewRiksdagenPartySummary summary) {

  207.         if (summary != null) {
  208.             // Legislative Production
  209.             layout.addComponent(CardInfoRowUtil.createInfoRow("Total Motions:",
  210.                 String.valueOf(summary.getTotalPartyMotions()),
  211.                 VaadinIcons.FILE_TEXT,
  212.                 "Total party-initiated motions"));

  213.             layout.addComponent(CardInfoRowUtil.createInfoRow("Recent Activity:",
  214.                 String.valueOf(summary.getTotalDocumentsLastYear()),
  215.                 VaadinIcons.CHART_TIMELINE,
  216.                 "Documents produced in the last year"));

  217.             // Cross-party Cooperation
  218.             layout.addComponent(CardInfoRowUtil.createInfoRow("Collaboration Rate:",
  219.                 String.format(Locale.ENGLISH,"%.1f%%", summary.getAvgCollaborationPercentage()),
  220.                 VaadinIcons.CONNECT,
  221.                 "Cross-party collaboration percentage"));

  222.             layout.addComponent(CardInfoRowUtil.createInfoRow("Joint Initiatives:",
  223.                 String.valueOf(summary.getTotalCollaborativeMotions()),
  224.                 VaadinIcons.USERS,
  225.                 "Multi-party collaborative motions"));

  226.             // Legislative Efficiency
  227.             layout.addComponent(CardInfoRowUtil.createInfoRow("Productivity:",
  228.                 String.format(Locale.ENGLISH,"%.1f", summary.getAvgDocumentsPerMember()),
  229.                 VaadinIcons.CHART_GRID,
  230.                 "Average documents per member"));
  231.         }
  232.     }

  233.     /**
  234.      * Adds the member performance metrics.
  235.      *
  236.      * @param layout the layout
  237.      * @param party the party
  238.      * @param summary the summary
  239.      */
  240.     // 4. Member Performance
  241.     private void addMemberPerformanceMetrics(final VerticalLayout layout,
  242.             final ViewRiksdagenParty party,
  243.             final ViewRiksdagenPartySummary summary) {

  244.         if (summary != null) {
  245.             // Activity Distribution
  246.             layout.addComponent(CardInfoRowUtil.createInfoRow("High Performers:",
  247.                 String.format(Locale.ENGLISH,"%d (%d%%)",
  248.                     summary.getVeryHighActivityMembers(),
  249.                     calculatePercentage(summary.getVeryHighActivityMembers(), party.getHeadCount())),
  250.                 VaadinIcons.STAR,
  251.                 "Members with very high activity levels"));

  252.             // Member Focus Areas
  253.             layout.addComponent(CardInfoRowUtil.createInfoRow("Party Policy Focus:",
  254.                 String.format(Locale.ENGLISH,"%d (%d%%)",
  255.                     summary.getPartyFocusedMembers(),
  256.                     calculatePercentage(summary.getPartyFocusedMembers(), party.getHeadCount())),
  257.                 VaadinIcons.FLAG,
  258.                 "Members focused on party policy work"));

  259.             layout.addComponent(CardInfoRowUtil.createInfoRow("Committee Focus:",
  260.                 String.format(Locale.ENGLISH,"%d (%d%%)",
  261.                     summary.getCommitteeFocusedMembers(),
  262.                     calculatePercentage(summary.getCommitteeFocusedMembers(), party.getHeadCount())),
  263.                 VaadinIcons.CLIPBOARD_USER,
  264.                 "Members focused on committee work"));

  265.             // Collaboration Metrics
  266.             layout.addComponent(CardInfoRowUtil.createInfoRow("Collaborative Members:",
  267.                 String.format(Locale.ENGLISH,"%d (%d%%)",
  268.                     summary.getHighlyCollaborativeMembers(),
  269.                     calculatePercentage(summary.getHighlyCollaborativeMembers(), party.getHeadCount())),
  270.                 VaadinIcons.CONNECT,
  271.                 "Members with high cross-party collaboration"));
  272.         }
  273.     }

  274.     /**
  275.      * Calculate percentage.
  276.      *
  277.      * @param value the value
  278.      * @param total the total
  279.      * @return the int
  280.      */
  281.     // Helper method for calculating percentages
  282.     private int calculatePercentage(final long value, final long total) {
  283.         return total > 0 ? Math.round((float) value * 100 / total) : 0;
  284.     }

  285.     /**
  286.      * Calculate activity rate.
  287.      *
  288.      * @param activeMembers the active members
  289.      * @param totalMembers the total members
  290.      * @return the double
  291.      */
  292.     // Helper method for calculating activity rates
  293.     private double calculateActivityRate(final long activeMembers, final long totalMembers) {
  294.         return totalMembers > 0 ? (double) activeMembers * 100 / totalMembers : 0;
  295.     }

  296.     /**
  297.      * Matches.
  298.      *
  299.      * @param page the page
  300.      * @param parameters the parameters
  301.      * @return true, if successful
  302.      */
  303.     @Override
  304.     public boolean matches(final String page, final String parameters) {
  305.         return PageCommandPartyConstants.COMMAND_PARTY_OVERVIEW.matches(page, parameters);
  306.     }
  307. }