DecisionDataFactoryImpl.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.common.dataseriesfactory.impl;
- import java.util.List;
- import java.util.Locale;
- import java.util.Objects;
- import java.util.regex.Pattern;
- import java.util.stream.Collectors;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
- import com.hack23.cia.model.external.riksdagen.dokumentstatus.impl.DocumentProposalData;
- import com.hack23.cia.model.external.riksdagen.dokumentstatus.impl.DocumentStatusContainer;
- import com.hack23.cia.service.api.ApplicationManager;
- import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.DecisionDataFactory;
- import com.hack23.cia.web.impl.ui.application.views.common.dataseriesfactory.api.ProposalCommitteeeSummary;
- /**
- * Implementation of DecisionDataFactory for processing parliamentary committee decisions.
- * Handles document status data and creates summaries of committee proposals.
- */
- @Service
- @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
- public final class DecisionDataFactoryImpl implements DecisionDataFactory {
- /** Document type constants. */
- private static final String PROPOSITION = "Proposition";
- /** The Constant MOTION. */
- private static final String MOTION = "Motion";
- /** The Constant PROP_TYPE. */
- private static final String PROP_TYPE = "prop";
- /** Chamber text length constraints for valid proposals. */
- private static final int CHAMBER_MIN_LENGTH = "avslag".length(); // 6
- /** The Constant CHAMBER_MAX_LENGTH. */
- private static final int CHAMBER_MAX_LENGTH = "återförvisning till utskottet".length(); // 29
- /**
- * Pattern for standardizing committee decision text.
- * Removes or replaces parliamentary specific tokens:
- * - (UTSKOTTET)
- * - Parentheses
- * - Various committee spelling variants
- */
- private static final Pattern COMMITTEE_TEXT_CLEANUP_PATTERN = Pattern.compile(
- "(\\(UTSKOTTET\\))|(\\()|(\\))|(UTBSKOTTET)|(UBTSKOTTET)|(UTKOTTET)",
- Pattern.CASE_INSENSITIVE
- );
- /** The application manager. */
- @Autowired
- private ApplicationManager applicationManager;
- /**
- * Creates committee summaries for a specific processing location.
- *
- * @param processedIn the location where proposals were processed
- * @return List of committee summaries
- * @throws IllegalArgumentException if processedIn is blank
- */
- @Override
- public List<ProposalCommitteeeSummary> createCommitteeSummary(final String processedIn) {
- validateProcessedIn(processedIn);
- return applicationManager.getDataContainer(DocumentStatusContainer.class)
- .getAll()
- .parallelStream()
- .filter(doc -> isValidDocument(doc, processedIn))
- .map(doc -> createProposalSummary(doc))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
- /**
- * Validates the processedIn parameter.
- *
- * @param processedIn location to validate
- * @throws IllegalArgumentException if validation fails
- */
- private void validateProcessedIn(final String processedIn) {
- if (StringUtils.isBlank(processedIn)) {
- throw new IllegalArgumentException("ProcessedIn parameter cannot be blank");
- }
- }
- /**
- * Validates if a document meets the criteria for processing.
- *
- * @param document the document to validate
- * @param processedIn the required processing location
- * @return true if document is valid for processing
- */
- private boolean isValidDocument(final DocumentStatusContainer document, final String processedIn) {
- if (document == null || document.getDocumentProposal() == null ||
- document.getDocumentProposal().getProposal() == null) {
- return false;
- }
- final DocumentProposalData proposal = document.getDocumentProposal().getProposal();
- return isValidProposal(proposal, processedIn);
- }
- /**
- * Validates proposal data for processing requirements.
- *
- * @param proposal the proposal to validate
- * @param processedIn the required processing location
- * @return true if proposal meets requirements
- */
- private boolean isValidProposal(final DocumentProposalData proposal, final String processedIn) {
- if (proposal == null || StringUtils.isBlank(proposal.getChamber()) ||
- StringUtils.isBlank(proposal.getProcessedIn()) ||
- StringUtils.isBlank(proposal.getCommittee())) {
- return false;
- }
- final int chamberLength = proposal.getChamber().length();
- return chamberLength >= CHAMBER_MIN_LENGTH &&
- chamberLength <= CHAMBER_MAX_LENGTH &&
- proposal.getProcessedIn().contains(processedIn);
- }
- /**
- * Creates a ProposalCommitteeeSummary from a valid document.
- *
- * @param document source document
- * @return ProposalCommitteeeSummary or null if invalid
- */
- private ProposalCommitteeeSummary createProposalSummary(final DocumentStatusContainer document) {
- try {
- final DocumentProposalData proposal = document.getDocumentProposal().getProposal();
- return new ProposalCommitteeeSummary(
- extractCommitteeShortName(proposal),
- determineDocumentType(document),
- standardizeDecisionText(proposal.getChamber()),
- document.getDocument().getHangarId(),
- proposal.getWording(),
- proposal.getWording2(),
- proposal.getDecisionType()
- );
- } catch (final Exception e) {
- // Log error if needed
- return null;
- }
- }
- /**
- * Standardizes the decision text by removing variations and normalizing format.
- *
- * @param chamber the original chamber text
- * @return standardized decision text
- */
- private static String standardizeDecisionText(final String chamber) {
- if (StringUtils.isBlank(chamber)) {
- return "";
- }
- String standardized = chamber.toUpperCase(Locale.ENGLISH);
- standardized = COMMITTEE_TEXT_CLEANUP_PATTERN.matcher(standardized).replaceAll("");
- // Normalize committee spelling
- if (standardized.contains("UTKOTTET")) {
- standardized = standardized.replace("UTKOTTET", "UTSKOTTET");
- }
- return standardized.trim();
- }
- /**
- * Extracts the standardized committee short name.
- *
- * @param proposal source proposal
- * @return committee short name
- */
- private static String extractCommitteeShortName(final DocumentProposalData proposal) {
- final String processedIn = proposal.getProcessedIn();
- if (StringUtils.isBlank(processedIn)) {
- return "";
- }
- final String shortName = processedIn
- .replaceAll("\\d", "")
- .replace("/:", "")
- .toUpperCase(Locale.ENGLISH);
- final int commaIndex = shortName.indexOf(',');
- return (commaIndex >= 0) ? shortName.substring(0, commaIndex) : shortName;
- }
- /**
- * Determines the human-readable document type.
- *
- * @param document source document
- * @return document type string
- */
- private static String determineDocumentType(final DocumentStatusContainer document) {
- if (PROP_TYPE.equalsIgnoreCase(document.getDocument().getDocumentType())) {
- return PROPOSITION;
- }
- final String subType = document.getDocument().getSubType();
- return (subType != null && subType.length() > MOTION.length())
- ? subType
- : MOTION;
- }
- }