PoliticianLeaderboardUtil.java

  1. package com.hack23.cia.web.impl.ui.application.views.common.pagemode;

  2. import java.util.List;
  3. import java.util.Locale;
  4. import java.util.Map;
  5. import java.util.stream.Collectors;

  6. import org.apache.commons.lang3.StringUtils;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;

  9. import com.hack23.cia.model.internal.application.data.ministry.impl.ViewRiksdagenGovermentRoleMember;
  10. import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPolitician;
  11. import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPoliticianBallotSummary;
  12. import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPoliticianExperienceSummary;
  13. import com.hack23.cia.service.external.esv.api.GovernmentBodyAnnualOutcomeSummary;
  14. import com.hack23.cia.service.external.esv.api.GovernmentBodyAnnualSummary;
  15. import com.hack23.cia.web.impl.ui.application.views.common.pagelinks.api.PageLinkFactory;
  16. import com.vaadin.icons.VaadinIcons;
  17. import com.vaadin.ui.VerticalLayout;

  18. /**
  19.  * The Class PoliticianLeaderboardUtil.
  20.  */
  21. @Service
  22. public class PoliticianLeaderboardUtil extends CardInfoRowUtil {

  23.     /** The page link factory. */
  24.     @Autowired
  25.     public PageLinkFactory pageLinkFactory;

  26.     /**
  27.      * Instantiates a new politician leaderboard util.
  28.      */
  29.     public PoliticianLeaderboardUtil() {
  30.     }

  31.     /**
  32.      * Adds the political analysis comment.
  33.      *
  34.      * @param layout the layout
  35.      * @param experienceSummary the experience summary
  36.      */
  37.     public final void addPoliticalAnalysisComment(final VerticalLayout layout,
  38.             final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
  39.         // Split the analysis points and create a bullet list
  40.         final String[] analysisPoints = experienceSummary.getPoliticalAnalysisComment().split("\\s*\\|\\|\\s*");
  41.         final StringBuilder analys=new StringBuilder();
  42.         for (final String point : analysisPoints) {
  43.             if (StringUtils.isNotBlank(point)) {
  44.                 analys.append(" • ").append(point);
  45.             }
  46.            }

  47.         // Political Analysis Comment
  48.         if (StringUtils.isNotBlank(experienceSummary.getPoliticalAnalysisComment())) {
  49.             layout.addComponent(createInfoRow("Analysis:",
  50.                     analys.toString(),
  51.                 VaadinIcons.COMMENT,
  52.                 "Political career analysis"));
  53.         }
  54.     }

  55.     /**
  56.      * Adds the knowledge areas.
  57.      *
  58.      * @param layout the layout
  59.      * @param experienceSummary the experience summary
  60.      */
  61.     public final void addKnowledgeAreas(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
  62.         if (experienceSummary.getKnowledgeAreas() != null && !experienceSummary.getKnowledgeAreas().isEmpty()) {
  63.             final String topAreas = buildTopString(
  64.                 experienceSummary.getKnowledgeAreas(),
  65.                 ka -> ka.getArea(),
  66.                 ka -> ka.getWeightedExp()
  67.             );
  68.             if (!topAreas.isEmpty()) {
  69.                 layout.addComponent(createInfoRow("Top Knowledge Areas:", topAreas, VaadinIcons.BOOK, "Key expertise"));
  70.             }
  71.         }
  72.     }

  73.     /**
  74.      * Adds the top roles.
  75.      *
  76.      * @param layout the layout
  77.      * @param experienceSummary the experience summary
  78.      */
  79.     public final void addTopRoles(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
  80.         if (experienceSummary.getRoles() != null && !experienceSummary.getRoles().isEmpty()) {
  81.             final String topRoles = buildTopString(
  82.                 experienceSummary.getRoles(),
  83.                 role -> role.getRole(),
  84.                 role -> role.getWeightedExp()
  85.             );
  86.             if (!topRoles.isEmpty()) {
  87.                 layout.addComponent(createInfoRow("Top Roles:", topRoles, VaadinIcons.STAR, "Most significant roles")); // Changed from CROWN to STAR
  88.             }
  89.         }
  90.     }

  91.     /**
  92.      * Adds the experience metrics.
  93.      *
  94.      * @param layout the layout
  95.      * @param experienceSummary the experience summary
  96.      */
  97.     public final void addExperienceMetrics(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
  98.         if (experienceSummary != null) {
  99.             // Career Overview
  100.             layout.addComponent(createInfoRow("Career Phase:",
  101.                 experienceSummary.getCareerPhase().toString().replace("_", " "),
  102.                 VaadinIcons.CALENDAR_CLOCK,
  103.                 "Current career stage"));

  104.             // Experience Level
  105.             layout.addComponent(createInfoRow("Experience Level:",
  106.                 experienceSummary.getExperienceLevel().toString().replace("_", " "),
  107.                 VaadinIcons.CHART_TIMELINE,
  108.                 "Overall political experience classification"));

  109.             // Leadership Profile
  110.             layout.addComponent(createInfoRow("Leadership Role:",
  111.                 experienceSummary.getLeadershipProfile().toString().replace("_", " "),
  112.                 VaadinIcons.USER_STAR,
  113.                 "Leadership experience level"));

  114.             // Specialization
  115.             layout.addComponent(createInfoRow("Expertise:",
  116.                 experienceSummary.getSpecializationLevel().toString().replace("_", " "),
  117.                 VaadinIcons.SPECIALIST,
  118.                 "Area of specialization"));


  119.         }
  120.     }

  121.     /**
  122.      * Adds the legislative metrics.
  123.      *
  124.      * @param layout the layout
  125.      * @param politician the politician
  126.      */
  127.     public final void addLegislativeMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician) {

  128.         layout.addComponent(createInfoRow("Documents/Year:", String.format(Locale.ENGLISH,"%.1f", politician.getAverageDocsPerYear()),
  129.                 VaadinIcons.FILE_TEXT, "Average documents per year"));
  130.         layout.addComponent(createInfoRow("Individual Motions:", String.valueOf(politician.getIndividualMotions()),
  131.                 VaadinIcons.USER, "Personal motions submitted"));
  132.         layout.addComponent(createInfoRow("Party Motions:", String.valueOf(politician.getPartyMotions()),
  133.                 VaadinIcons.GROUP, "Party-based motions"));
  134.         layout.addComponent(createInfoRow("Committee Motions:", String.valueOf(politician.getCommitteeMotions()),
  135.                 VaadinIcons.GROUP, "Committee-based motions"));
  136.         layout.addComponent(createInfoRow("Document Impact:", politician.getDocActivityProfile(), VaadinIcons.CHART_3D,
  137.                 "Legislative influence assessment"));
  138.     }

  139.     /**
  140.      * Adds the party alignment metrics.
  141.      *
  142.      * @param layout the layout
  143.      * @param politician the politician
  144.      * @param ballotSummary the ballot summary
  145.      */
  146.     public final void addPartyAlignmentMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician,
  147.             final ViewRiksdagenPoliticianBallotSummary ballotSummary) {

  148.         layout.addComponent(createInfoRow("Party Loyalty:", String.format(Locale.ENGLISH,"%.1f%%", ballotSummary != null ? ballotSummary.getLoyaltyRate() : 0.0),
  149.                 VaadinIcons.GROUP, "Party line adherence"));
  150.         layout.addComponent(createInfoRow("Independence Rate:", String.format(Locale.ENGLISH,"%.1f%%", ballotSummary != null ? ballotSummary.getRebelRate() : 0.0),
  151.                 VaadinIcons.RANDOM, "Votes against party line"));
  152.         layout.addComponent(createInfoRow("Cross-Party Collaboration:",
  153.                 String.format(Locale.ENGLISH,"%.1f%%", politician.getCollaborationPercentage()), VaadinIcons.CONNECT,
  154.                 "Inter-party cooperation"));
  155.         layout.addComponent(createInfoRow("Multi-Party Motions:", String.valueOf(politician.getMultiPartyMotions()),
  156.                 VaadinIcons.USERS, "Cross-party legislative initiatives"));
  157.     }



  158.     /**
  159.      * Adds the parliamentary performance metrics.
  160.      *
  161.      * @param layout the layout
  162.      * @param politician the politician
  163.      * @param ballotSummary the ballot summary
  164.      */
  165.     public final void addParliamentaryPerformanceMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician,
  166.             final ViewRiksdagenPoliticianBallotSummary ballotSummary) {

  167.         layout.addComponent(
  168.                 createInfoRow("Attendance Rate:",
  169.                         String.format(Locale.ENGLISH, "%.1f%%", 100 - (ballotSummary != null ? ballotSummary.getAbsenceRate() : 0.0)),
  170.                         VaadinIcons.USER_CHECK, "Session attendance rate"));
  171.         layout.addComponent(createInfoRow("Voting Success:",
  172.                 String.format(Locale.ENGLISH, "%.1f%%", ballotSummary != null ? ballotSummary.getSuccessRate() : 0.0),
  173.                 VaadinIcons.TROPHY, "Votes on winning side"));
  174.         layout.addComponent(createInfoRow("Activity Level:", politician.getDocActivityLevel(), VaadinIcons.CHART_LINE,
  175.                 "Overall engagement level"));
  176.         layout.addComponent(createInfoRow("Analysis Comment:",
  177.                 String.valueOf(ballotSummary != null ? ballotSummary.getAnalysisComment() : 0), VaadinIcons.USER_CARD,
  178.                 "Analysis Comment"));
  179.     }

  180.     /**
  181.      * Adds the ministry role summary.
  182.      *
  183.      * @param cardLayout the card layout
  184.      * @param govMember the gov member
  185.      * @param governmentBodyByMinistry the government body by ministry
  186.      * @param reportByMinistry the report by ministry
  187.      */
  188.     public final void addMinistryRoleSummary(final VerticalLayout cardLayout,
  189.             final ViewRiksdagenGovermentRoleMember govMember,
  190.             final Map<String, List<GovernmentBodyAnnualSummary>> governmentBodyByMinistry,
  191.             final Map<String, List<GovernmentBodyAnnualOutcomeSummary>> reportByMinistry) {

  192.         cardLayout.addComponent(pageLinkFactory.addMinistryPageLink(govMember.getDetail()));

  193.         final List<GovernmentBodyAnnualSummary> ministryBodies = governmentBodyByMinistry.get(govMember.getDetail());
  194.         if (ministryBodies == null || ministryBodies.isEmpty()) {
  195.             return;
  196.         }

  197.         final int totalHeadCount = ministryBodies.stream().mapToInt(GovernmentBodyAnnualSummary::getAnnualWorkHeadCount)
  198.                 .sum();
  199.         final int bodyCount = ministryBodies.size();

  200.         cardLayout.addComponent(createMetricRow(VaadinIcons.GROUP,
  201.                 pageLinkFactory.addMinistryGovermentBodiesPageLink(govMember.getDetail()),
  202.                 "Number of government bodies", String.valueOf(bodyCount)));

  203.         cardLayout.addComponent(createMetricRow(VaadinIcons.USER,
  204.                 pageLinkFactory.addMinistryGovermentBodiesHeadcountPageLink(govMember.getDetail()),
  205.                 "Total headcount of government bodies", String.valueOf(totalHeadCount)));

  206.         final List<GovernmentBodyAnnualOutcomeSummary> outcomeSummaries = reportByMinistry.get(govMember.getDetail());
  207.         double currentYearIncome = 0;
  208.         double currentYearSpending = 0;
  209.         final int CURRENT_YEAR = 2024;
  210.         final String INKOMSTTITELGRUPPSNAMN = "Inkomsttitelgruppsnamn";
  211.         final String EXPENDITURE_GROUP_NAME = "UtgiftsomrÃ¥desnamn";

  212.         if (outcomeSummaries != null) {
  213.             final Map<Integer, Double> annualIncome = outcomeSummaries.stream()
  214.                     .filter(t -> t.getDescriptionFields().get(INKOMSTTITELGRUPPSNAMN) != null)
  215.                     .collect(Collectors.groupingBy(GovernmentBodyAnnualOutcomeSummary::getYear,
  216.                             Collectors.summingDouble(GovernmentBodyAnnualOutcomeSummary::getYearTotal)));

  217.             final Map<Integer, Double> annualSpending = outcomeSummaries.stream()
  218.                     .filter(t -> t.getDescriptionFields().get(EXPENDITURE_GROUP_NAME) != null)
  219.                     .collect(Collectors.groupingBy(GovernmentBodyAnnualOutcomeSummary::getYear,
  220.                             Collectors.summingDouble(GovernmentBodyAnnualOutcomeSummary::getYearTotal)));

  221.             if (annualIncome.get(CURRENT_YEAR) != null) {
  222.                 currentYearIncome = annualIncome.get(CURRENT_YEAR) / 1000;
  223.             }

  224.             if (annualSpending.get(CURRENT_YEAR) != null) {
  225.                 currentYearSpending = annualSpending.get(CURRENT_YEAR) / 1000;
  226.             }
  227.         }

  228.         final String incomeStr = String.format(Locale.ENGLISH, "%.2f B SEK", currentYearIncome);
  229.         cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_UP,
  230.                 pageLinkFactory.addMinistryGovermentBodiesIncomePageLink(govMember.getDetail()),
  231.                 "Yearly Income (B SEK)", incomeStr));

  232.         final String spendingStr = String.format(Locale.ENGLISH, "%.2f B SEK", currentYearSpending);
  233.         cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_DOWN,
  234.                 pageLinkFactory.addMinistrGovermentBodiesSpendingPageLink(govMember.getDetail()),
  235.                 "Yearly Spending (B SEK)", spendingStr));
  236.     }

  237.     /**
  238.      * Builds the top string.
  239.      *
  240.      * @param <T> the generic type
  241.      * @param items the items
  242.      * @param nameFunction the name function
  243.      * @param weightFunction the weight function
  244.      * @return the string
  245.      */
  246.     private <T> String buildTopString(final List<T> items,
  247.                                       final java.util.function.Function<T, String> nameFunction,
  248.                                       final java.util.function.Function<T, Long> weightFunction) {
  249.         return items.stream()
  250.             .filter(i -> nameFunction.apply(i) != null && !"Other".equals(nameFunction.apply(i)))
  251.             .sorted((o1, o2) -> weightFunction.apply(o2).compareTo(weightFunction.apply(o1)))
  252.             .limit(3)
  253.             .map(nameFunction)
  254.             .collect(Collectors.joining(", "));
  255.     }

  256. }