PoliticianLeaderboardUtil.java
package com.hack23.cia.web.impl.ui.application.views.common.pagemode;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hack23.cia.model.internal.application.data.ministry.impl.ViewRiksdagenGovermentRoleMember;
import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPolitician;
import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPoliticianBallotSummary;
import com.hack23.cia.model.internal.application.data.politician.impl.ViewRiksdagenPoliticianExperienceSummary;
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.views.common.pagelinks.api.PageLinkFactory;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.ui.VerticalLayout;
/**
* The Class PoliticianLeaderboardUtil.
*/
@Service
public class PoliticianLeaderboardUtil extends CardInfoRowUtil {
/** The page link factory. */
@Autowired
public PageLinkFactory pageLinkFactory;
/**
* Instantiates a new politician leaderboard util.
*/
public PoliticianLeaderboardUtil() {
}
/**
* Adds the political analysis comment.
*
* @param layout the layout
* @param experienceSummary the experience summary
*/
public final void addPoliticalAnalysisComment(final VerticalLayout layout,
final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
// Split the analysis points and create a bullet list
final String[] analysisPoints = experienceSummary.getPoliticalAnalysisComment().split("\\s*\\|\\|\\s*");
final StringBuilder analys=new StringBuilder();
for (final String point : analysisPoints) {
if (StringUtils.isNotBlank(point)) {
analys.append(" • ").append(point);
}
}
// Political Analysis Comment
if (StringUtils.isNotBlank(experienceSummary.getPoliticalAnalysisComment())) {
layout.addComponent(createInfoRow("Analysis:",
analys.toString(),
VaadinIcons.COMMENT,
"Political career analysis"));
}
}
/**
* Adds the knowledge areas.
*
* @param layout the layout
* @param experienceSummary the experience summary
*/
public final void addKnowledgeAreas(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
if (experienceSummary.getKnowledgeAreas() != null && !experienceSummary.getKnowledgeAreas().isEmpty()) {
final String topAreas = buildTopString(
experienceSummary.getKnowledgeAreas(),
ka -> ka.getArea(),
ka -> ka.getWeightedExp()
);
if (!topAreas.isEmpty()) {
layout.addComponent(createInfoRow("Top Knowledge Areas:", topAreas, VaadinIcons.BOOK, "Key expertise"));
}
}
}
/**
* Adds the top roles.
*
* @param layout the layout
* @param experienceSummary the experience summary
*/
public final void addTopRoles(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
if (experienceSummary.getRoles() != null && !experienceSummary.getRoles().isEmpty()) {
final String topRoles = buildTopString(
experienceSummary.getRoles(),
role -> role.getRole(),
role -> role.getWeightedExp()
);
if (!topRoles.isEmpty()) {
layout.addComponent(createInfoRow("Top Roles:", topRoles, VaadinIcons.STAR, "Most significant roles")); // Changed from CROWN to STAR
}
}
}
/**
* Adds the experience metrics.
*
* @param layout the layout
* @param experienceSummary the experience summary
*/
public final void addExperienceMetrics(final VerticalLayout layout, final ViewRiksdagenPoliticianExperienceSummary experienceSummary) {
if (experienceSummary != null) {
// Career Overview
layout.addComponent(createInfoRow("Career Phase:",
experienceSummary.getCareerPhase().toString().replace("_", " "),
VaadinIcons.CALENDAR_CLOCK,
"Current career stage"));
// Experience Level
layout.addComponent(createInfoRow("Experience Level:",
experienceSummary.getExperienceLevel().toString().replace("_", " "),
VaadinIcons.CHART_TIMELINE,
"Overall political experience classification"));
// Leadership Profile
layout.addComponent(createInfoRow("Leadership Role:",
experienceSummary.getLeadershipProfile().toString().replace("_", " "),
VaadinIcons.USER_STAR,
"Leadership experience level"));
// Specialization
layout.addComponent(createInfoRow("Expertise:",
experienceSummary.getSpecializationLevel().toString().replace("_", " "),
VaadinIcons.SPECIALIST,
"Area of specialization"));
}
}
/**
* Adds the legislative metrics.
*
* @param layout the layout
* @param politician the politician
*/
public final void addLegislativeMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician) {
layout.addComponent(createInfoRow("Documents/Year:", String.format(Locale.ENGLISH,"%.1f", politician.getAverageDocsPerYear()),
VaadinIcons.FILE_TEXT, "Average documents per year"));
layout.addComponent(createInfoRow("Individual Motions:", String.valueOf(politician.getIndividualMotions()),
VaadinIcons.USER, "Personal motions submitted"));
layout.addComponent(createInfoRow("Party Motions:", String.valueOf(politician.getPartyMotions()),
VaadinIcons.GROUP, "Party-based motions"));
layout.addComponent(createInfoRow("Committee Motions:", String.valueOf(politician.getCommitteeMotions()),
VaadinIcons.GROUP, "Committee-based motions"));
layout.addComponent(createInfoRow("Document Impact:", politician.getDocActivityProfile(), VaadinIcons.CHART_3D,
"Legislative influence assessment"));
}
/**
* Adds the party alignment metrics.
*
* @param layout the layout
* @param politician the politician
* @param ballotSummary the ballot summary
*/
public final void addPartyAlignmentMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician,
final ViewRiksdagenPoliticianBallotSummary ballotSummary) {
layout.addComponent(createInfoRow("Party Loyalty:", String.format(Locale.ENGLISH,"%.1f%%", ballotSummary != null ? ballotSummary.getLoyaltyRate() : 0.0),
VaadinIcons.GROUP, "Party line adherence"));
layout.addComponent(createInfoRow("Independence Rate:", String.format(Locale.ENGLISH,"%.1f%%", ballotSummary != null ? ballotSummary.getRebelRate() : 0.0),
VaadinIcons.RANDOM, "Votes against party line"));
layout.addComponent(createInfoRow("Cross-Party Collaboration:",
String.format(Locale.ENGLISH,"%.1f%%", politician.getCollaborationPercentage()), VaadinIcons.CONNECT,
"Inter-party cooperation"));
layout.addComponent(createInfoRow("Multi-Party Motions:", String.valueOf(politician.getMultiPartyMotions()),
VaadinIcons.USERS, "Cross-party legislative initiatives"));
}
/**
* Adds the parliamentary performance metrics.
*
* @param layout the layout
* @param politician the politician
* @param ballotSummary the ballot summary
*/
public final void addParliamentaryPerformanceMetrics(final VerticalLayout layout, final ViewRiksdagenPolitician politician,
final ViewRiksdagenPoliticianBallotSummary ballotSummary) {
layout.addComponent(
createInfoRow("Attendance Rate:",
String.format(Locale.ENGLISH, "%.1f%%", 100 - (ballotSummary != null ? ballotSummary.getAbsenceRate() : 0.0)),
VaadinIcons.USER_CHECK, "Session attendance rate"));
layout.addComponent(createInfoRow("Voting Success:",
String.format(Locale.ENGLISH, "%.1f%%", ballotSummary != null ? ballotSummary.getSuccessRate() : 0.0),
VaadinIcons.TROPHY, "Votes on winning side"));
layout.addComponent(createInfoRow("Activity Level:", politician.getDocActivityLevel(), VaadinIcons.CHART_LINE,
"Overall engagement level"));
layout.addComponent(createInfoRow("Analysis Comment:",
String.valueOf(ballotSummary != null ? ballotSummary.getAnalysisComment() : 0), VaadinIcons.USER_CARD,
"Analysis Comment"));
}
/**
* Adds the ministry role summary.
*
* @param cardLayout the card layout
* @param govMember the gov member
* @param governmentBodyByMinistry the government body by ministry
* @param reportByMinistry the report by ministry
*/
public final void addMinistryRoleSummary(final VerticalLayout cardLayout,
final ViewRiksdagenGovermentRoleMember govMember,
final Map<String, List<GovernmentBodyAnnualSummary>> governmentBodyByMinistry,
final Map<String, List<GovernmentBodyAnnualOutcomeSummary>> reportByMinistry) {
cardLayout.addComponent(pageLinkFactory.addMinistryPageLink(govMember.getDetail()));
final List<GovernmentBodyAnnualSummary> ministryBodies = governmentBodyByMinistry.get(govMember.getDetail());
if (ministryBodies == null || ministryBodies.isEmpty()) {
return;
}
final int totalHeadCount = ministryBodies.stream().mapToInt(GovernmentBodyAnnualSummary::getAnnualWorkHeadCount)
.sum();
final int bodyCount = ministryBodies.size();
cardLayout.addComponent(createMetricRow(VaadinIcons.GROUP,
pageLinkFactory.addMinistryGovermentBodiesPageLink(govMember.getDetail()),
"Number of government bodies", String.valueOf(bodyCount)));
cardLayout.addComponent(createMetricRow(VaadinIcons.USER,
pageLinkFactory.addMinistryGovermentBodiesHeadcountPageLink(govMember.getDetail()),
"Total headcount of government bodies", String.valueOf(totalHeadCount)));
final List<GovernmentBodyAnnualOutcomeSummary> 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<Integer, Double> annualIncome = outcomeSummaries.stream()
.filter(t -> t.getDescriptionFields().get(INKOMSTTITELGRUPPSNAMN) != null)
.collect(Collectors.groupingBy(GovernmentBodyAnnualOutcomeSummary::getYear,
Collectors.summingDouble(GovernmentBodyAnnualOutcomeSummary::getYearTotal)));
final Map<Integer, Double> 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;
}
if (annualSpending.get(CURRENT_YEAR) != null) {
currentYearSpending = annualSpending.get(CURRENT_YEAR) / 1000;
}
}
final String incomeStr = String.format(Locale.ENGLISH, "%.2f B SEK", currentYearIncome);
cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_UP,
pageLinkFactory.addMinistryGovermentBodiesIncomePageLink(govMember.getDetail()),
"Yearly Income (B SEK)", incomeStr));
final String spendingStr = String.format(Locale.ENGLISH, "%.2f B SEK", currentYearSpending);
cardLayout.addComponent(createMetricRow(VaadinIcons.ARROW_DOWN,
pageLinkFactory.addMinistrGovermentBodiesSpendingPageLink(govMember.getDetail()),
"Yearly Spending (B SEK)", spendingStr));
}
/**
* Builds the top string.
*
* @param <T> the generic type
* @param items the items
* @param nameFunction the name function
* @param weightFunction the weight function
* @return the string
*/
private <T> String buildTopString(final List<T> items,
final java.util.function.Function<T, String> nameFunction,
final java.util.function.Function<T, Long> weightFunction) {
return items.stream()
.filter(i -> nameFunction.apply(i) != null && !"Other".equals(nameFunction.apply(i)))
.sorted((o1, o2) -> weightFunction.apply(o2).compareTo(weightFunction.apply(o1)))
.limit(3)
.map(nameFunction)
.collect(Collectors.joining(", "));
}
}