This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit bd8c9750e87ffa1306d614381d29b8945c582608 Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Wed Jun 28 15:23:26 2017 +0200 Afficher le détail de resultat pour les systèmes de vote complex (ref #35) --- pollen-services/pom.xml | 38 ++++++++++++ .../services/bean/VoteCountingResultBean.java | 12 ++++ .../Coombs/CoombsDetailResultBean.java | 38 ++++++++++++ .../bean/voteCounting/Coombs/CoombsRoundBean.java | 62 ++++++++++++++++++++ .../voteCounting/Coombs/CoombsRoundChoiceBean.java | 61 ++++++++++++++++++++ .../InstantRunoffDetailResultBean.java | 38 ++++++++++++ .../InstantRunoff/InstantRunoffRoundBean.java | 62 ++++++++++++++++++++ .../InstantRunoffRoundChoiceBean.java | 49 ++++++++++++++++ .../voteCounting/VoteCountingDetailResultBean.java | 53 +++++++++++++++++ .../voteCounting/borda/BordaChoiceRankBean.java | 58 +++++++++++++++++++ .../voteCounting/borda/BordaDetailResultBean.java | 38 ++++++++++++ .../condorcet/CondorcetBattleBean.java | 64 +++++++++++++++++++++ .../condorcet/CondorcetDetailResultBean.java | 38 ++++++++++++ pollen-ui-riot-js/src/main/web/i18n.json | 26 +++++++++ .../src/main/web/js/VoterListService.js | 4 +- .../src/main/web/tag/poll/Poll.tag.html | 4 ++ .../src/main/web/tag/poll/Results.tag.html | 11 +++- .../src/main/web/tag/poll/Votes.tag.html | 4 -- .../votecounting/AbstractVoteCountingStrategy.java | 5 +- .../model/VoteCountingDetailResult.java | 9 +++ .../votecounting/model/VoteCountingResult.java | 13 ++++- .../pollen/votecounting/BordaChoiceRank.java | 53 +++++++++++++++++ .../pollen/votecounting/BordaDetailResult.java | 27 +++++++++ .../votecounting/BordaVoteCountingStrategy.java | 25 +++++++- .../pollen/votecounting/CondorcetBattle.java | 53 +++++++++++++++++ .../pollen/votecounting/CondorcetDetailResult.java | 27 +++++++++ .../CondorcetVoteCountingStrategy.java | 30 ++++++++-- .../pollen/votecounting/CoombsDetailResult.java | 27 +++++++++ .../chorem/pollen/votecounting/CoombsRound.java | 40 +++++++++++++ .../pollen/votecounting/CoombsRoundChoice.java | 64 +++++++++++++++++++++ .../votecounting/CoombsVoteCountingStrategy.java | 67 +++++++++++++++++----- .../votecounting/InstantRunoffDetailResult.java | 27 +++++++++ .../pollen/votecounting/InstantRunoffRound.java | 40 +++++++++++++ .../votecounting/InstantRunoffRoundChoice.java | 43 ++++++++++++++ .../InstantRunoffVoteCountingStrategy.java | 52 +++++++++++++---- .../votecounting/NormalVoteCountingStrategy.java | 2 +- .../votecounting/NumberVoteCountingStrategy.java | 2 +- .../PercentageVoteCountingStrategy.java | 2 +- 38 files changed, 1223 insertions(+), 45 deletions(-) diff --git a/pollen-services/pom.xml b/pollen-services/pom.xml index 5c78a99f..755343ed 100644 --- a/pollen-services/pom.xml +++ b/pollen-services/pom.xml @@ -43,6 +43,44 @@ <artifactId>pollen-votecounting-api</artifactId> <version>${project.version}</version> </dependency> + + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-normal</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-percentage</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-condorcet</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-number</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-borda</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-instant-runoff</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>pollen-votecounting-coombs</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>${project.groupId}</groupId> <artifactId>pollen-votecounting-aggregator</artifactId> diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingResultBean.java index 65a07075..3082a4bf 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingResultBean.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingResultBean.java @@ -21,6 +21,7 @@ package org.chorem.pollen.services.bean; * #L% */ +import org.chorem.pollen.services.bean.voteCounting.VoteCountingDetailResultBean; import org.chorem.pollen.votecounting.model.ChoiceScore; import org.chorem.pollen.votecounting.model.VoteCountingResult; @@ -43,6 +44,8 @@ public class VoteCountingResultBean { */ protected List<ChoiceScoreBean> scores; + protected VoteCountingDetailResultBean detail; + protected int nbVotants; public VoteCountingResultBean() { @@ -60,6 +63,7 @@ public class VoteCountingResultBean { scores.add(choiceScoreBean); } + setDetail(VoteCountingDetailResultBean.toBean(result.getDetailResult())); setNbVotants(result.getNbVotants()); } @@ -79,4 +83,12 @@ public class VoteCountingResultBean { public void setNbVotants(int nbVotants) { this.nbVotants = nbVotants; } + + public VoteCountingDetailResultBean getDetail() { + return detail; + } + + public void setDetail(VoteCountingDetailResultBean detail) { + this.detail = detail; + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsDetailResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsDetailResultBean.java new file mode 100644 index 00000000..3026a5fa --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsDetailResultBean.java @@ -0,0 +1,38 @@ +package org.chorem.pollen.services.bean.voteCounting.Coombs; + +import com.google.common.collect.Lists; +import org.chorem.pollen.services.bean.voteCounting.VoteCountingDetailResultBean; +import org.chorem.pollen.votecounting.CoombsDetailResult; +import org.chorem.pollen.votecounting.CoombsRound; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsDetailResultBean extends VoteCountingDetailResultBean { + + protected List<CoombsRoundBean> rounds; + + public void fromResult(CoombsDetailResult result) { + + for (CoombsRound coombsRound : result.getRounds()) { + + CoombsRoundBean coombsRoundBean = new CoombsRoundBean(); + coombsRoundBean.fromResult(coombsRound); + getRounds().add(coombsRoundBean); + + } + } + + public List<CoombsRoundBean> getRounds() { + if (rounds == null) { + rounds = Lists.newLinkedList(); + } + return rounds; + } + + public void setRounds(List<CoombsRoundBean> rounds) { + this.rounds = rounds; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundBean.java new file mode 100644 index 00000000..403681cc --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundBean.java @@ -0,0 +1,62 @@ +package org.chorem.pollen.services.bean.voteCounting.Coombs; + +import com.google.common.collect.Lists; +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.CoombsRound; +import org.chorem.pollen.votecounting.CoombsRoundChoice; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsRoundBean { + + protected List<CoombsRoundChoiceBean> roundChoices; + + protected List<PollenEntityId<Choice>> choiceIdsExclude; + + public void fromResult(CoombsRound result) { + + for (CoombsRoundChoice coombsRoundChoice : result.getRoundChoices()) { + + CoombsRoundChoiceBean coombsRoundChoiceBean = new CoombsRoundChoiceBean(); + coombsRoundChoiceBean.fromResult(coombsRoundChoice); + getRoundChoices().add(coombsRoundChoiceBean); + + } + + for (String choiceIdExclude : result.getChoiceIdsExclude()) { + + PollenEntityId<Choice> choiceId = PollenEntityId.newId(Choice.class); + choiceId.setEntityId(choiceIdExclude); + getChoiceIdsExclude().add(choiceId); + + } + + } + + + public List<CoombsRoundChoiceBean> getRoundChoices() { + if (roundChoices == null) { + roundChoices = Lists.newLinkedList(); + } + return roundChoices; + } + + public void setRoundChoices(List<CoombsRoundChoiceBean> roundChoices) { + this.roundChoices = roundChoices; + } + + public List<PollenEntityId<Choice>> getChoiceIdsExclude() { + if (choiceIdsExclude == null) { + choiceIdsExclude = Lists.newLinkedList(); + } + return choiceIdsExclude; + } + + public void setChoiceIdsExclude(List<PollenEntityId<Choice>> choiceIdsExclude) { + this.choiceIdsExclude = choiceIdsExclude; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundChoiceBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundChoiceBean.java new file mode 100644 index 00000000..452a3e92 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/Coombs/CoombsRoundChoiceBean.java @@ -0,0 +1,61 @@ +package org.chorem.pollen.services.bean.voteCounting.Coombs; + +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.CoombsRoundChoice; + +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsRoundChoiceBean { + + protected PollenEntityId<Choice> choiceId; + + protected BigDecimal firstScore; + + protected BigDecimal lastScore; + + public CoombsRoundChoiceBean() { + this.choiceId = PollenEntityId.newId(Choice.class); + } + + public void fromResult(CoombsRoundChoice result) { + + setChoiceId(result.getChoiceId()); + setFirstScore(result.getFirstScore()); + setLastScore(result.getLastScore()); + + } + + public PollenEntityId<Choice> getChoiceId() { + return choiceId; + } + + public void setChoiceId(PollenEntityId<Choice> choiceId) { + this.choiceId = choiceId; + } + + public void setChoiceId(String choiceId) { + + this.choiceId.setEntityId(choiceId); + } + + public BigDecimal getFirstScore() { + return firstScore; + } + + public void setFirstScore(BigDecimal firstScore) { + this.firstScore = firstScore; + } + + public BigDecimal getLastScore() { + return lastScore; + } + + public void setLastScore(BigDecimal lastScore) { + this.lastScore = lastScore; + } + +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffDetailResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffDetailResultBean.java new file mode 100644 index 00000000..2971b3f2 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffDetailResultBean.java @@ -0,0 +1,38 @@ +package org.chorem.pollen.services.bean.voteCounting.InstantRunoff; + +import com.google.common.collect.Lists; +import org.chorem.pollen.services.bean.voteCounting.VoteCountingDetailResultBean; +import org.chorem.pollen.votecounting.InstantRunoffDetailResult; +import org.chorem.pollen.votecounting.InstantRunoffRound; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffDetailResultBean extends VoteCountingDetailResultBean { + + protected List<InstantRunoffRoundBean> rounds; + + public void fromResult(InstantRunoffDetailResult result) { + + for (InstantRunoffRound instantRunoffRound : result.getRounds()) { + + InstantRunoffRoundBean instantRunoffRoundBean = new InstantRunoffRoundBean(); + instantRunoffRoundBean.fromResult(instantRunoffRound); + getRounds().add(instantRunoffRoundBean); + + } + } + + public List<InstantRunoffRoundBean> getRounds() { + if (rounds == null) { + rounds = Lists.newLinkedList(); + } + return rounds; + } + + public void setRounds(List<InstantRunoffRoundBean> rounds) { + this.rounds = rounds; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundBean.java new file mode 100644 index 00000000..cb3ddc05 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundBean.java @@ -0,0 +1,62 @@ +package org.chorem.pollen.services.bean.voteCounting.InstantRunoff; + +import com.google.common.collect.Lists; +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.InstantRunoffRound; +import org.chorem.pollen.votecounting.InstantRunoffRoundChoice; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffRoundBean { + + protected List<InstantRunoffRoundChoiceBean> roundChoices; + + protected List<PollenEntityId<Choice>> choiceIdsExclude; + + public void fromResult(InstantRunoffRound result) { + + for (InstantRunoffRoundChoice instantRunoffRoundChoice : result.getRoundChoices()) { + + InstantRunoffRoundChoiceBean instantRunoffRoundChoiceBean = new InstantRunoffRoundChoiceBean(); + instantRunoffRoundChoiceBean.fromResult(instantRunoffRoundChoice); + getRoundChoices().add(instantRunoffRoundChoiceBean); + + } + + for (String choiceIdExclude : result.getChoiceIdsExclude()) { + + PollenEntityId<Choice> choiceId = PollenEntityId.newId(Choice.class); + choiceId.setEntityId(choiceIdExclude); + getChoiceIdsExclude().add(choiceId); + + } + + } + + + public List<InstantRunoffRoundChoiceBean> getRoundChoices() { + if (roundChoices == null) { + roundChoices = Lists.newLinkedList(); + } + return roundChoices; + } + + public void setRoundChoices(List<InstantRunoffRoundChoiceBean> roundChoices) { + this.roundChoices = roundChoices; + } + + public List<PollenEntityId<Choice>> getChoiceIdsExclude() { + if (choiceIdsExclude == null) { + choiceIdsExclude = Lists.newLinkedList(); + } + return choiceIdsExclude; + } + + public void setChoiceIdsExclude(List<PollenEntityId<Choice>> choiceIdsExclude) { + this.choiceIdsExclude = choiceIdsExclude; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundChoiceBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundChoiceBean.java new file mode 100644 index 00000000..211811a3 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/InstantRunoff/InstantRunoffRoundChoiceBean.java @@ -0,0 +1,49 @@ +package org.chorem.pollen.services.bean.voteCounting.InstantRunoff; + +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.InstantRunoffRoundChoice; + +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffRoundChoiceBean { + + protected PollenEntityId<Choice> choiceId; + + protected BigDecimal score; + + public InstantRunoffRoundChoiceBean() { + this.choiceId = PollenEntityId.newId(Choice.class); + } + + public void fromResult(InstantRunoffRoundChoice result) { + + setChoiceId(result.getChoiceId()); + setScore(result.getScore()); + + } + + public PollenEntityId<Choice> getChoiceId() { + return choiceId; + } + + public void setChoiceId(PollenEntityId<Choice> choiceId) { + this.choiceId = choiceId; + } + + public void setChoiceId(String choiceId) { + + this.choiceId.setEntityId(choiceId); + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/VoteCountingDetailResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/VoteCountingDetailResultBean.java new file mode 100644 index 00000000..8fda866a --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/VoteCountingDetailResultBean.java @@ -0,0 +1,53 @@ +package org.chorem.pollen.services.bean.voteCounting; + +import org.chorem.pollen.services.bean.voteCounting.Coombs.CoombsDetailResultBean; +import org.chorem.pollen.services.bean.voteCounting.InstantRunoff.InstantRunoffDetailResultBean; +import org.chorem.pollen.services.bean.voteCounting.borda.BordaDetailResultBean; +import org.chorem.pollen.services.bean.voteCounting.condorcet.CondorcetDetailResultBean; +import org.chorem.pollen.votecounting.BordaDetailResult; +import org.chorem.pollen.votecounting.CondorcetDetailResult; +import org.chorem.pollen.votecounting.CoombsDetailResult; +import org.chorem.pollen.votecounting.InstantRunoffDetailResult; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public abstract class VoteCountingDetailResultBean { + + public static VoteCountingDetailResultBean toBean(VoteCountingDetailResult detailResult) { + VoteCountingDetailResultBean bean = null; + + if (detailResult != null) { + + if (CondorcetDetailResult.class.isInstance(detailResult)) { + + CondorcetDetailResultBean condorcetDetailResultBean = new CondorcetDetailResultBean(); + condorcetDetailResultBean.fromResult(CondorcetDetailResult.class.cast(detailResult)); + bean = condorcetDetailResultBean; + + } else if (BordaDetailResult.class.isInstance(detailResult)) { + + BordaDetailResultBean bordaDetailResultBean = new BordaDetailResultBean(); + bordaDetailResultBean.fromResult(BordaDetailResult.class.cast(detailResult)); + bean = bordaDetailResultBean; + + } else if (CoombsDetailResult.class.isInstance(detailResult)) { + + CoombsDetailResultBean coombsDetailResultBean = new CoombsDetailResultBean(); + coombsDetailResultBean.fromResult(CoombsDetailResult.class.cast(detailResult)); + bean = coombsDetailResultBean; + + } else if (InstantRunoffDetailResult.class.isInstance(detailResult)) { + + InstantRunoffDetailResultBean instantRunoffDetailResultBean = new InstantRunoffDetailResultBean(); + instantRunoffDetailResultBean.fromResult(InstantRunoffDetailResult.class.cast(detailResult)); + bean = instantRunoffDetailResultBean; + + } + } + + return bean; + } + +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaChoiceRankBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaChoiceRankBean.java new file mode 100644 index 00000000..62dc309e --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaChoiceRankBean.java @@ -0,0 +1,58 @@ +package org.chorem.pollen.services.bean.voteCounting.borda; + +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.BordaChoiceRank; + +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class BordaChoiceRankBean { + + protected PollenEntityId<Choice> choiceId; + + protected int rank; + + protected BigDecimal score; + + public BordaChoiceRankBean() { + choiceId = PollenEntityId.newId(Choice.class); + } + + public void fromResult(BordaChoiceRank result) { + + setChoiceId(result.getChoiceId()); + setRank(result.getRank()); + setScore(result.getScore()); + } + + public PollenEntityId<Choice> getChoiceId() { + return choiceId; + } + + public void setChoiceId(PollenEntityId<Choice> choiceId) { + this.choiceId = choiceId; + } + + public void setChoiceId(String choiceId) { + this.choiceId.setEntityId(choiceId); + } + + public int getRank() { + return rank; + } + + public void setRank(int rank) { + this.rank = rank; + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaDetailResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaDetailResultBean.java new file mode 100644 index 00000000..73bf70c8 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/borda/BordaDetailResultBean.java @@ -0,0 +1,38 @@ +package org.chorem.pollen.services.bean.voteCounting.borda; + +import com.google.common.collect.Lists; +import org.chorem.pollen.services.bean.voteCounting.VoteCountingDetailResultBean; +import org.chorem.pollen.votecounting.BordaChoiceRank; +import org.chorem.pollen.votecounting.BordaDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class BordaDetailResultBean extends VoteCountingDetailResultBean { + + protected List<BordaChoiceRankBean> choiceRanks; + + public void fromResult(BordaDetailResult result) { + + for (BordaChoiceRank choiceRank : result.getChoiceRanks()) { + + BordaChoiceRankBean bordaChoiceRankBean = new BordaChoiceRankBean(); + bordaChoiceRankBean.fromResult(choiceRank); + getChoiceRanks().add(bordaChoiceRankBean); + + } + } + + public List<BordaChoiceRankBean> getChoiceRanks() { + if (choiceRanks == null) { + choiceRanks = Lists.newLinkedList(); + } + return choiceRanks; + } + + public void setChoiceRanks(List<BordaChoiceRankBean> choiceRanks) { + this.choiceRanks = choiceRanks; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetBattleBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetBattleBean.java new file mode 100644 index 00000000..0a4fef59 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetBattleBean.java @@ -0,0 +1,64 @@ +package org.chorem.pollen.services.bean.voteCounting.condorcet; + +import org.chorem.pollen.persistence.entity.Choice; +import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.votecounting.CondorcetBattle; + +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CondorcetBattleBean { + + protected PollenEntityId<Choice> opponentId; + + protected PollenEntityId<Choice> runnerId; + + protected BigDecimal score; + + public CondorcetBattleBean() { + this.opponentId = PollenEntityId.newId(Choice.class); + this.runnerId = PollenEntityId.newId(Choice.class);; + } + + public void fromResult(CondorcetBattle result) { + + setOpponentId(result.getOpponentId()); + setRunnerId(result.getRunnerId()); + setScore(result.getScore()); + + } + + public PollenEntityId<Choice> getOpponentId() { + return opponentId; + } + + public void setOpponentId(PollenEntityId<Choice> opponentId) { + this.opponentId = opponentId; + } + + public void setOpponentId(String opponentId) { + this.opponentId.setEntityId(opponentId); + } + + public PollenEntityId<Choice> getRunnerId() { + return runnerId; + } + + public void setRunnerId(PollenEntityId<Choice> runnerId) { + this.runnerId = runnerId; + } + + public void setRunnerId(String runnerId) { + this.runnerId.setEntityId(runnerId); + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetDetailResultBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetDetailResultBean.java new file mode 100644 index 00000000..cd3005dd --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/voteCounting/condorcet/CondorcetDetailResultBean.java @@ -0,0 +1,38 @@ +package org.chorem.pollen.services.bean.voteCounting.condorcet; + +import com.google.common.collect.Lists; +import org.chorem.pollen.services.bean.voteCounting.VoteCountingDetailResultBean; +import org.chorem.pollen.votecounting.CondorcetBattle; +import org.chorem.pollen.votecounting.CondorcetDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CondorcetDetailResultBean extends VoteCountingDetailResultBean { + + protected List<CondorcetBattleBean> battles; + + public void fromResult(CondorcetDetailResult result) { + + for (CondorcetBattle battle : result.getBattles()) { + + CondorcetBattleBean condorcetBattleBean = new CondorcetBattleBean(); + condorcetBattleBean.fromResult(battle); + getBattles().add(condorcetBattleBean); + + } + } + + public List<CondorcetBattleBean> getBattles() { + if (battles == null) { + battles = Lists.newLinkedList(); + } + return battles; + } + + public void setBattles(List<CondorcetBattleBean> battles) { + this.battles = battles; + } +} diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index b38ab8ec..90ab739e 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -87,6 +87,19 @@ "poll_results_unit_6_many": "votes", "poll_results_unit_7_one": "vote", "poll_results_unit_7_many": "votes", + "poll_results_borda_title": "Tableau des points", + "poll_results_borda_ranks": "Rangs", + "poll_results_borda_points": "Points", + "poll_results_borda_total": "Totaux", + "poll_results_condorcet_title": "Tableau des combats", + "poll_results_condorcet_total": "Totaux", + "poll_results_instantRunoff_title": "Tableau des éliminations", + "poll_results_instantRunoff_rounds": "Tours", + "poll_results_coombs_title": "Tableau des éliminations", + "poll_results_coombs_rounds": "Tours", + "poll_results_coombs_ranks": "Rangs", + "poll_results_coombs_firstRank": "1er", + "poll_results_coombs_lastRank": "Der.", "poll_comments_one": "Commentaire", "poll_comments_many": "Commentaires", "poll_comments_noComment": "Pas de commentaire.", @@ -579,6 +592,19 @@ "poll_results_unit_6_many": "votes", "poll_results_unit_7_one": "vote", "poll_results_unit_7_many": "votes", + "poll_results_borda_title": "Score table", + "poll_results_borda_ranks": "Ranks", + "poll_results_borda_points": "Points", + "poll_results_borda_total": "Total", + "poll_results_condorcet_title": "Battle table", + "poll_results_condorcet_total": "Total", + "poll_results_instantRunoff_title": "Eliminations table", + "poll_results_instantRunoff_rounds": "Rounds", + "poll_results_coombs_title": "Eliminations table", + "poll_results_coombs_rounds": "Rounds", + "poll_results_coombs_ranks": "Ranks", + "poll_results_coombs_firstRank": "First", + "poll_results_coombs_lastRank": "Last", "poll_comments_one": "Comment", "poll_comments_many": "Comments", "poll_comments_noComment": "No comment.", diff --git a/pollen-ui-riot-js/src/main/web/js/VoterListService.js b/pollen-ui-riot-js/src/main/web/js/VoterListService.js index 3e1ecfa5..aae9bc26 100644 --- a/pollen-ui-riot-js/src/main/web/js/VoterListService.js +++ b/pollen-ui-riot-js/src/main/web/js/VoterListService.js @@ -184,7 +184,7 @@ class VoterListService extends FetchService { pageNumber: 0 }; let importPromises = []; - importPromises.push(FavoriteListService.members(favoriteListId, pagination).then(result => { + importPromises.push(FavoriteListService.members(favoriteListId, "", pagination).then(result => { result.elements.forEach(member => {this.addMember(voterList, member.name, member.email, member.weight);}); return Promise.resolve(); })); @@ -194,7 +194,7 @@ class VoterListService extends FetchService { pageSize: -1, pageNumber: 0 }; - importPromises.push(FavoriteListService.childrenLists(favoriteListId, paginationChild).then(result => { + importPromises.push(FavoriteListService.childrenLists(favoriteListId, "", paginationChild).then(result => { let subListImportPromises = []; result.elements.forEach(childList => { let subVoterList = this.addVoterList(voterList, childList.child.name, childList.weight); diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html index 39066540..8017a2f3 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html @@ -201,6 +201,10 @@ require("./Report.tag.html"); z-index: 1; } + .container { + padding-bottom: 1.2em; + } + .poll-urls .o-form-element { padding: 0.3em 0; } diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Results.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Results.tag.html index 7788d11c..70dfb1e6 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Results.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Results.tag.html @@ -1,5 +1,8 @@ require("./ChoiceView.tag.html"); -require("./Podium.tag.html"); +require("../voteCountingType/CondorcetDetailResult.tag.html"); +require("../voteCountingType/BordaDetailResult.tag.html"); +require("../voteCountingType/InstantRunoffDetailResult.tag.html"); +require("../voteCountingType/CoombsDetailResult.tag.html"); <Results> <div class="container" show="{loaded}"> <div if="{!poll.resultIsVisible}" @@ -7,7 +10,6 @@ require("./Podium.tag.html"); {__.noResult} </div> <div if="{poll.resultIsVisible && poll.results}" class="result-body" > - <Podium/> <table class="table-results"> <thead> <tr> @@ -26,7 +28,10 @@ require("./Podium.tag.html"); </tr> </tbody> </table> - + <CondorcetDetailResult if={poll.voteCountingType === 3}/> + <BordaDetailResult if={poll.voteCountingType === 5}/> + <InstantRunoffDetailResult if={poll.voteCountingType === 6}/> + <CoombsDetailResult if={poll.voteCountingType === 7}/> </div> </div> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html index bbc9ba16..7ed152f5 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html @@ -5,11 +5,7 @@ require("../components/HumanInput.tag.html"); <Votes> <div class="container"> <div show="{loaded}"> - - <Podium if={poll.resultIsVisible}/> - <div class="voters"> - <form ref="formAddVote" class="fix"> <HumanInput onsubmit="{addVote}"/> <div class="current-voter"> diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingStrategy.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingStrategy.java index 3ad7b925..50f6cf03 100644 --- a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingStrategy.java +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingStrategy.java @@ -29,6 +29,7 @@ import org.chorem.pollen.votecounting.model.ChoiceIdAble; import org.chorem.pollen.votecounting.model.ChoiceScore; import org.chorem.pollen.votecounting.model.ListOfVoter; import org.chorem.pollen.votecounting.model.ListVoteCountingResult; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; import org.chorem.pollen.votecounting.model.VoteCountingResult; import org.chorem.pollen.votecounting.model.VoteForChoice; import org.chorem.pollen.votecounting.model.Voter; @@ -88,7 +89,7 @@ public abstract class AbstractVoteCountingStrategy implements VoteCountingStrate return resultByChoice; } - protected VoteCountingResult orderByValues(Collection<ChoiceScore> scores) { + protected VoteCountingResult orderByValues(Collection<ChoiceScore> scores, VoteCountingDetailResult detailResult) { // get scores by score value Multimap<BigDecimal, ChoiceScore> map = Multimaps.index( @@ -117,7 +118,7 @@ public abstract class AbstractVoteCountingStrategy implements VoteCountingStrate Collections.sort(orderedScores); // transform map of result to list of them (and sort them) - return VoteCountingResult.newResult(orderedScores); + return VoteCountingResult.newResult(orderedScores, detailResult); } public Set<String> getAllChoiceIds(Set<Voter> voters) { diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingDetailResult.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingDetailResult.java new file mode 100644 index 00000000..db40658b --- /dev/null +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingDetailResult.java @@ -0,0 +1,9 @@ +package org.chorem.pollen.votecounting.model; + +import java.io.Serializable; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public interface VoteCountingDetailResult extends Serializable { +} diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingResult.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingResult.java index 16162263..ecb109ca 100644 --- a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingResult.java +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteCountingResult.java @@ -47,6 +47,8 @@ public class VoteCountingResult implements Serializable { private int nbVotants; + private VoteCountingDetailResult detailResult; + /** * Result for each choice order by their winning rank. * @@ -54,9 +56,10 @@ public class VoteCountingResult implements Serializable { */ private transient ArrayListMultimap<Integer, ChoiceScore> scoresByRank; - public static VoteCountingResult newResult(List<ChoiceScore> scores) { + public static VoteCountingResult newResult(List<ChoiceScore> scores, VoteCountingDetailResult detailResult) { VoteCountingResult result = new VoteCountingResult(); result.setScores(scores); + result.setDetailResult(detailResult); return result; } @@ -98,4 +101,12 @@ public class VoteCountingResult implements Serializable { public void setNbVotants(int nbVotants) { this.nbVotants = nbVotants; } + + public VoteCountingDetailResult getDetailResult() { + return detailResult; + } + + public void setDetailResult(VoteCountingDetailResult detailResult) { + this.detailResult = detailResult; + } } diff --git a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaChoiceRank.java b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaChoiceRank.java new file mode 100644 index 00000000..0264f4c9 --- /dev/null +++ b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaChoiceRank.java @@ -0,0 +1,53 @@ +package org.chorem.pollen.votecounting; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class BordaChoiceRank implements Serializable{ + + private static final long serialVersionUID = -7120439299559002904L; + + protected String choiceId; + + protected int rank; + + protected BigDecimal score; + + public String getChoiceId() { + return choiceId; + } + + public void setChoiceId(String choiceId) { + this.choiceId = choiceId; + } + + public int getRank() { + return rank; + } + + public void setRank(int rank) { + this.rank = rank; + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } + + public void addScoreValue(double scoreToAdd) { + BigDecimal newScoreValue; + if (score == null) { + + newScoreValue = BigDecimal.valueOf(scoreToAdd); + } else { + newScoreValue = score.add(BigDecimal.valueOf(scoreToAdd)); + } + setScore(newScoreValue); + } +} diff --git a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaDetailResult.java b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaDetailResult.java new file mode 100644 index 00000000..a2e1d426 --- /dev/null +++ b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaDetailResult.java @@ -0,0 +1,27 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class BordaDetailResult implements VoteCountingDetailResult{ + + private static final long serialVersionUID = 2110358900989911730L; + + protected List<BordaChoiceRank> choiceRanks; + + public List<BordaChoiceRank> getChoiceRanks() { + if (choiceRanks == null) { + choiceRanks = Lists.newLinkedList(); + } + return choiceRanks; + } + + public void setChoiceRanks(List<BordaChoiceRank> choiceRanks) { + this.choiceRanks = choiceRanks; + } +} diff --git a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCountingStrategy.java b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCountingStrategy.java index 36da3145..b2fb5605 100644 --- a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCountingStrategy.java +++ b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCountingStrategy.java @@ -30,6 +30,7 @@ import org.chorem.pollen.votecounting.model.Voter; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -49,6 +50,7 @@ public class BordaVoteCountingStrategy extends AbstractVoteCountingStrategy { // get empty result by choice Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters); + BordaDetailResult detailResult = new BordaDetailResult(); // nb of choices int nbChoices = scores.keySet().size(); @@ -69,18 +71,22 @@ public class BordaVoteCountingStrategy extends AbstractVoteCountingStrategy { Voter voter = entry.getKey(); double weight = voter.getWeight(); double choiceWeight = nbChoices * weight; + int rank = 0; for (Set<String> sortedChoiceId : entry.getValue()) { for (String choiceId : sortedChoiceId) { scores.get(choiceId).addScoreValue(choiceWeight); + addChoiceRank(detailResult, choiceId, rank, weight); + } + rank++; choiceWeight -= weight; } } // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), detailResult); } @Override @@ -97,4 +103,21 @@ public class BordaVoteCountingStrategy extends AbstractVoteCountingStrategy { } return voteForChoices; } + + protected void addChoiceRank(BordaDetailResult detailResult, String choiceId, int rank, double score) { + Optional<BordaChoiceRank> choiceRankOptional = detailResult.getChoiceRanks().stream() + .filter(cr -> cr.getChoiceId().equals(choiceId) + && cr.getRank() == rank) + .findFirst(); + BordaChoiceRank choiceRank; + if (choiceRankOptional.isPresent()) { + choiceRank = choiceRankOptional.get(); + } else { + choiceRank = new BordaChoiceRank(); + choiceRank.setChoiceId(choiceId); + choiceRank.setRank(rank); + detailResult.getChoiceRanks().add(choiceRank); + } + choiceRank.addScoreValue(score); + } } diff --git a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetBattle.java b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetBattle.java new file mode 100644 index 00000000..0a60c22b --- /dev/null +++ b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetBattle.java @@ -0,0 +1,53 @@ +package org.chorem.pollen.votecounting; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CondorcetBattle implements Serializable { + + private static final long serialVersionUID = -9068912900242060104L; + + protected String opponentId; + + protected String runnerId; + + protected BigDecimal score; + + public String getOpponentId() { + return opponentId; + } + + public void setOpponentId(String opponentId) { + this.opponentId = opponentId; + } + + public String getRunnerId() { + return runnerId; + } + + public void setRunnerId(String runnerId) { + this.runnerId = runnerId; + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } + + public void addScoreValue(double scoreToAdd) { + BigDecimal newScoreValue; + if (score == null) { + + newScoreValue = BigDecimal.valueOf(scoreToAdd); + } else { + newScoreValue = score.add(BigDecimal.valueOf(scoreToAdd)); + } + setScore(newScoreValue); + } +} diff --git a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetDetailResult.java b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetDetailResult.java new file mode 100644 index 00000000..493da24a --- /dev/null +++ b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetDetailResult.java @@ -0,0 +1,27 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CondorcetDetailResult implements VoteCountingDetailResult { + + private static final long serialVersionUID = -1586948510368173100L; + + protected List<CondorcetBattle> battles; + + public List<CondorcetBattle> getBattles() { + if (battles == null) { + battles = Lists.newLinkedList(); + } + return battles; + } + + public void setBattles(List<CondorcetBattle> battles) { + this.battles = battles; + } +} diff --git a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCountingStrategy.java b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCountingStrategy.java index bf15dcde..a4125830 100644 --- a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCountingStrategy.java +++ b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCountingStrategy.java @@ -29,6 +29,7 @@ import org.chorem.pollen.votecounting.model.VoteForChoice; import org.chorem.pollen.votecounting.model.Voter; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -48,15 +49,16 @@ public class CondorcetVoteCountingStrategy extends AbstractVoteCountingStrategy // get empty result by choice Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters); + CondorcetDetailResult detailResult = new CondorcetDetailResult(); for (Voter voter : voters) { // add this voter votes to result - addVoterChoices(voter, scores); + addVoterChoices(voter, scores, detailResult); } // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), detailResult); } @Override @@ -75,7 +77,8 @@ public class CondorcetVoteCountingStrategy extends AbstractVoteCountingStrategy } protected void addVoterChoices(Voter voter, - Map<String, ChoiceScore> scores) { + Map<String, ChoiceScore> scores, + CondorcetDetailResult detailResult) { if (log.isDebugEnabled()) { log.debug("Start count for voter " + voter.getVoterId()); @@ -90,7 +93,8 @@ public class CondorcetVoteCountingStrategy extends AbstractVoteCountingStrategy for (VoteForChoice voteForChoiceY : voter.getVoteForChoices()) { - if (choiceIdX.equals(voteForChoiceY.getChoiceId())) { + String choiceIdY = voteForChoiceY.getChoiceId(); + if (choiceIdX.equals(choiceIdY)) { // no battle for same choice continue; } @@ -101,6 +105,7 @@ public class CondorcetVoteCountingStrategy extends AbstractVoteCountingStrategy // X wins over Y; score += voterWeight; + addBattle(detailResult, choiceIdX, choiceIdY, voterWeight); } } @@ -110,4 +115,21 @@ public class CondorcetVoteCountingStrategy extends AbstractVoteCountingStrategy } } + protected void addBattle(CondorcetDetailResult detailResult, String opponentId, String runnerId, double score) { + Optional<CondorcetBattle> battleOptional = detailResult.getBattles().stream() + .filter(battle -> battle.getOpponentId().equals(opponentId) + && battle.getRunnerId().equals(runnerId)) + .findFirst(); + CondorcetBattle battle; + if (battleOptional.isPresent()) { + battle = battleOptional.get(); + } else { + battle = new CondorcetBattle(); + battle.setOpponentId(opponentId); + battle.setRunnerId(runnerId); + detailResult.getBattles().add(battle); + } + battle.addScoreValue(score); + } + } diff --git a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsDetailResult.java b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsDetailResult.java new file mode 100644 index 00000000..2eaa1b62 --- /dev/null +++ b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsDetailResult.java @@ -0,0 +1,27 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsDetailResult implements VoteCountingDetailResult { + + private static final long serialVersionUID = -9210665864802832614L; + + protected List<CoombsRound> rounds; + + public List<CoombsRound> getRounds() { + if (rounds == null) { + rounds = Lists.newLinkedList(); + } + return rounds; + } + + public void setRounds(List<CoombsRound> rounds) { + this.rounds = rounds; + } +} diff --git a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRound.java b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRound.java new file mode 100644 index 00000000..8c8f55ca --- /dev/null +++ b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRound.java @@ -0,0 +1,40 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsRound implements Serializable { + + private static final long serialVersionUID = 2674984887224861918L; + + protected List<CoombsRoundChoice> roundChoices; + + protected List<String> choiceIdsExclude; + + public List<CoombsRoundChoice> getRoundChoices() { + if (roundChoices == null) { + roundChoices = Lists.newLinkedList(); + } + return roundChoices; + } + + public void setRoundChoices(List<CoombsRoundChoice> roundChoices) { + this.roundChoices = roundChoices; + } + + public List<String> getChoiceIdsExclude() { + if (choiceIdsExclude == null) { + choiceIdsExclude = Lists.newLinkedList(); + } + return choiceIdsExclude; + } + + public void setChoiceIdsExclude(List<String> choiceIdsExclude) { + this.choiceIdsExclude = choiceIdsExclude; + } +} diff --git a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRoundChoice.java b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRoundChoice.java new file mode 100644 index 00000000..f71b0fc8 --- /dev/null +++ b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsRoundChoice.java @@ -0,0 +1,64 @@ +package org.chorem.pollen.votecounting; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class CoombsRoundChoice implements Serializable { + + private static final long serialVersionUID = -4609190485828274256L; + + protected String choiceId; + + protected BigDecimal firstScore; + + protected BigDecimal lastScore; + + public String getChoiceId() { + return choiceId; + } + + public void setChoiceId(String choiceId) { + this.choiceId = choiceId; + } + + public BigDecimal getFirstScore() { + return firstScore; + } + + public void setFirstScore(BigDecimal firstScore) { + this.firstScore = firstScore; + } + + public BigDecimal getLastScore() { + return lastScore; + } + + public void setLastScore(BigDecimal lastScore) { + this.lastScore = lastScore; + } + + public void addFirstScore(double scoreToAdd) { + BigDecimal newScoreValue; + if (firstScore == null) { + + newScoreValue = BigDecimal.valueOf(scoreToAdd); + } else { + newScoreValue = firstScore.add(BigDecimal.valueOf(scoreToAdd)); + } + setFirstScore(newScoreValue); + } + + public void addLastScore(double scoreToAdd) { + BigDecimal newScoreValue; + if (lastScore == null) { + + newScoreValue = BigDecimal.valueOf(scoreToAdd); + } else { + newScoreValue = lastScore.add(BigDecimal.valueOf(scoreToAdd)); + } + setLastScore(newScoreValue); + } +} diff --git a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCountingStrategy.java b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCountingStrategy.java index 16337449..d7f16078 100644 --- a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCountingStrategy.java +++ b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCountingStrategy.java @@ -30,10 +30,11 @@ import org.chorem.pollen.votecounting.model.VoteForChoice; import org.chorem.pollen.votecounting.model.Voter; import java.math.BigDecimal; -import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -49,6 +50,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { // get empty result by choice Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters); + CoombsDetailResult detailResult = new CoombsDetailResult(); // calcul du score minimum pour atteindre la majorité absolue double totalWeight = 0.; @@ -69,10 +71,11 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { choiceIdsToExclude, choiceIdsToKeep, scores, - totalWeight); + totalWeight, + detailResult); // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), detailResult); } @Override @@ -94,12 +97,18 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { Set<String> idsToExclude, Set<String> idsToInclude, Map<String, ChoiceScore> resultByChoice, - double totalWeight) { + double totalWeight, + CoombsDetailResult detailResult) { + + CoombsRound round = new CoombsRound(); + + detailResult.getRounds().add(round); List<ChoiceScore> results = applyScores(topRankChoices, idsToExclude, idsToInclude, - resultByChoice); + resultByChoice, + round); if (!results.isEmpty()) { @@ -111,7 +120,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { // pas de majorité absolue, il faut éliminer le(s) choix les plus mauvais - guessChoiceIdsToRemove(idsToInclude, idsToExclude, topRankChoices); + guessChoiceIdsToRemove(idsToInclude, idsToExclude, topRankChoices, round); // on ne veux plus utiliser ces choix dans le tour suivant idsToInclude.removeAll(idsToExclude); @@ -121,7 +130,8 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { idsToExclude, idsToInclude, resultByChoice, - totalWeight); + totalWeight, + detailResult); } else { @@ -133,7 +143,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { protected List<ChoiceScore> applyScores(Map<Voter, List<Set<String>>> topRankChoices, Set<String> idsToExclude, Set<String> idsToInclude, - Map<String, ChoiceScore> resultByChoice) { + Map<String, ChoiceScore> resultByChoice, CoombsRound round) { if (CollectionUtils.isNotEmpty(idsToExclude)) { @@ -171,6 +181,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { if (idsToInclude.contains(id)) { ChoiceScore choiceScore = resultByChoice.get(id); choiceScore.addScoreValue(voterWeight); + addRoundChoiceFirst(round, id, voterWeight); } } } @@ -184,7 +195,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { results.add(resultByChoice.get(id)); } - Collections.sort(results); + results.sort(Comparator.comparing(ChoiceScore::getScoreValue)); // on supprime tout score à 0 Iterator<ChoiceScore> itr = results.iterator(); @@ -203,13 +214,14 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { protected void guessChoiceIdsToRemove(Set<String> idsToInclude, Set<String> idsToExclude, - Map<Voter, List<Set<String>>> results) { + Map<Voter, List<Set<String>>> results, + CoombsRound round) { // pour chaque choix restant on calcule son score lorsqu'il est le dernier Map<String, ChoiceScore> badScores = Maps.newTreeMap(); for (String id : idsToInclude) { - badScores.put(id, ChoiceScore.newScore(id, null)); + badScores.put(id, ChoiceScore.newScore(id, BigDecimal.ZERO)); } for (Map.Entry<Voter, List<Set<String>>> entry : results.entrySet()) { @@ -223,6 +235,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { for (String lastId : lastIds) { badScores.get(lastId).addScoreValue(voterWeight); + addRoundChoiceLast(round, lastId, voterWeight); } } } @@ -231,10 +244,7 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { List<ChoiceScore> scores = Lists.newArrayList(badScores.values()); // que l'on trie - Collections.sort(scores); - - // puis revers (pour avoir les meilleurs scores au début) - Collections.reverse(scores); + scores.sort(Comparator.comparing(ChoiceScore::getScoreValue).reversed()); BigDecimal firstScoreValue = scores.get(0).getScoreValue(); double maxScore = firstScoreValue == null ? 0 : firstScoreValue.doubleValue(); @@ -252,5 +262,32 @@ public class CoombsVoteCountingStrategy extends AbstractVoteCountingStrategy { break; } } + round.getChoiceIdsExclude().addAll(idsToExclude); + } + + protected void addRoundChoiceFirst(CoombsRound round, String choiceId, double score) { + CoombsRoundChoice roundChoice = getRoundChoice(round, choiceId); + roundChoice.addFirstScore(score); + } + + protected void addRoundChoiceLast(CoombsRound round, String choiceId, double score) { + CoombsRoundChoice roundChoice = getRoundChoice(round, choiceId); + roundChoice.addLastScore(score); + } + + protected CoombsRoundChoice getRoundChoice(CoombsRound round, String choiceId) { + Optional<CoombsRoundChoice> roundChoiceOptional = round.getRoundChoices().stream() + .filter(rc -> rc.getChoiceId().equals(choiceId)) + .findFirst(); + + CoombsRoundChoice result; + if (roundChoiceOptional.isPresent()) { + result = roundChoiceOptional.get(); + } else { + result = new CoombsRoundChoice(); + result.setChoiceId(choiceId); + round.getRoundChoices().add(result); + } + return result; } } diff --git a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffDetailResult.java b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffDetailResult.java new file mode 100644 index 00000000..50ff629c --- /dev/null +++ b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffDetailResult.java @@ -0,0 +1,27 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; +import org.chorem.pollen.votecounting.model.VoteCountingDetailResult; + +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffDetailResult implements VoteCountingDetailResult { + + private static final long serialVersionUID = 2519984481797055100L; + + protected List<InstantRunoffRound> rounds; + + public List<InstantRunoffRound> getRounds() { + if (rounds == null) { + rounds = Lists.newLinkedList(); + } + return rounds; + } + + public void setRounds(List<InstantRunoffRound> rounds) { + this.rounds = rounds; + } +} diff --git a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRound.java b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRound.java new file mode 100644 index 00000000..e7e918e2 --- /dev/null +++ b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRound.java @@ -0,0 +1,40 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Lists; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffRound implements Serializable { + + private static final long serialVersionUID = -1027997064615605718L; + + protected List<InstantRunoffRoundChoice> roundChoices; + + protected List<String> choiceIdsExclude; + + public List<InstantRunoffRoundChoice> getRoundChoices() { + if (roundChoices == null) { + roundChoices = Lists.newLinkedList(); + } + return roundChoices; + } + + public void setRoundChoices(List<InstantRunoffRoundChoice> roundChoices) { + this.roundChoices = roundChoices; + } + + public List<String> getChoiceIdsExclude() { + if (choiceIdsExclude == null) { + choiceIdsExclude = Lists.newLinkedList(); + } + return choiceIdsExclude; + } + + public void setChoiceIdsExclude(List<String> choiceIdsExclude) { + this.choiceIdsExclude = choiceIdsExclude; + } +} diff --git a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRoundChoice.java b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRoundChoice.java new file mode 100644 index 00000000..57d221bf --- /dev/null +++ b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffRoundChoice.java @@ -0,0 +1,43 @@ +package org.chorem.pollen.votecounting; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class InstantRunoffRoundChoice implements Serializable { + + private static final long serialVersionUID = -6422243016894928123L; + + protected String choiceId; + + protected BigDecimal score; + + public String getChoiceId() { + return choiceId; + } + + public void setChoiceId(String choiceId) { + this.choiceId = choiceId; + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } + + public void addScoreValue(double scoreToAdd) { + BigDecimal newScoreValue; + if (score == null) { + + newScoreValue = BigDecimal.valueOf(scoreToAdd); + } else { + newScoreValue = score.add(BigDecimal.valueOf(scoreToAdd)); + } + setScore(newScoreValue); + } +} diff --git a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCountingStrategy.java b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCountingStrategy.java index e865104e..70e7000a 100644 --- a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCountingStrategy.java +++ b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCountingStrategy.java @@ -29,10 +29,11 @@ import org.chorem.pollen.votecounting.model.VoteForChoice; import org.chorem.pollen.votecounting.model.Voter; import java.math.BigDecimal; -import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -48,6 +49,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat // get empty result by choice Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters); + InstantRunoffDetailResult detailResult = new InstantRunoffDetailResult(); // calcul du score minimum pour atteindre la majorité absolue double totalWeight = 0.; @@ -68,10 +70,11 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat choiceIdsToExclude, choiceIdsToKeep, scores, - totalWeight); + totalWeight, + detailResult); // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), detailResult); } @Override @@ -93,12 +96,18 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat Set<String> idsToExclude, Set<String> idsToInclude, Map<String, ChoiceScore> resultByChoice, - double totalWeight) { + double totalWeight, + InstantRunoffDetailResult detailResult) { + + InstantRunoffRound round = new InstantRunoffRound(); + + detailResult.getRounds().add(round); List<ChoiceScore> results = applyScores(topRankChoices, idsToExclude, idsToInclude, - resultByChoice); + resultByChoice, + round); if (!results.isEmpty()) { @@ -110,7 +119,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat // pas de majorité absolue, il faut éliminer le(s) choix les plus mauvais - guessChoiceIdsToRemove(idsToExclude, results); + guessChoiceIdsToRemove(idsToExclude, results, round); // on ne veux plus utiliser ces choix dans le tour suivant @@ -121,7 +130,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat idsToExclude, idsToInclude, resultByChoice, - totalWeight); + totalWeight, detailResult); } else { @@ -133,7 +142,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat protected List<ChoiceScore> applyScores(Map<Voter, List<Set<String>>> topRankChoices, Set<String> idsToExclude, Set<String> idsToInclude, - Map<String, ChoiceScore> resultByChoice) { + Map<String, ChoiceScore> resultByChoice, InstantRunoffRound round) { if (CollectionUtils.isNotEmpty(idsToExclude)) { @@ -171,6 +180,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat if (idsToInclude.contains(id)) { ChoiceScore choiceScore = resultByChoice.get(id); choiceScore.addScoreValue(voterWeight); + addRoundChoice(round, id, voterWeight); } } } @@ -184,7 +194,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat results.add(resultByChoice.get(id)); } - Collections.sort(results); + results.sort(Comparator.comparing(ChoiceScore::getScoreValue)); // on supprime tout score à 0 Iterator<ChoiceScore> itr = results.iterator(); @@ -202,7 +212,7 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat } protected void guessChoiceIdsToRemove(Set<String> idsToExclude, - List<ChoiceScore> results) { + List<ChoiceScore> results, InstantRunoffRound round) { double min = results.get(0).getScoreValue().doubleValue(); @@ -219,6 +229,28 @@ public class InstantRunoffVoteCountingStrategy extends AbstractVoteCountingStrat break; } } + round.getChoiceIdsExclude().addAll(idsToExclude); + } + + protected void addRoundChoice(InstantRunoffRound round, String choiceId, double score) { + InstantRunoffRoundChoice roundChoice = getRoundChoice(round, choiceId); + roundChoice.addScoreValue(score); + } + + protected InstantRunoffRoundChoice getRoundChoice(InstantRunoffRound round, String choiceId) { + Optional<InstantRunoffRoundChoice> roundChoiceOptional = round.getRoundChoices().stream() + .filter(rc -> rc.getChoiceId().equals(choiceId)) + .findFirst(); + + InstantRunoffRoundChoice result; + if (roundChoiceOptional.isPresent()) { + result = roundChoiceOptional.get(); + } else { + result = new InstantRunoffRoundChoice(); + result.setChoiceId(choiceId); + round.getRoundChoices().add(result); + } + return result; } } diff --git a/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategy.java b/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategy.java index 2a232563..456e19c3 100644 --- a/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategy.java +++ b/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategy.java @@ -53,7 +53,7 @@ public class NormalVoteCountingStrategy extends AbstractVoteCountingStrategy { } // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), null); } protected void addVoterChoices(Voter voter, diff --git a/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCountingStrategy.java b/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCountingStrategy.java index bde7d216..8b5deaf5 100644 --- a/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCountingStrategy.java +++ b/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCountingStrategy.java @@ -50,7 +50,7 @@ public class NumberVoteCountingStrategy extends AbstractVoteCountingStrategy { } // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), null); } @Override diff --git a/pollen-votecounting-percentage/src/main/java/org/chorem/pollen/votecounting/PercentageVoteCountingStrategy.java b/pollen-votecounting-percentage/src/main/java/org/chorem/pollen/votecounting/PercentageVoteCountingStrategy.java index eaf90a26..d503fd01 100644 --- a/pollen-votecounting-percentage/src/main/java/org/chorem/pollen/votecounting/PercentageVoteCountingStrategy.java +++ b/pollen-votecounting-percentage/src/main/java/org/chorem/pollen/votecounting/PercentageVoteCountingStrategy.java @@ -60,7 +60,7 @@ public class PercentageVoteCountingStrategy extends AbstractVoteCountingStrategy } // order scores (using their value) and return result - return orderByValues(scores.values()); + return orderByValues(scores.values(), null); } protected void addVoterChoices(Voter voter, -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.