Reorganized model structure

This commit is contained in:
2023-07-09 15:05:34 +02:00
parent ed76aca004
commit 3e178885b5
30 changed files with 424 additions and 95 deletions
@@ -1,9 +0,0 @@
package de.twomartens.wahlrecht.model;
public record Constituency(String name, int numberOfSeats) {
@Override
public String toString() {
return name;
}
}
@@ -1,21 +0,0 @@
package de.twomartens.wahlrecht.model;
import java.util.Map;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@RequiredArgsConstructor
@Setter
@Getter
public class Party {
private final String name;
private Nomination overallNomination;
private Map<Constituency, Nomination> constituencyNominations;
private boolean passesVotingThreshold;
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,41 @@
package de.twomartens.wahlrecht.model.db;
import java.util.Date;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Candidate {
@Id
ObjectId id;
@CreatedDate
Date created;
@LastModifiedDate
Date lastModified;
@Indexed
String name;
String profession;
}
@@ -0,0 +1,43 @@
package de.twomartens.wahlrecht.model.db;
import java.time.Instant;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@CompoundIndex(def = "{'number': 1, 'electionName': 1}")
public class Constituency {
@Id
ObjectId id;
@CreatedDate
Instant created;
@LastModifiedDate
Instant lastModified;
int number;
String electionName;
String name;
int numberOfSeats;
}
@@ -0,0 +1,47 @@
package de.twomartens.wahlrecht.model.db;
import de.twomartens.wahlrecht.model.dto.v1.VotingThreshold;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Date;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Election {
@Id
ObjectId id;
@CreatedDate
Date created;
@LastModifiedDate
Date lastModified;
@Indexed(unique = true)
String name;
LocalDate day;
VotingThreshold votingThreshold;
int totalNumberOfSeats;
Collection<Constituency> constituencies;
}
@@ -0,0 +1,49 @@
package de.twomartens.wahlrecht.model.db;
import java.util.Collection;
import java.util.Date;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@CompoundIndex(def = "{'name': 1, 'partyAbbreviation': 1, 'electionName': 1}")
public class Nomination {
@Id
ObjectId id;
@CreatedDate
Date created;
@LastModifiedDate
Date lastModified;
String name;
String partyAbbreviation;
String electionName;
Collection<Candidate> candidates;
boolean supportVotesOnNomination;
public boolean supportsVotesOnNomination() {
return supportVotesOnNomination;
}
}
@@ -0,0 +1,46 @@
package de.twomartens.wahlrecht.model.db;
import java.util.Date;
import java.util.Map;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Getter
@Setter
@Builder
@EqualsAndHashCode
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@CompoundIndex(def = "{'abbreviation': 1, 'electionName': 1}")
public class PartyInElection {
@Id
ObjectId id;
@CreatedDate
Date created;
@LastModifiedDate
Date lastModified;
String abbreviation;
String electionName;
String name;
Nomination overallNomination;
Map<Integer, Nomination> constituencyNominations;
}
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.dto.v1;
public record Candidate(String name, String profession) {
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.model.dto.v1;
public record Constituency(String electionName, int number, String name, int numberOfSeats) {
@Override
public String toString() {
return name;
}
}
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.dto.v1;
public enum Elected {
NOT_ELECTED,
@@ -1,6 +1,7 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.dto.v1;
public record ElectedCandidate(Candidate candidate, Elected elected) {
public String name() {
return candidate.name();
}
@@ -0,0 +1,14 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import lombok.Builder;
@Builder
public record ElectedResult(Map<VotingResult, Collection<ElectedCandidate>> electedCandidates,
LinkedList<Double> usedElectionNumbers) {
}
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.dto.v1;
import java.time.LocalDate;
import java.util.Collection;
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.List;
public record Nomination(String name, String partyAbbreviation, String electionName,
boolean supportVotesOnNomination,
List<Candidate> candidates) {
}
@@ -0,0 +1,12 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.Map;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
public record PartyInElection(String electionName, String abbreviation, String name,
Nomination overallNomination,
Map<Integer, Nomination> constituencyNominations) {
}
@@ -0,0 +1,13 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import lombok.Builder;
@Builder
public record SeatResult(Map<VotingResult, Integer> seatsPerResult,
LinkedList<Double> usedElectionNumbers) {
}
@@ -0,0 +1,10 @@
package de.twomartens.wahlrecht.model.dto.v1;
import java.util.Map;
import lombok.Builder;
import lombok.Getter;
public record VotingResult(int votesOnNomination, int votesThroughHealing, Integer totalVotes,
Map<Integer, Integer> votesPerPosition, String nominationName) {
}
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.dto.v1;
import lombok.AccessLevel;
import lombok.Getter;
@@ -0,0 +1,5 @@
package de.twomartens.wahlrecht.model.internal;
public record Candidate(String name, String profession) {
}
@@ -0,0 +1,9 @@
package de.twomartens.wahlrecht.model.internal;
public record Constituency(String electionName, int number, String name, int numberOfSeats) {
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,8 @@
package de.twomartens.wahlrecht.model.internal;
public enum Elected {
NOT_ELECTED,
CONSTITUENCY,
OVERALL_NOMINATION_ORDER,
OVERALL_VOTE_ORDER
}
@@ -0,0 +1,13 @@
package de.twomartens.wahlrecht.model.internal;
public record ElectedCandidate(Candidate candidate, Elected elected) {
public String name() {
return candidate.name();
}
public String profession() {
return candidate.profession();
}
}
@@ -1,7 +1,7 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.internal;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
@@ -9,7 +9,7 @@ import lombok.Builder;
@Builder
public record ElectedResult(Map<VotingResult, Collection<ElectedCandidate>> electedCandidates,
Deque<Double> usedElectionNumbers) {
LinkedList<Double> usedElectionNumbers) {
public Map<Nomination, Collection<ElectedCandidate>> electedCandidatesByNomination() {
return electedCandidates.entrySet().stream()
@@ -0,0 +1,10 @@
package de.twomartens.wahlrecht.model.internal;
import java.time.LocalDate;
import java.util.Collection;
public record Election(String name, LocalDate day, VotingThreshold votingThreshold,
int totalNumberOfSeats,
Collection<Constituency> constituencies) {
}
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.internal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -13,12 +13,15 @@ import lombok.ToString.Include;
@RequiredArgsConstructor
@ToString(onlyExplicitlyIncluded = true)
public class Nomination {
private final List<Candidate> candidates = new ArrayList<>();
private final Map<Candidate, Integer> candidateToPosition = new HashMap<>();
private final Map<Integer, Candidate> positionToCandidate = new HashMap<>();
private final String electionName;
private final String partyAbbreviation;
@Include
private final String name;
private final boolean supportVotesOnNomination;
private final List<Candidate> candidates = new ArrayList<>();
private final Map<Candidate, Integer> candidateToPosition = new HashMap<>();
private final Map<Integer, Candidate> positionToCandidate = new HashMap<>();
private void addCandidate(Candidate candidate) {
candidates.add(candidate);
@@ -1,6 +1,6 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.internal;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
@@ -8,7 +8,7 @@ import lombok.Builder;
@Builder
public record SeatResult(Map<VotingResult, Integer> seatsPerResult,
Deque<Double> usedElectionNumbers) {
LinkedList<Double> usedElectionNumbers) {
public Map<Nomination, Integer> seatsPerNomination() {
return seatsPerResult.entrySet().stream()
@@ -1,4 +1,4 @@
package de.twomartens.wahlrecht.model;
package de.twomartens.wahlrecht.model.internal;
import java.util.Map;
import lombok.Builder;
@@ -0,0 +1,15 @@
package de.twomartens.wahlrecht.model.internal;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum VotingThreshold {
NONE(0.00),
THREE(0.03),
FIVE(0.05);
private final double multiplier;
}
@@ -1,21 +1,20 @@
package de.twomartens.wahlrecht.service;
import de.twomartens.wahlrecht.model.Candidate;
import de.twomartens.wahlrecht.model.Constituency;
import de.twomartens.wahlrecht.model.Elected;
import de.twomartens.wahlrecht.model.ElectedCandidate;
import de.twomartens.wahlrecht.model.ElectedResult;
import de.twomartens.wahlrecht.model.Election;
import de.twomartens.wahlrecht.model.Nomination;
import de.twomartens.wahlrecht.model.SeatResult;
import de.twomartens.wahlrecht.model.VotingResult;
import java.util.ArrayDeque;
import de.twomartens.wahlrecht.model.internal.Candidate;
import de.twomartens.wahlrecht.model.internal.Constituency;
import de.twomartens.wahlrecht.model.internal.Elected;
import de.twomartens.wahlrecht.model.internal.ElectedCandidate;
import de.twomartens.wahlrecht.model.internal.ElectedResult;
import de.twomartens.wahlrecht.model.internal.Election;
import de.twomartens.wahlrecht.model.internal.Nomination;
import de.twomartens.wahlrecht.model.internal.SeatResult;
import de.twomartens.wahlrecht.model.internal.VotingResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -26,7 +25,8 @@ import org.springframework.stereotype.Service;
@Service
public class CalculationService {
private final Deque<Double> electionNumberHistory = new ArrayDeque<>();
private final LinkedList<Double> electionNumberHistory = new LinkedList<>();
public ElectedResult calculateConstituency(@NonNull Constituency constituency,
@NonNull Collection<VotingResult> votingResults) {
@@ -76,7 +76,8 @@ public class CalculationService {
.build();
}
public ElectedResult calculateElectedOverallCandidates(Map<VotingResult, Integer> seatsPerNomination,
public ElectedResult calculateElectedOverallCandidates(
Map<VotingResult, Integer> seatsPerNomination,
Map<VotingResult, Collection<ElectedCandidate>> electedCandidates) {
electionNumberHistory.clear();
@@ -161,7 +162,8 @@ public class CalculationService {
return elected;
}
private static int calculateSeatsByNominationOrder(@NonNull VotingResult votingResult, int numberOfSeats) {
private static int calculateSeatsByNominationOrder(@NonNull VotingResult votingResult,
int numberOfSeats) {
int votesOnNomination = votingResult.getVotesOnNomination();
int totalVotes = votingResult.getTotalVotesWithoutHealing();
return (int) Math.round((numberOfSeats * votesOnNomination) / (double) totalVotes);
@@ -199,7 +201,6 @@ public class CalculationService {
}
} while (assignedSeats != numberOfSeats);
return assignedSeatsPerVotingResult;
}
@@ -232,7 +233,6 @@ public class CalculationService {
}
private double calculateAssignedSeatNumber(double electionNumber, int totalVotesOfNomination) {
return totalVotesOfNomination / electionNumber;
}