diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/ApplicationMenuItemFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/ApplicationMenuItemFactoryImpl.java index c8a5818b0d5..869d946c730 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/ApplicationMenuItemFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/ApplicationMenuItemFactoryImpl.java @@ -100,10 +100,19 @@ public final class ApplicationMenuItemFactoryImpl extends AbstractMenuItemFactor private static final PageModeMenuCommand COMMAND_CHARTS_CURRENT_MINISTRIES_LEADER_SCOREBOARD = new PageModeMenuCommand(UserViews.MINISTRY_RANKING_VIEW_NAME, PageMode.CHARTS, ChartIndicators.CURRENTMINISTRIESLEADERSCORECARD.toString()); + /** The Constant COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD. */ + private static final PageModeMenuCommand COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD = new PageModeMenuCommand(UserViews.PARTY_RANKING_VIEW_NAME, PageMode.CHARTS, ChartIndicators.CURRENTPARTYLEADERSCORECARD.toString()); + /** The Constant COMMITTEE_RANKING_TEXT. */ private static final String COMMITTEE_RANKING_TEXT = "Committee Ranking"; + /** The Constant DESC_LEADERS_SCOREBOARD. */ + private static final String DESC_LEADERS_SCOREBOARD = "Leaders: comparing party leadership impact."; + + /** The Constant PART_LEADERS_SCOREBOARD. */ + private static final String PART_LEADERS_SCOREBOARD = "Party leaders scoreboard"; + /** The Constant COMMITTEE_RANKING_LINK_TEXT. */ private static final String COMMITTEE_RANKING_LINK_TEXT = COMMITTEE_RANKING_TEXT; @@ -254,6 +263,7 @@ public void addRankingMenu(final MenuBar menuBar) { countryMenuItemFactory.createCountryTopicMenu(swedenMenuItem); rankingsMenuItem.addItem(MINISTRIES_LEADER_SCOREBOARD, VaadinIcons.TROPHY,COMMAND_CHARTS_CURRENT_MINISTRIES_LEADER_SCOREBOARD); + rankingsMenuItem.addItem(PART_LEADERS_SCOREBOARD, VaadinIcons.TROPHY,COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD); final MenuItem countryMenuItem = rankingsMenuItem.addItem(COUNTRY_RANKING_LINK_TEXT, VaadinIcons.FLAG, COMMAND_COUNTRY_RANKING_OVERVIEW); countryMenuItemFactory.createCountryTopicMenu(countryMenuItem); @@ -308,6 +318,9 @@ public void createOverviewPage(final VerticalLayout panelContent) { createButtonLink(grid, MINISTRIES_LEADER_SCOREBOARD, VaadinIcons.TROPHY, COMMAND_CHARTS_CURRENT_MINISTRIES_LEADER_SCOREBOARD, MINISTRIES_LEADER_SCOREBOARD_DESCRIPTION); + createButtonLink(grid, PART_LEADERS_SCOREBOARD, VaadinIcons.TROPHY, + COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD, DESC_LEADERS_SCOREBOARD); + createButtonLink(grid, COUNTRY_RANKING_LINK_TEXT, VaadinIcons.FLAG, COMMAND_COUNTRY_RANKING_OVERVIEW, COUNTRY_RANKING_DESCRIPTION); createButtonLink(grid, MINISTRY_RANKING_LINK_TEXT, VaadinIcons.OFFICE, COMMAND_MINISTRY_RANKING_OVERVIEW, MINISTRY_RANKING_DESCRIPTION); createButtonLink(grid, MINISTRIES_LINK_TEXT, VaadinIcons.OFFICE, COMMAND_MINISTRY_RANKING_DATAGRID, MINISTRIES_DESCRIPTION); diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/CommitteeRankingMenuItemFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/CommitteeRankingMenuItemFactoryImpl.java index 6a8d84faee8..b807c16e433 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/CommitteeRankingMenuItemFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/CommitteeRankingMenuItemFactoryImpl.java @@ -105,9 +105,6 @@ public final class CommitteeRankingMenuItemFactoryImpl extends AbstractMenuItemF /** The Constant POLITICAL_WORK_SUMMARY_TEXT. */ private static final String POLITICAL_WORK_SUMMARY_TEXT = "Political Work Summary"; - /** The Constant RANKING_LIST_BY_TOPIC_TEXT. */ - private static final String RANKING_LIST_BY_TOPIC_TEXT = "Ranking list by topic"; - /** The Constant POLITICAL_WORK_SUMMARY_DESCRIPTION. */ private static final String POLITICAL_WORK_SUMMARY_DESCRIPTION = "Scoreboard over current member size, political days served and total assignments"; @@ -162,9 +159,7 @@ public void createCommitteeeRankingMenuBar(final MenuBar menuBar) { public void createCommitteeRankingTopics(final MenuItem committeeMenuItem) { committeeMenuItem.addItem(OVERVIEW_TEXT, VaadinIcons.GROUP, COMMAND_OVERVIEW); - final MenuItem listByTopic = committeeMenuItem.addItem(RANKING_LIST_BY_TOPIC_TEXT, VaadinIcons.GROUP, null); - - final MenuItem listItem = listByTopic.addItem(POLITICAL_WORK_SUMMARY_TEXT,VaadinIcons.GROUP, COMMAND_DATAGRID); + final MenuItem listItem = committeeMenuItem.addItem(POLITICAL_WORK_SUMMARY_TEXT,VaadinIcons.GROUP, COMMAND_DATAGRID); listItem.setDescription(CURRENT_AND_PAST_MEMBER_AND_SUMMARY_OF_POLTICIAL_DAYS); final MenuItem chartByTopic = committeeMenuItem.addItem(CHART_BY_TOPIC_TEXT, VaadinIcons.GROUP, null); diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/MinistryRankingMenuItemFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/MinistryRankingMenuItemFactoryImpl.java index 8ee4788ae5b..5c305ffa870 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/MinistryRankingMenuItemFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/MinistryRankingMenuItemFactoryImpl.java @@ -145,9 +145,6 @@ public final class MinistryRankingMenuItemFactoryImpl extends AbstractMenuItemFa /** The Constant POLITICAL_WORK_SUMMARY_TEXT. */ private static final String POLITICAL_WORK_SUMMARY_TEXT = "Political Work Summary"; - /** The Constant RANKING_LIST_BY_TOPIC_TEXT. */ - private static final String RANKING_LIST_BY_TOPIC_TEXT = "Ranking list by topic"; - /** The Constant CURRENT_MINISTRIES_CURRENT_MEMBERS_DESCRIPTION. */ private static final String CURRENT_MINISTRIES_CURRENT_MEMBERS_DESCRIPTION = "Chart over current ministries by headcount"; @@ -207,9 +204,7 @@ public void createMinistryRankingTopics(final MenuItem ministryMenuItem) { ministryMenuItem.addItem(OVERVIEW_TEXT, VaadinIcons.DASHBOARD, COMMAN_OVERVIEW); - final MenuItem listByTopic = ministryMenuItem.addItem(RANKING_LIST_BY_TOPIC_TEXT, VaadinIcons.LIST, null); - - final MenuItem listItem = listByTopic.addItem(POLITICAL_WORK_SUMMARY_TEXT, VaadinIcons.BAR_CHART, COMMAND_DATAGRID); + final MenuItem listItem = ministryMenuItem.addItem(POLITICAL_WORK_SUMMARY_TEXT, VaadinIcons.BAR_CHART, COMMAND_DATAGRID); listItem.setDescription(CURRENT_AND_PAST_MEMBER_AND_SUMMARY_OF_TOTAL_POLTICIAL_DAYS_MEMBERSHIP_DESCRIPTION); final MenuItem chartByTopic = ministryMenuItem.addItem(CHART_BY_TOPIC_TEXT, VaadinIcons.PIE_CHART, null); diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PartyRankingMenuItemFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PartyRankingMenuItemFactoryImpl.java index 8fcdf5d172f..443e5501016 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PartyRankingMenuItemFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PartyRankingMenuItemFactoryImpl.java @@ -60,7 +60,6 @@ public final class PartyRankingMenuItemFactoryImpl extends AbstractMenuItemFacto private static final String PARTY_BY_TOTAL_MEMBERS_BASED_ON_ROLES_IN_DEPARTMENTS_COMMITTEES_AND_PARLIAMENT = "Party by total members across EU/gov/committees/parliament"; private static final String PARTY_RANKING = "Party Ranking"; - private static final String RANKING_LIST_BY_TOPIC_TEXT = "Ranking list by topic"; private static final String TOTAL_MEMBERS = "Total members"; // Political analyst perspective descriptions (~50 chars) @@ -104,6 +103,9 @@ public PartyRankingMenuItemFactoryImpl() { public void createOverviewPage(final VerticalLayout panelContent) { final ResponsiveRow grid = RowUtil.createGridLayout(panelContent); + createButtonLink(grid, PART_LEADERS_SCOREBOARD, VaadinIcons.TROPHY, + COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD, DESC_LEADERS_SCOREBOARD); + createButtonLink(grid, TOTAL_MEMBERS, VaadinIcons.USERS, COMMAND_DATAGRID, DESC_ALL_PARTIES_ROLES); @@ -116,8 +118,6 @@ public void createOverviewPage(final VerticalLayout panelContent) { createButtonLink(grid, CURRENT_PARTIES_ACTIVE_IN_PARLIAMENT_HEAD_COUNT, VaadinIcons.INSTITUTION, COMMAND_CHARTS_CURRENT_PARTIES, DESC_PARLIAMENT_HEADCOUNT); - createButtonLink(grid, PART_LEADERS_SCOREBOARD, VaadinIcons.TROPHY, - COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD, DESC_LEADERS_SCOREBOARD); createButtonLink(grid, ALL_PARTIES_TOTAL_DAYS_SERVED_IN_PARLIAMENT, VaadinIcons.CLOCK, COMMAND_CHARTS_ALL_PARTIES, DESC_DAYS_SERVED_PARLIAMENT); @@ -153,10 +153,10 @@ public void createPartyRankingMenuBar(final MenuBar menuBar) { public void createPartyRankingTopics(final MenuItem partynMenuItem) { partynMenuItem.addItem(OVERVIEW_TEXT, VaadinIcons.DASHBOARD, COMMAND_OVERVIEW); - final MenuItem listByTopic = partynMenuItem.addItem(RANKING_LIST_BY_TOPIC_TEXT, VaadinIcons.LIST, null); + partynMenuItem.addItem(PART_LEADERS_SCOREBOARD, VaadinIcons.TROPHY,COMMAND_CHARTS_CURRENT_PARTIES_LEADER_SCOREBOARD); // Total members using USERS icon for multiple people - final MenuItem listItem = listByTopic.addItem(TOTAL_MEMBERS, VaadinIcons.USERS, COMMAND_DATAGRID); + final MenuItem listItem = partynMenuItem.addItem(TOTAL_MEMBERS, VaadinIcons.USERS, COMMAND_DATAGRID); listItem.setDescription(PARTY_BY_TOTAL_MEMBERS_BASED_ON_ROLES_IN_DEPARTMENTS_COMMITTEES_AND_PARLIAMENT); final MenuItem chartByTopic = partynMenuItem.addItem(CHART_BY_TOPIC_TEXT, VaadinIcons.CHART, null); diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PoliticianRankingMenuItemFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PoliticianRankingMenuItemFactoryImpl.java index 661a65f617a..55be49c93b2 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PoliticianRankingMenuItemFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/common/menufactory/impl/PoliticianRankingMenuItemFactoryImpl.java @@ -57,7 +57,6 @@ public final class PoliticianRankingMenuItemFactoryImpl extends AbstractMenuItem private static final String PAGE_VISIT_HISTORY_TEXT = "Page Visit History"; private static final String POLITICAL_EXPERIENCE_SUMMARY = "Political Experience Summary"; private static final String POLITICIAN_RANKING = "Politician Ranking"; - private static final String RANKING_LIST_BY_TOPIC_TEXT = "Ranking list by topic"; // Description for total experience detail (tooltip) private static final String CURRENT_AND_PAST_ASSIGNMENTS_DESCRIPTION = @@ -146,11 +145,8 @@ public void createPoliticianRankingTopics(final MenuItem politicianMenuItem) { // Overview: DASHBOARD for a general overview panel politicianMenuItem.addItem(OVERVIEW_TEXT, VaadinIcons.DASHBOARD, COMMAND_OVERVIEW); - // Ranking list by topic: LIST icon for enumerations - final MenuItem listByTopic = politicianMenuItem.addItem(RANKING_LIST_BY_TOPIC_TEXT, VaadinIcons.LIST, null); - // Political experience summary: USER_CLOCK indicating experience over time - final MenuItem listItem = listByTopic.addItem(POLITICAL_EXPERIENCE_SUMMARY, VaadinIcons.USER_CLOCK, COMMAND_DATAGRID); + final MenuItem listItem = politicianMenuItem.addItem(POLITICAL_EXPERIENCE_SUMMARY, VaadinIcons.USER_CLOCK, COMMAND_DATAGRID); listItem.setDescription(CURRENT_AND_PAST_ASSIGNMENTS_DESCRIPTION); // Chart by topic: CHART icon for visual data representation diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/country/pagemode/WorldIndicatorsPageModContentFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/country/pagemode/WorldIndicatorsPageModContentFactoryImpl.java index c551f61e580..fe2d7a95814 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/country/pagemode/WorldIndicatorsPageModContentFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/country/pagemode/WorldIndicatorsPageModContentFactoryImpl.java @@ -19,7 +19,6 @@ package com.hack23.cia.web.impl.ui.application.views.user.country.pagemode; import java.io.Serializable; -import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -38,22 +37,22 @@ import com.hack23.cia.web.impl.ui.application.action.ViewAction; import com.hack23.cia.web.impl.ui.application.views.common.chartfactory.api.WorldIndicatorChartDataManager; import com.hack23.cia.web.impl.ui.application.views.common.viewnames.PageMode; +import com.vaadin.icons.VaadinIcons; +import com.vaadin.server.Responsive; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; import com.vaadin.ui.Layout; import com.vaadin.ui.MenuBar; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; - /** * The Class WorldIndicatorsPageModContentFactoryImpl. */ @Component public final class WorldIndicatorsPageModContentFactoryImpl extends AbstractCountryPageModContentFactoryImpl { - private static final List AS_LIST = Arrays.asList("indicatorName", - "sourceValue", - "sourceOrganization"); - /** The chart data manager. */ @Autowired private WorldIndicatorChartDataManager chartDataManager; @@ -70,59 +69,116 @@ public WorldIndicatorsPageModContentFactoryImpl() { public Layout createContent(final String parameters, final MenuBar menuBar, final Panel panel) { final VerticalLayout panelContent = createPanelContent(); getCountryMenuItemFactory().createCountryTopicMenu(menuBar); - createPageHeader(panel, panelContent,"Country Indicator","Country Indicator","Compare key performance indicators across multiple countries."); + createPageHeader(panel, panelContent, "Country Indicator", "Country Indicator", + "Compare key performance indicators across multiple countries."); final String pageId = getPageId(parameters); - final String indicator = parameters.substring(PageMode.INDICATORS.toString().length()+"/".length()); + final String indicator = parameters.substring(PageMode.INDICATORS.toString().length() + "/".length()); - createDataIndicatorSummaryChartPanel(panelContent,indicator,panel); + createDataIndicatorSummaryChartPanel(panelContent, indicator, panel); - getPageActionEventHelper().createPageEvent(ViewAction.VISIT_COUNTRY_VIEW, ApplicationEventGroup.USER, NAME, parameters, pageId); + getPageActionEventHelper().createPageEvent(ViewAction.VISIT_COUNTRY_VIEW, ApplicationEventGroup.USER, NAME, + parameters, pageId); return panelContent; - } /** * Creates the data indicator summary chart panel. * - * @param verticalLayout - * the vertical layout - * @param indicator - * the indicator - * @param panel - * the panel + * @param verticalLayout the vertical layout + * @param indicator the indicator + * @param panel the panel */ - private void createDataIndicatorSummaryChartPanel(final VerticalLayout verticalLayout,final String indicator,final Panel panel) { - - final DataContainer indicatorDataCountrSummaryDailyDataContainer = getApplicationManager() + private void createDataIndicatorSummaryChartPanel(final VerticalLayout verticalLayout, final String indicator, + final Panel panel) { + final DataContainer indicatorDataSummaryContainer = getApplicationManager() .getDataContainer(ViewWorldbankIndicatorDataCountrySummary.class); - - final Optional indicatorSummary = indicatorDataCountrSummaryDailyDataContainer - .getAll() - .parallelStream() + final Optional indicatorSummary = indicatorDataSummaryContainer + .getAll().parallelStream() .filter(t -> t != null && t.getEmbeddedId().getIndicatorId().equals(indicator)).findFirst(); - ViewWorldbankIndicatorDataCountrySummary indicatorSummaryValue = null; if (indicatorSummary.isPresent()) { indicatorSummaryValue = indicatorSummary.get(); - getFormFactory().addFormPanelTextFields(verticalLayout, - indicatorSummaryValue, - ViewWorldbankIndicatorDataCountrySummary.class, - AS_LIST); + // Instead of formFactory, we create a card for the indicator summary + final Label sectionHeader = new Label("Indicator Summary"); + sectionHeader.addStyleName("section-header"); + verticalLayout.addComponent(sectionHeader); + + final VerticalLayout card = new VerticalLayout(); + card.setMargin(true); + card.setSpacing(true); + card.addStyleName("indicator-summary-card"); + Responsive.makeResponsive(card); + + // For each field in AS_LIST, we create a row + if (indicatorSummaryValue.getIndicatorName() != null) { + card.addComponent(createInfoRow("Indicator Name:", indicatorSummaryValue.getIndicatorName(), + VaadinIcons.INFO_CIRCLE, "Name of the indicator")); + } + if (indicatorSummaryValue.getSourceValue() != null) { + card.addComponent(createInfoRow("Source Value:", indicatorSummaryValue.getSourceValue(), + VaadinIcons.GLOBE, "Source of this indicator data")); + } + if (indicatorSummaryValue.getSourceOrganization() != null) { + card.addComponent(createInfoRow("Source Organization:", indicatorSummaryValue.getSourceOrganization(), + VaadinIcons.INSTITUTION, "Organization providing this data")); + } + + verticalLayout.addComponent(card); } final DataContainer dataContainer = getApplicationManager() - .getDataContainer(WorldBankData.class); + .getDataContainer(WorldBankData.class); + final List dataList = dataContainer.findListByEmbeddedProperty(WorldBankData.class, + WorldBankData_.indicator, Indicator.class, Indicator_.id, indicator); - final List dataList = dataContainer.findListByEmbeddedProperty(WorldBankData.class, WorldBankData_.indicator, Indicator.class, Indicator_.id, indicator); + chartDataManager.createIndicatorChart(verticalLayout, dataList, indicatorSummaryValue); + } - chartDataManager.createIndicatorChart(verticalLayout,dataList,indicatorSummaryValue); + /** + * Creates a row displaying a caption and value, with optional icon and tooltip. + * + * @param caption the field caption + * @param value the field value + * @param icon a VaadinIcons icon for better visual cue + * @param tooltip optional tooltip to provide more info + * @return a HorizontalLayout representing the info row + */ + private HorizontalLayout createInfoRow(final String caption, final String value, VaadinIcons icon, + final String tooltip) { + final HorizontalLayout layout = new HorizontalLayout(); + layout.setSpacing(true); + layout.addStyleName("metric-label"); + layout.setWidthUndefined(); + + if (icon != null) { + final Label iconLabel = new Label(icon.getHtml(), ContentMode.HTML); + iconLabel.addStyleName("card-info-icon"); + if (tooltip != null && !tooltip.isEmpty()) { + iconLabel.setDescription(tooltip); + } + layout.addComponent(iconLabel); + } + + final Label captionLabel = new Label(caption); + captionLabel.addStyleName("card-info-caption"); + if (tooltip != null && !tooltip.isEmpty()) { + captionLabel.setDescription(tooltip); + } + layout.addComponent(captionLabel); + + if (value != null && !value.isEmpty()) { + final Label valueLabel = new Label(value); + valueLabel.addStyleName("card-info-value"); + layout.addComponent(valueLabel); + } + return layout; } @Override diff --git a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/party/pagemode/PartyRankingCurrentPartiesLeaderScoreboardPageModContentFactoryImpl.java b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/party/pagemode/PartyRankingCurrentPartiesLeaderScoreboardPageModContentFactoryImpl.java index 9671588098a..fa3436e742a 100644 --- a/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/party/pagemode/PartyRankingCurrentPartiesLeaderScoreboardPageModContentFactoryImpl.java +++ b/citizen-intelligence-agency/src/main/java/com/hack23/cia/web/impl/ui/application/views/user/party/pagemode/PartyRankingCurrentPartiesLeaderScoreboardPageModContentFactoryImpl.java @@ -25,15 +25,21 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Service; +import com.hack23.cia.model.internal.application.data.ministry.impl.ViewRiksdagenGovermentRoleMember; +import com.hack23.cia.model.internal.application.data.ministry.impl.ViewRiksdagenGovermentRoleMember_; import com.hack23.cia.model.internal.application.data.party.impl.ViewRiksdagenPartyRoleMember; import com.hack23.cia.model.internal.application.data.party.impl.ViewRiksdagenPartyRoleMember_; import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPolitician; import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPolitician_; import com.hack23.cia.model.internal.application.system.impl.ApplicationEventGroup; import com.hack23.cia.service.api.DataContainer; +import com.hack23.cia.service.external.esv.api.EsvApi; +import com.hack23.cia.service.external.esv.api.GovernmentBodyAnnualOutcomeSummary; +import com.hack23.cia.service.external.esv.api.GovernmentBodyAnnualSummary; import com.hack23.cia.web.impl.ui.application.action.ViewAction; import com.hack23.cia.web.impl.ui.application.views.common.rows.RowUtil; import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentRatio; @@ -63,6 +69,11 @@ public final class PartyRankingCurrentPartiesLeaderScoreboardPageModContentFacto /** The Constant NAME. */ public static final String NAME = UserViews.PARTY_RANKING_VIEW_NAME; + /** The esv api. */ + // Adding EsvApi to fetch ministry-related data + @Autowired + private EsvApi esvApi; + /** * Instantiates a new party ranking current parties leader scoreboard page mod content factory impl. */ @@ -95,7 +106,6 @@ public Layout createContent(final String parameters, final MenuBar menuBar, fina panelContent.addComponent(chartLayout); panelContent.setExpandRatio(chartLayout, ContentRatio.LARGE_FORM); - // Create a responsive row within chartLayout to hold the leader cards final VerticalLayout wrapper = new VerticalLayout(); wrapper.setSizeFull(); chartLayout.addComponent(wrapper); @@ -103,33 +113,37 @@ public Layout createContent(final String parameters, final MenuBar menuBar, fina final ResponsiveRow row = RowUtil.createGridLayout(wrapper); row.setSizeFull(); - // Load party leaders and their politician data final Map politicianMap = loadPoliticiansByPersonId(); final Map partyLeaderMap = computePartyLeaders(politicianMap.keySet()); - // Filter only party leaders final List partyLeaders = politicianMap.values().stream() .filter(p -> partyLeaderMap.getOrDefault(p.getPersonId(), false)) .collect(Collectors.toList()); - // Sort leaders: those currently in government first, then non-government + // Sort: in government first, then alphabetical by last name partyLeaders.sort((a, b) -> { final boolean aInGov = a.isActiveGovernment(); final boolean bInGov = b.isActiveGovernment(); if (aInGov == bInGov) { - // If both are in the same category, sort by last name or some other criteria return a.getLastName().compareToIgnoreCase(b.getLastName()); } // government first return Boolean.compare(!aInGov, !bInGov); }); - // Create cards for each party leader + // Load ESV data for ministries + final Map> dataMap = esvApi.getData(); + final int CURRENT_YEAR = 2024; + final List currentYearGovernmentBodies = dataMap.get(CURRENT_YEAR); + final Map> governmentBodyByMinistry = currentYearGovernmentBodies + .stream().collect(Collectors.groupingBy(GovernmentBodyAnnualSummary::getMinistry)); + + final Map> reportByMinistry = esvApi + .getGovernmentBodyReportByMinistry(); + for (final ViewRiksdagenPolitician leader : partyLeaders) { - final Panel cardPanel = createLeaderCard(leader); - row.addColumn() - .withDisplayRules(12, 6, 4, 4) // responsive column distribution - .withComponent(cardPanel); + final Panel cardPanel = createLeaderCard(leader, governmentBodyByMinistry, reportByMinistry); + row.addColumn().withDisplayRules(12, 6, 4, 4).withComponent(cardPanel); } getPageActionEventHelper().createPageEvent(ViewAction.VISIT_PARTY_RANKING_VIEW, ApplicationEventGroup.USER, NAME, @@ -147,12 +161,10 @@ private Map loadPoliticiansByPersonId() { final DataContainer politicianDataContainer = getApplicationManager() .getDataContainer(ViewRiksdagenPolitician.class); - // Load all active politicians, not just government ones, to include non-gov leaders final List activePoliticians = politicianDataContainer.findListByProperty( new Object[] { Boolean.TRUE }, ViewRiksdagenPolitician_.active); - return activePoliticians.stream() - .collect(Collectors.toMap(ViewRiksdagenPolitician::getPersonId, p -> p)); + return activePoliticians.stream().collect(Collectors.toMap(ViewRiksdagenPolitician::getPersonId, p -> p)); } /** @@ -171,21 +183,59 @@ private Map computePartyLeaders(Iterable personIds) { new Object[] { personId, Boolean.TRUE }, ViewRiksdagenPartyRoleMember_.personId, ViewRiksdagenPartyRoleMember_.active); - final boolean isLeader = roles.stream().anyMatch( - role -> role.getRoleCode() != null && "Partiledare".equalsIgnoreCase(role.getRoleCode().trim())); - + final boolean isLeader = roles.stream() + .anyMatch(role -> role.getRoleCode() != null && "Partiledare".equalsIgnoreCase(role.getRoleCode().trim())); result.put(personId, isLeader); } return result; } + /** + * Checks if is party leader. + * + * @param personId the person id + * @return true, if is party leader + */ + private boolean isPartyLeader(String personId) { + final DataContainer partyRoleMemberDataContainer = getApplicationManager() + .getDataContainer(ViewRiksdagenPartyRoleMember.class); + + final List partyRoles = partyRoleMemberDataContainer.findListByProperty( + new Object[] { personId, Boolean.TRUE }, ViewRiksdagenPartyRoleMember_.personId, + ViewRiksdagenPartyRoleMember_.active); + + return partyRoles.stream().anyMatch(r -> "Partiledare".equalsIgnoreCase(r.getRoleCode())); + } + + /** + * Gets the party leader role. + * + * @param personId the person id + * @return the party leader role + */ + private ViewRiksdagenPartyRoleMember getPartyLeaderRole(String personId) { + final DataContainer partyRoleMemberDataContainer = getApplicationManager() + .getDataContainer(ViewRiksdagenPartyRoleMember.class); + + final List partyRoles = partyRoleMemberDataContainer.findListByProperty( + new Object[] { personId, Boolean.TRUE }, ViewRiksdagenPartyRoleMember_.personId, + ViewRiksdagenPartyRoleMember_.active); + + return partyRoles.stream().filter(r -> "Partiledare".equalsIgnoreCase(r.getRoleCode())).findFirst().orElse(null); + } + /** * Creates the leader card. * * @param leader the leader + * @param governmentBodyByMinistry the government body by ministry + * @param reportByMinistry the report by ministry * @return the panel */ - private Panel createLeaderCard(final ViewRiksdagenPolitician leader) { + private Panel createLeaderCard(final ViewRiksdagenPolitician leader, + final Map> governmentBodyByMinistry, + final Map> reportByMinistry) { + final Panel cardPanel = new Panel(); cardPanel.addStyleName("leader-baseball-card"); cardPanel.setSizeFull(); @@ -221,31 +271,79 @@ private Panel createLeaderCard(final ViewRiksdagenPolitician leader) { partyLink.setIcon(VaadinIcons.GROUP); cardContent.addComponent(partyLink); - // If leader is in government, note it + final boolean isPartyLeader = isPartyLeader(leader.getPersonId()); + if (isPartyLeader) { + final ViewRiksdagenPartyRoleMember leaderRole = getPartyLeaderRole(leader.getPersonId()); + if (leaderRole != null) { + final Label subHeader = new Label("Partiledare (" + leader.getParty() + ") since " + leaderRole.getFromDate()); + subHeader.addStyleName("card-subtitle"); + cardContent.addComponent(subHeader); + } + } + + // Government or not if (leader.isActiveGovernment()) { final Label govLabel = new Label("Currently in Government"); govLabel.addStyleName("card-subtitle"); cardContent.addComponent(govLabel); + + // Add ministry summary if we can identify their ministry + // The ministry detail is stored in the same structure as the ministry snippet: + // We need to find which ministry they belong to + // In the ministry snippet, "govMember.getDetail()" gives ministry detail key. + // Here we only have leader, not govMember. We must find a corresponding approach: + // In party leaders snippet, we didn't load govMember. We'll need to adapt: + + // Let's assume we can identify the leader's ministry from active government roles data: + // we do similar approach: load active government role members and find the one matching this leader + final ViewRiksdagenPolitician pol = leader; // same as leader + final ViewRiksdagenGovermentRoleMember govMember = findGovernmentRoleForLeader(pol); + if (govMember != null) { + addMinistryRoleSummary(cardContent, govMember, governmentBodyByMinistry, reportByMinistry); + } + } else { final Label nonGovLabel = new Label("Not in Government"); nonGovLabel.addStyleName("card-subtitle-nongov"); cardContent.addComponent(nonGovLabel); } - // Add tenure and experience info + // Tenure and Experience rows final VerticalLayout statsContainer = new VerticalLayout(); statsContainer.setSpacing(false); statsContainer.addStyleName("card-stats-container"); statsContainer.setWidth("100%"); - // Tenure as party leader if available - final Label tenureLabel = createTenureLabel(leader); - if (tenureLabel != null) { - statsContainer.addComponent(tenureLabel); - } + // Tenure (assuming leader might have totalDaysServed property) + final Label tenureIcon = new Label(VaadinIcons.CLOCK.getHtml(), ContentMode.HTML); + tenureIcon.setDescription("Total Tenure"); + final Label tenureLabel = new Label("Tenure:"); + tenureLabel.addStyleName("card-tenure-text"); + final Label tenureValue = new Label(leader.getTotalDaysServedParty() + " days"); + tenureValue.addStyleName("card-tenure-value"); + final HorizontalLayout tenureLayout = new HorizontalLayout(tenureIcon, tenureLabel, tenureValue); + tenureLayout.setSpacing(true); + tenureLayout.addStyleName("card-tenure"); + statsContainer.addComponent(tenureLayout); + + // Experience + final HorizontalLayout experienceLayout = new HorizontalLayout(); + experienceLayout.setSpacing(true); + experienceLayout.addStyleName("card-experience-section"); + final Label expIcon = new Label(VaadinIcons.USER_CHECK.getHtml(), ContentMode.HTML); + expIcon.setDescription("Political Experience"); + final Label expLabel = new Label("Experience:"); + expLabel.addStyleName("card-experience-text"); - // Experience row - statsContainer.addComponent(createExperienceRow(leader)); + final int govYears = (int) (leader.getTotalDaysServedGovernment() / 365); + final int partyYears = (int) (leader.getTotalDaysServedParty() / 365); + final int parliamentYears = (int) (leader.getTotalDaysServedParliament() / 365); + final String expText = String.format(Locale.ROOT, "Government: %dy, Party: %dy, Parliament: %dy", + govYears, partyYears, parliamentYears); + final Label expValue = new Label(expText); + expValue.addStyleName("card-experience-value"); + experienceLayout.addComponents(expIcon, expLabel, expValue); + statsContainer.addComponent(experienceLayout); cardContent.addComponent(statsContainer); @@ -253,54 +351,121 @@ private Panel createLeaderCard(final ViewRiksdagenPolitician leader) { } /** - * Creates the tenure label. + * Find government role for leader. * * @param leader the leader - * @return the label + * @return the view riksdagen goverment role member */ - private Label createTenureLabel(ViewRiksdagenPolitician leader) { - // Assuming leader has a property or a way to compute total days as party leader - // If not available, this may need to query ViewRiksdagenPartyRoleMember for fromDate - // and compute tenure. For simplicity, assume total days served in party roles is a proxy. - - final long daysAsPartyLeader = leader.getTotalDaysServedParty(); // hypothetical property - if (daysAsPartyLeader > 0) { - final Label tenureLabel = new Label("Party Leadership Tenure: " + daysAsPartyLeader + " days"); - tenureLabel.addStyleName("card-tenure-value"); - return tenureLabel; - } - return null; + private ViewRiksdagenGovermentRoleMember findGovernmentRoleForLeader(ViewRiksdagenPolitician leader) { + // Similar to ministry snippet loadActiveGovernmentRoleMembers: + final DataContainer govermentRoleMemberDataContainer = getApplicationManager() + .getDataContainer(ViewRiksdagenGovermentRoleMember.class); + final List activeGovMembers = govermentRoleMemberDataContainer.findListByProperty( + new Object[] { Boolean.TRUE }, ViewRiksdagenGovermentRoleMember_.active); + + // Find the government role that matches this leader's personId + return activeGovMembers.stream() + .filter(govMember -> govMember.getPersonId().equals(leader.getPersonId())) + .findFirst().orElse(null); } /** - * Creates the experience row. + * Adds the ministry role summary. * - * @param leader the leader - * @return the horizontal layout + * @param cardLayout the card layout + * @param govMember the gov member + * @param governmentBodyByMinistry the government body by ministry + * @param reportByMinistry the report by ministry */ - private HorizontalLayout createExperienceRow(final ViewRiksdagenPolitician leader) { - final HorizontalLayout experienceLayout = new HorizontalLayout(); - experienceLayout.setSpacing(true); - experienceLayout.addStyleName("card-experience-section"); + private void addMinistryRoleSummary(final VerticalLayout cardLayout, + final ViewRiksdagenGovermentRoleMember govMember, + final Map> governmentBodyByMinistry, + final Map> reportByMinistry) { - final Label expIcon = new Label(VaadinIcons.USER_CHECK.getHtml(), ContentMode.HTML); - expIcon.setDescription("Political Experience"); + cardLayout.addComponent(getPageLinkFactory().addMinistryPageLink(govMember.getDetail())); - final Label expLabel = new Label("Experience:"); - expLabel.addStyleName("card-experience-text"); + final List ministryBodies = governmentBodyByMinistry.get(govMember.getDetail()); + if (ministryBodies == null || ministryBodies.isEmpty()) { + return; + } - final int partyYears = (int) (leader.getTotalDaysServedParty() / 365); - final int parliamentYears = (int) (leader.getTotalDaysServedParliament() / 365); - final int govYears = (int) (leader.getTotalDaysServedGovernment() / 365); + final int totalHeadCount = ministryBodies.stream().mapToInt(GovernmentBodyAnnualSummary::getAnnualWorkHeadCount) + .sum(); + final int bodyCount = ministryBodies.size(); + + cardLayout.addComponent(createMetricRow(VaadinIcons.GROUP, + getPageLinkFactory().addMinistryGovermentBodiesPageLink(govMember.getDetail()), + "Number of government bodies", String.valueOf(bodyCount))); + + cardLayout.addComponent(createMetricRow(VaadinIcons.USER, + getPageLinkFactory().addMinistryGovermentBodiesHeadcountPageLink(govMember.getDetail()), + "Total headcount of government bodies", String.valueOf(totalHeadCount))); + + final List outcomeSummaries = reportByMinistry.get(govMember.getDetail()); + double currentYearIncome = 0; + double currentYearSpending = 0; + final int CURRENT_YEAR = 2024; + final String INKOMSTTITELGRUPPSNAMN = "Inkomsttitelgruppsnamn"; + final String EXPENDITURE_GROUP_NAME = "Utgiftsområdesnamn"; + + if (outcomeSummaries != null) { + final Map annualIncome = outcomeSummaries.stream() + .filter(t -> t.getDescriptionFields().get(INKOMSTTITELGRUPPSNAMN) != null) + .collect(Collectors.groupingBy(GovernmentBodyAnnualOutcomeSummary::getYear, + Collectors.summingDouble(GovernmentBodyAnnualOutcomeSummary::getYearTotal))); + + final Map annualSpending = outcomeSummaries.stream() + .filter(t -> t.getDescriptionFields().get(EXPENDITURE_GROUP_NAME) != null) + .collect(Collectors.groupingBy(GovernmentBodyAnnualOutcomeSummary::getYear, + Collectors.summingDouble(GovernmentBodyAnnualOutcomeSummary::getYearTotal))); + + if (annualIncome.get(CURRENT_YEAR) != null) { + currentYearIncome = annualIncome.get(CURRENT_YEAR) / 1000; + } - final String expText = String.format(Locale.ROOT, "Government: %dy, Party: %dy, Parliament: %dy", govYears, - partyYears, parliamentYears); + if (annualSpending.get(CURRENT_YEAR) != null) { + currentYearSpending = annualSpending.get(CURRENT_YEAR) / 1000; + } + } - final Label expValue = new Label(expText); - expValue.addStyleName("card-experience-value"); + final String incomeStr = String.format(Locale.ROOT, "%.2f B SEK", currentYearIncome); + cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_UP, + getPageLinkFactory().addMinistryGovermentBodiesIncomePageLink(govMember.getDetail()), + "Yearly Income (B SEK)", incomeStr)); - experienceLayout.addComponents(expIcon, expLabel, expValue); - return experienceLayout; + final String spendingStr = String.format(Locale.ROOT, "%.2f B SEK", currentYearSpending); + cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_DOWN, + getPageLinkFactory().addMinistrGovermentBodiesSpendingPageLink(govMember.getDetail()), + "Yearly Spending (B SEK)", spendingStr)); + } + + /** + * Creates the metric row. + * + * @param icon the icon + * @param linkComponent the link component + * @param description the description + * @param valueText the value text + * @return the horizontal layout + */ + private HorizontalLayout createMetricRow(VaadinIcons icon, com.vaadin.ui.Component linkComponent, + String description, String valueText) { + final HorizontalLayout layout = new HorizontalLayout(); + layout.setSpacing(true); + layout.addStyleName("metric-label"); + layout.setWidthUndefined(); + + final Label iconLabel = new Label(icon.getHtml(), ContentMode.HTML); + iconLabel.setDescription(description); + + final Label valueLabel = new Label(valueText); + valueLabel.addStyleName("metric-value"); + + layout.addComponent(iconLabel); + layout.addComponent(linkComponent); + layout.addComponent(valueLabel); + + return layout; } /** @@ -315,4 +480,5 @@ public boolean matches(final String page, final String parameters) { return NAME.equals(page) && StringUtils.contains(parameters, PageMode.CHARTS.toString()) && parameters.contains(ChartIndicators.CURRENTPARTYLEADERSCORECARD.toString()); } + }