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 453e1e776335c0ea59433226ae1b090cf17b2bf6 Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Thu May 24 12:27:18 2018 +0200 refs #164 : Revoir la validation des votes par les méthodes de votes --- .../pollen/services/bean/VoteCountingTypeBean.java | 21 ------ .../chorem/pollen/services/service/ErrorMap.java | 5 ++ .../services/service/VoteCountingService.java | 52 +++++---------- .../services/service/VoteCountingTypeService.java | 2 - .../pollen/services/service/VoteService.java | 20 +----- .../i18n/pollen-services_en_GB.properties | 1 - .../i18n/pollen-services_fr_FR.properties | 1 - .../pollen/votecounting/AbstractVoteCounting.java | 23 +++++++ .../AbstractVoteCountingMaxChoice.java | 35 ++++++++++ .../votecounting/AbstractVoteCountingStrategy.java | 19 ++---- .../chorem/pollen/votecounting/VoteCounting.java | 77 +++------------------- .../pollen/votecounting/model/VoteForChoice.java | 4 ++ .../i18n/pollen-votecounting-api_en_GB.properties | 1 + .../i18n/pollen-votecounting-api_fr_FR.properties | 1 + .../pollen/votecounting/BordaVoteCounting.java | 53 ++++----------- .../pollen-votecounting-borda_en_GB.properties | 2 +- .../pollen-votecounting-borda_fr_FR.properties | 2 +- .../pollen/votecounting/CondorcetVoteCounting.java | 59 +++++------------ .../pollen-votecounting-condorcet_en_GB.properties | 3 +- .../pollen-votecounting-condorcet_fr_FR.properties | 3 +- .../pollen/votecounting/CoombsVoteCounting.java | 60 +++++------------ .../pollen-votecounting-coombs_en_GB.properties | 3 +- .../pollen-votecounting-coombs_fr_FR.properties | 3 +- .../votecounting/CumulativeVoteCounting.java | 73 ++++++++++---------- ...pollen-votecounting-cumulative_en_GB.properties | 5 +- ...pollen-votecounting-cumulative_fr_FR.properties | 5 +- .../votecounting/InstantRunoffVoteCounting.java | 60 +++++------------ ...en-votecounting-instant-runoff_en_GB.properties | 3 +- ...en-votecounting-instant-runoff_fr_FR.properties | 3 +- .../votecounting/MajorityJudgmentVoteCounting.java | 59 +++++------------ ...votecounting-majority-judgment_en_GB.properties | 3 +- ...votecounting-majority-judgment_fr_FR.properties | 3 +- .../pollen/votecounting/NormalVoteCounting.java | 52 +-------------- .../votecounting/NormalVoteCountingStrategy.java | 9 +-- .../NormalVoteCountingStrategyTest.java | 4 +- .../pollen/votecounting/NumberVoteCounting.java | 52 +-------------- 36 files changed, 250 insertions(+), 531 deletions(-) diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingTypeBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingTypeBean.java index 32e0ed17..b96ab5a4 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingTypeBean.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/VoteCountingTypeBean.java @@ -41,13 +41,8 @@ public class VoteCountingTypeBean { protected String renderType; - protected boolean canLimitNumberOfChoices; - protected Double minimumValue; - protected Double maximumValue; - - public int getId() { return id; } @@ -104,14 +99,6 @@ public class VoteCountingTypeBean { } } - public boolean isCanLimitNumberOfChoices() { - return canLimitNumberOfChoices; - } - - public void setCanLimitNumberOfChoices(boolean canLimitNumberOfChoices) { - this.canLimitNumberOfChoices = canLimitNumberOfChoices; - } - public Double getMinimumValue() { return minimumValue; } @@ -119,12 +106,4 @@ public class VoteCountingTypeBean { public void setMinimumValue(Double minimumValue) { this.minimumValue = minimumValue; } - - public Double getMaximumValue() { - return maximumValue; - } - - public void setMaximumValue(Double maximumValue) { - this.maximumValue = maximumValue; - } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/ErrorMap.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/ErrorMap.java index 9a7c7a43..8badade2 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/ErrorMap.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/ErrorMap.java @@ -58,6 +58,11 @@ public class ErrorMap { errors.putAll(errorMap.getErrors()); } + + public void addAllErrors(Multimap<String, String> errors) { + this.errors.putAll(errors); + } + public void copyTo(ErrorMap errorMap, String prefix) { for (String key : errors.keySet()) { diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingService.java index 7ab3907f..72e99968 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingService.java @@ -36,7 +36,6 @@ import org.chorem.pollen.services.bean.ChoiceBean; import org.chorem.pollen.services.bean.ListVoteCountingResultBean; import org.chorem.pollen.services.bean.VoteBean; import org.chorem.pollen.services.bean.VoteCountingResultBean; -import org.chorem.pollen.services.bean.VoteToChoiceBean; import org.chorem.pollen.services.service.security.PollenPermissions; import org.chorem.pollen.votecounting.VoteCounting; import org.chorem.pollen.votecounting.VoteCountingFactory; @@ -50,7 +49,6 @@ import org.chorem.pollen.votecounting.model.VoteForChoice; import org.chorem.pollen.votecounting.model.Voter; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -294,6 +292,19 @@ public class VoteCountingService extends PollenServiceSupport { } + protected SimpleVoter toSimpleVoter(VoteBean vote) { + + Set<VoteForChoice> voteForChoices = vote.getChoice() + .stream() + .map(voteToChoice -> VoteForChoice.newVote( + voteToChoice.getChoiceId().getEntityId() , + voteToChoice.getVoteValue())) + .collect(Collectors.toSet()); + + return SimpleVoter.newVoter(vote.getVoterId().getEntityId(), vote.getWeight(), voteForChoices); + + } + protected <S extends VoteCountingStrategy<C>, C extends VoteCountingConfig> VoteCounting<S, C> getVoteCounting(Poll poll) { int id = poll.getVoteCountingType(); @@ -308,40 +319,13 @@ public class VoteCountingService extends PollenServiceSupport { } - protected <C extends VoteCountingConfig> ErrorMap voteIsValid(VoteBean vote, VoteCounting<?, C> voteCounting, List<ChoiceBean> choices, C config) { - ErrorMap errors = new ErrorMap(); - - Map<String, Integer> choiceOrderById = choices.stream().collect(Collectors.toMap(ChoiceBean::getEntityId, ChoiceBean::getChoiceOrder)); - Double total = null; - for (VoteToChoiceBean choice : vote.getChoice()) { - - Double voteValue = choice.getVoteValue(); - if (voteCounting.isVoteValueNull(voteValue)) { - continue; - } - - if (total == null) { - total = 0.0; - } - - total += voteValue; - - check(errors, - "voteValue#" + choice.getChoiceId().getReducedId(), - voteCounting.isVoteValueValid(voteValue, config), - voteCounting.getVoteValueNotValidMessage( - getLocale(), - choiceOrderById.get(choice.getChoiceId().getEntityId()) + 1, - voteValue, - config)); - } + protected <C extends VoteCountingConfig> ErrorMap checkVote(VoteBean vote, VoteCounting<?, C> voteCounting, List<ChoiceBean> choices, C config) { + ErrorMap errorMap = new ErrorMap(); - check(errors, - "totalVoteValue", - voteCounting.isTotalVoteValueValid(total, config), - voteCounting.getTotalVoteValueNotValidMessage(getLocale(), config)); + SimpleVoter simpleVoter = toSimpleVoter(vote); - return errors; + errorMap.addAllErrors(voteCounting.checkVote(simpleVoter, config, getLocale())); + return errorMap; } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingTypeService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingTypeService.java index ba7cfc56..045ce2bf 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingTypeService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteCountingTypeService.java @@ -76,9 +76,7 @@ public class VoteCountingTypeService extends PollenServiceSupport { newVoteCountingType.setHelper(voteCounting.getHelp(l)); newVoteCountingType.setShortHelper(voteCounting.getShortHelp(l)); newVoteCountingType.setRenderType(voteCounting.getVoteValueEditorType()); - newVoteCountingType.setCanLimitNumberOfChoices(voteCounting.canLimitNumberOfChoices()); newVoteCountingType.setMinimumValue(voteCounting.getMinimumValue()); - newVoteCountingType.setMaximumValue(voteCounting.getMaximumValue()); return newVoteCountingType; } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java index 6cfab535..5cac2fb8 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java @@ -43,7 +43,6 @@ import org.chorem.pollen.services.bean.VoteBean; import org.chorem.pollen.services.bean.VoteToChoiceBean; import org.chorem.pollen.services.service.security.PollenPermissions; import org.chorem.pollen.votecounting.VoteCounting; -import org.chorem.pollen.votecounting.model.MaxChoicesNumberConfig; import org.chorem.pollen.votecounting.model.VoteCountingConfig; import org.nuiton.util.pagination.PaginationParameter; import org.nuiton.util.pagination.PaginationResult; @@ -351,26 +350,9 @@ public class VoteService extends PollenServiceSupport { C config = getVoteCountingService().getVoteCountingConfig(poll); - ErrorMap valueErrors = getVoteCountingService().voteIsValid(vote, voteCounting, choices, config); + ErrorMap valueErrors = getVoteCountingService().checkVote(vote, voteCounting, choices, config); valueErrors.copyTo(errors, "vote."); - if (MaxChoicesNumberConfig.class.isInstance(config)) { - MaxChoicesNumberConfig maxChoicesNumberConfig = MaxChoicesNumberConfig.class.cast(config); - - if (maxChoicesNumberConfig.getMaxChoiceNumber() > 0) { - int nbChoice = 0; - for (VoteToChoiceBean voteToChoice : vote.getChoice()) { - if (voteCounting.isChoiceInVote(voteToChoice.getVoteValue())) { - nbChoice++; - } - } - - check(errors, "vote.limitedVote", nbChoice <= maxChoicesNumberConfig.getMaxChoiceNumber(), l(getLocale(), "pollen.error.vote.limitedVote.overflow")); - } - } - - //TODO Finish validation - return errors; } diff --git a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties index 8417241e..28e65d98 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties @@ -99,7 +99,6 @@ pollen.error.user.mailUnauthorized=The email address is not authorized pollen.error.user.nameEmpty=The name cannot be empty pollen.error.user.passwordEmpty=The password cannot be empty pollen.error.user.passwordInvalid=The password is not valid -pollen.error.vote.limitedVote.overflow=Too many choices pollen.error.vote.poll.finished=Votes are finished, you cannot vote anymore pollen.error.vote.poll.notStarted=poll is not started pollen.error.vote.voterName.alreadyExist=voter name is already used diff --git a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties index 2cbfd805..6407eb33 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties @@ -99,7 +99,6 @@ pollen.error.user.mailUnauthorized=L'adresse de courriel n'est pas autorisée pollen.error.user.nameEmpty=Le nom ne peut pas être vide pollen.error.user.passwordEmpty=Le mot de passe ne peut pas être vide pollen.error.user.passwordInvalid=Le mot de passe est invalide -pollen.error.vote.limitedVote.overflow=Le nombre de choix maximal atteind pollen.error.vote.poll.finished=Les votes sont terminés, vous ne pouvez plus voter pollen.error.vote.poll.notStarted=Les votes n'ont pas commencé pollen.error.vote.voterName.alreadyExist=Le nom existe déjà diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCounting.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCounting.java index 7e28a8fd..9b25697b 100644 --- a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCounting.java +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCounting.java @@ -21,7 +21,11 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.VoteCountingConfig; +import org.chorem.pollen.votecounting.model.VoteForChoice; +import org.chorem.pollen.votecounting.model.Voter; import java.util.Locale; @@ -102,4 +106,23 @@ public abstract class AbstractVoteCounting<S extends VoteCountingStrategy<C>, C public Class<C> getConfigType() { return configType; } + + @Override + public Multimap<String, String> checkVote(Voter vote, C config, Locale locale) { + Multimap<String, String> errorMap = ArrayListMultimap.create(); + + for(VoteForChoice voteForChoice : vote.getVoteForChoices()) { + Multimap<String, String> choiceError = checkVoteForChoice(voteForChoice, config, locale); + choiceError.entries().forEach(entry -> + errorMap.put("choice[" + voteForChoice.getChoiceId() + "]." + entry.getKey(), entry.getValue())); + } + + return errorMap; + + } + + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, C config, Locale locale) { + return ArrayListMultimap.create(); + + } } diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingMaxChoice.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingMaxChoice.java new file mode 100644 index 00000000..88900794 --- /dev/null +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/AbstractVoteCountingMaxChoice.java @@ -0,0 +1,35 @@ +package org.chorem.pollen.votecounting; + +import com.google.common.collect.Multimap; +import org.chorem.pollen.votecounting.model.MaxChoicesNumberConfig; +import org.chorem.pollen.votecounting.model.Voter; + +import java.util.Locale; + +import static org.nuiton.i18n.I18n.l; + +public abstract class AbstractVoteCountingMaxChoice<S extends VoteCountingStrategy<C>, C extends MaxChoicesNumberConfig> extends AbstractVoteCounting<S, C> { + + + protected AbstractVoteCountingMaxChoice(int id, Class<S> strategyType, Class<C> configType, String i18nName, String i18nShortHelp, String i18nHelp) { + super(id, strategyType, configType, i18nName, i18nShortHelp, i18nHelp); + } + + @Override + public Multimap<String, String> checkVote(Voter vote, C config, Locale locale) { + Multimap<String, String> errorMap = super.checkVote(vote, config, locale); + + if (config.getMaxChoiceNumber() > 0) { + long nbChoice = vote.getVoteForChoices() + .stream() + .filter(voteForChoice -> voteForChoice.getVoteValue() != null && voteForChoice.getVoteValue() != 0) + .count(); + if (nbChoice > config.getMaxChoiceNumber()) { + errorMap.put("choices", l(locale, "pollen.voteCountingType.maxChoices.error.overflow")); + } + + } + + return errorMap; + } +} 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 ffd402bf..0500b312 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 @@ -54,17 +54,8 @@ public abstract class AbstractVoteCountingStrategy<C extends VoteCountingConfig> public static final BigDecimal ZERO_D = BigDecimal.valueOf(0.); - protected final Comparator<VoteForChoice> voteValueComparator = (o1, o2) -> { - Double v1 = o1.getVoteValue(); - Double v2 = o2.getVoteValue(); - if (v1 == null) { - v1 = Double.MAX_VALUE; - } - if (v2 == null) { - v2 = Double.MAX_VALUE; - } - return v1.intValue() - v2.intValue(); - }; + protected final Comparator<VoteForChoice> voteValueComparator = + Comparator.comparing(VoteForChoice::getVoteValue, Comparator.nullsLast(Comparator.naturalOrder())); protected C config; @@ -84,6 +75,10 @@ public abstract class AbstractVoteCountingStrategy<C extends VoteCountingConfig> } public Map<String, ChoiceScore> newEmptyChoiceScoreMap(Set<Voter> voters) { + return newEmptyChoiceScoreMap(voters, null); + } + + public Map<String, ChoiceScore> newEmptyChoiceScoreMap(Set<Voter> voters, BigDecimal defaultScore) { // get all choice Id Set<String> choiceIds = getAllChoiceIds(voters); @@ -91,7 +86,7 @@ public abstract class AbstractVoteCountingStrategy<C extends VoteCountingConfig> // creates all empty result for choice for (String choiceId : choiceIds) { - ChoiceScore choiceScore = ChoiceScore.newScore(choiceId, null); + ChoiceScore choiceScore = ChoiceScore.newScore(choiceId, defaultScore); resultByChoice.put(choiceId, choiceScore); } return resultByChoice; diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/VoteCounting.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/VoteCounting.java index f25986b0..c6d1a525 100644 --- a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/VoteCounting.java +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/VoteCounting.java @@ -21,8 +21,10 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.VoteCountingConfig; +import org.chorem.pollen.votecounting.model.Voter; import java.util.Locale; @@ -56,14 +58,6 @@ public interface VoteCounting<S extends VoteCountingStrategy<C>, C extends VoteC */ int getId(); - /** - * Test if the given value is a vote value (says users has filled it). - * - * @param voteValue the vote value to test - * @return {@code true} if the given value is persisted. - */ - boolean isChoiceInVote(Double voteValue); - /** * Get the vote counting strategy name to display in UI. * @@ -88,55 +82,6 @@ public interface VoteCounting<S extends VoteCountingStrategy<C>, C extends VoteC */ String getHelp(Locale locale); - /** - * Tests if the given vote value is valid. - * - * @param voteValue the vote value to test - * @return {@code true} if the given vote value is valid, {@code false} - * otherwise. - */ - boolean isVoteValueValid(Double voteValue, C config); - - /** - * If a vote value is not valid, gets the localized message of the error. - * - * @param locale the locale used to render the error message - * @param choiceNumber number of the choice - * @param voteValue value fo the vote - * @return the localized validation message - */ - String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - C config); - - /** - * Tests if the total values of a vote is valid. - * - * @param totalValues the given total values - * @return {@code true} if the total values of a vote is valid, - * {@code false} otherwhise. - */ - boolean isTotalVoteValueValid(Double totalValues, C config); - - /** - * If the total values of a vote is not valid, gets the localized error - * message. - * - * @param locale the locale used to render the error message - * @return the localized validation message - */ - String getTotalVoteValueNotValidMessage(Locale locale, C config); - - /** - * Tests if a given vote value is null or not. - * - * @param value the vote value to test - * @return {@code true} if the given vote value is null (or considered as - * null), {@code false} otherwise. - */ - boolean isVoteValueNull(Double value); - /** * Gets the type of editor used to render a vote value. * @@ -145,14 +90,6 @@ public interface VoteCounting<S extends VoteCountingStrategy<C>, C extends VoteC */ ChoiceToVoteRenderType getVoteValueEditorType(); - /** - * Can the creator limit the number of selected choices for a vote? - * It makes sense to limit the number of choice for the normal vote, but not for Condorcet - * - * @return {@code true} if the type of vote counting accept a limited number of choice, {@code false} otherwise. - */ - boolean canLimitNumberOfChoices(); - /** * Gets the minimum value for the vote value to be valid. * If null, then it means no minimum value. @@ -162,10 +99,12 @@ public interface VoteCounting<S extends VoteCountingStrategy<C>, C extends VoteC Double getMinimumValue(); /** - * Gets the maximum value for the vote value to be valid. - * If null, then it means no maximum value. + * Check if vote vote is valid * - * @return the maximum value for the vote to be valid. + * @param vote vote to check + * @param config counting type configuration + * @param locale the locale used to render the error message + * @return the errorMaps (field, errors) empty if vote is OK */ - Double getMaximumValue(); + Multimap<String, String> checkVote(Voter vote, C config, Locale locale); } diff --git a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteForChoice.java b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteForChoice.java index ff58aa93..d259b525 100644 --- a/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteForChoice.java +++ b/pollen-votecounting-api/src/main/java/org/chorem/pollen/votecounting/model/VoteForChoice.java @@ -30,6 +30,10 @@ import java.io.Serializable; */ public class VoteForChoice implements ChoiceIdAble, Serializable { + public static final String PROPERTY_VOTE_VALUE = "voteValue"; + + public static final String PROPERTY_CHOICE_ID = "choiceId"; + private static final long serialVersionUID = 1L; diff --git a/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_en_GB.properties b/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_en_GB.properties index 4b3fea89..5c29547d 100644 --- a/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_en_GB.properties +++ b/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_en_GB.properties @@ -1 +1,2 @@ pollen.voteCountingType.help=%s Vote\: %s +pollen.voteCountingType.maxChoices.error.overflow=Too many choices diff --git a/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_fr_FR.properties b/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_fr_FR.properties index e2fa489c..c01cc713 100644 --- a/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_fr_FR.properties +++ b/pollen-votecounting-api/src/main/resources/i18n/pollen-votecounting-api_fr_FR.properties @@ -1 +1,2 @@ pollen.voteCountingType.help=Méthode %s \: %s +pollen.voteCountingType.maxChoices.error.overflow=Le nombre de choix maximal atteind diff --git a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCounting.java b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCounting.java index 64a6699c..82eaf891 100644 --- a/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCounting.java +++ b/pollen-votecounting-borda/src/main/java/org/chorem/pollen/votecounting/BordaVoteCounting.java @@ -21,7 +21,9 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; +import org.chorem.pollen.votecounting.model.VoteForChoice; import java.util.Locale; @@ -34,7 +36,7 @@ import static org.nuiton.i18n.I18n.n; * @author Tony Chemit - dev@tchemit.fr * @since 1.6 */ -public class BordaVoteCounting extends AbstractVoteCounting<BordaVoteCountingStrategy, BordaConfig> { +public class BordaVoteCounting extends AbstractVoteCountingMaxChoice<BordaVoteCountingStrategy, BordaConfig> { public static final int ID = 5; @@ -48,59 +50,28 @@ public class BordaVoteCounting extends AbstractVoteCounting<BordaVoteCountingStr ); } - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, BordaConfig config) { - // no validation on total value, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - BordaConfig config) { - return l(locale, "pollen.error.vote.invalidBordaVoteValue", - choiceNumber, voteValue); - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 1d; } @Override - public Double getMaximumValue() { - return null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, BordaConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue > 0 && voteValue < 100; - } + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue != null && voteValue < 0) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.borda.voteValue.error.positive", voteValue)); + } - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null; - } + return errorMap; - @Override - public boolean isVoteValueValid(Double voteValue, BordaConfig config) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, BordaConfig config) { - // no validation on total value - return true; } } diff --git a/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_en_GB.properties b/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_en_GB.properties index 72361b4f..decaf53f 100644 --- a/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_en_GB.properties +++ b/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_en_GB.properties @@ -1,4 +1,4 @@ -pollen.error.vote.invalidBordaVoteValue=The value '%2$s' of vote for choice n° %1$d is not valid, only integer values are authorized. pollen.voteCountingType.borda=Borda pollen.voteCountingType.borda.help=Rank choices by preference order from 1 to N (1\=favorite).<br/>Only the rank is taken into account, not the values. Two choices can have the same value.<br/>For each vote, points are won by the choices according to their rank (like for the Eurovision contest); the winner is the choice which have the most points.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Borda_count' target\='\#doc'>http\://en.wikipedia.org/wiki/Borda_count</a> pollen.voteCountingType.borda.shortHelp=Rank choices by preference order from 1 to N (1\=favorite). +pollen.voteCountingType.borda.voteValue.error.positive=The value %1$s must be positive. diff --git a/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_fr_FR.properties b/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_fr_FR.properties index eb080073..fb03e17a 100644 --- a/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_fr_FR.properties +++ b/pollen-votecounting-borda/src/main/resources/i18n/pollen-votecounting-borda_fr_FR.properties @@ -1,4 +1,4 @@ -pollen.error.vote.invalidBordaVoteValue=La valeur du vote '%2$s' pour le choix n° %1$d n'est pas correcte, seul des valeurs entières sont autorisées. pollen.voteCountingType.borda=Borda pollen.voteCountingType.borda.help=Classer les choix par ordre de préférence de 1 à N (1\=préféré).<br/> Seul l'ordre des choix compte, peu importe les valeurs. Deux choix peuvent avoir la même valeur.<br/>Pour chaque vote, on attribue des points aux choix en fonction de leur rang (comme pour le concours de l'Eurovision) ; le gagnant est le choix avec le plus de points.<br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/M%C3%A9thode_Borda' target\='\#doc'>http\://fr.wikip [...] pollen.voteCountingType.borda.shortHelp=Classer les choix par ordre de préférence de 1 à N (1\=préféré). +pollen.voteCountingType.borda.voteValue.error.positive=La valeur du vote %21$s doit être positive. diff --git a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCounting.java b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCounting.java index ced470a2..abae0285 100644 --- a/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCounting.java +++ b/pollen-votecounting-condorcet/src/main/java/org/chorem/pollen/votecounting/CondorcetVoteCounting.java @@ -21,8 +21,10 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.EmptyVoteCountingConfig; +import org.chorem.pollen.votecounting.model.VoteForChoice; import java.util.Locale; @@ -49,59 +51,32 @@ public class CondorcetVoteCounting extends AbstractVoteCounting<CondorcetVoteCou ); } - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, EmptyVoteCountingConfig config) { - // no validation on total value, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - EmptyVoteCountingConfig config) { - return l(locale, "pollen.error.vote.invalidCondorcetVoteValue", - choiceNumber, voteValue); - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 1d; } @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, EmptyVoteCountingConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); + + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue == null) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.condorcet.voteValue.error.required")); + } else if (voteValue < 0) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.condorcet.voteValue.error.positive", voteValue)); + } + + return errorMap; - @Override - public boolean isVoteValueValid(Double voteValue, EmptyVoteCountingConfig config) { - return voteValue == null || voteValue > 0; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, EmptyVoteCountingConfig config) { - // no validation on total value - return true; } } diff --git a/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_en_GB.properties b/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_en_GB.properties index c322f441..06afd681 100644 --- a/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_en_GB.properties +++ b/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_en_GB.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidCondorcetVoteValue=The value '%2$d' of vote for choice n° %1$d is not valid, only integer values are authorized. pollen.voteCountingType.condorcet=Condorcet pollen.voteCountingType.condorcet.help=Rank choices by preference order from 1 to N (1\=favorite).<br/>Only the rank is taken into account, not the values. Two choices can have the same value.<br/>In this method, the winner is the choice which wins the most duels against the other choices.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Condorcet_method' target\='\#doc'>http\://en.wikipedia.org/wiki/Condorcet_method</a> pollen.voteCountingType.condorcet.shortHelp=Rank choices by preference order from 1 to N (1\=favorite). +pollen.voteCountingType.condorcet.voteValue.error.positive=The value %1$s must be positive. +pollen.voteCountingType.condorcet.voteValue.error.required=The value is required. diff --git a/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_fr_FR.properties b/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_fr_FR.properties index 4b95875c..b748948c 100644 --- a/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_fr_FR.properties +++ b/pollen-votecounting-condorcet/src/main/resources/i18n/pollen-votecounting-condorcet_fr_FR.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidCondorcetVoteValue=La valeur du vote '%2$d' pour le choix n° %1$d n'est pas correcte, seul des valeurs entières sont autorisées. pollen.voteCountingType.condorcet=Condorcet pollen.voteCountingType.condorcet.help=Classer les choix par ordre de préférence de 1 à N (1\=préféré).<br/>Seul l'ordre des choix compte, peu importe les valeurs. Deux choix peuvent avoir la même valeur.<br/>Dans cette méthode, le gagnant est celui qui remporte le plus de duels par rapport aux autres choix.<br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/M%C3%A9thode_Condorcet' target\='\#doc'>http\://fr.wikipedia.org/wiki/Méthode_Condorcet</a> pollen.voteCountingType.condorcet.shortHelp=Classer les choix par ordre de préférence de 1 à N (1\=préféré). +pollen.voteCountingType.condorcet.voteValue.error.positive=La valeur du vote %1$s doit être positive. +pollen.voteCountingType.condorcet.voteValue.error.required=La valeur du vote est obligatoire. diff --git a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCounting.java b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCounting.java index 6606e1f9..d92ede32 100644 --- a/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCounting.java +++ b/pollen-votecounting-coombs/src/main/java/org/chorem/pollen/votecounting/CoombsVoteCounting.java @@ -21,8 +21,10 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.EmptyVoteCountingConfig; +import org.chorem.pollen.votecounting.model.VoteForChoice; import java.util.Locale; @@ -49,60 +51,32 @@ public class CoombsVoteCounting extends AbstractVoteCounting<CoombsVoteCountingS ); } - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, EmptyVoteCountingConfig config) { - // no validation on total value, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - EmptyVoteCountingConfig config) { - return l(locale, "pollen.error.vote.invalidCoombsVoteValue", - choiceNumber, - voteValue); - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 1d; } @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue > 0 && voteValue < 100; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, EmptyVoteCountingConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); + + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue == null) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.coombs.voteValue.error.required")); + } else if (voteValue < 0) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.coombs.voteValue.error.positive", voteValue)); + } + + return errorMap; - @Override - public boolean isVoteValueValid(Double voteValue, EmptyVoteCountingConfig config) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, EmptyVoteCountingConfig config) { - // no validation on total value - return true; } } diff --git a/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_en_GB.properties b/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_en_GB.properties index cce09ceb..e1b3a9dc 100644 --- a/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_en_GB.properties +++ b/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_en_GB.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidCoombsVoteValue=The value '%2$d' of vote for choice n° %1$d is not valid, only integer values are authorized. pollen.voteCountingType.coombs=Coombs pollen.voteCountingType.coombs.help=Rank choices by preference order from 1 to N (1\=favorite).<br/>Only the rank is taken into account, not the values. Two choices can have the same value.<br/>In this method, the choice which is last the most times is eliminated round by round until only one remains.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Coombs%27_method' target\='\#doc'>http\://en.wikipedia.org/wiki/Coombs_method</a> pollen.voteCountingType.coombs.shortHelp=Rank choices by preference order from 1 to N (1\=favorite). +pollen.voteCountingType.coombs.voteValue.error.positive=The value %1$s must be positive. +pollen.voteCountingType.coombs.voteValue.error.required=The value is required. diff --git a/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_fr_FR.properties b/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_fr_FR.properties index 1c689915..96ecb8a2 100644 --- a/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_fr_FR.properties +++ b/pollen-votecounting-coombs/src/main/resources/i18n/pollen-votecounting-coombs_fr_FR.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidCoombsVoteValue=La valeur du vote '%2$d' pour le choix n° %1$d n'est pas correcte, seul des valeurs entières sont autorisées. pollen.voteCountingType.coombs=Coombs pollen.voteCountingType.coombs.help=Classer les choix par ordre de préférence de 1 à N (1\=préféré).<br/>Seul l'ordre des choix compte, peu importe les valeurs. Deux choix peuvent avoir la même valeur.<br/>Dans cette méthode, le choix qui arrive le plus souvent en dernier de liste est éliminé à chaque tour, jusqu'à ce qu'il n'en reste qu'un.<br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/M%C3%A9thode_de_Coombs' target\='\#doc'>http\://fr.wikipedia.org/wiki/Méthode_de_ [...] pollen.voteCountingType.coombs.shortHelp=Classer les choix par ordre de préférence de 1 à N (1\=préféré). +pollen.voteCountingType.coombs.voteValue.error.positive=La valeur du vote %1$s doit être positive. +pollen.voteCountingType.coombs.voteValue.error.required=La valeur du vote est obligatoire. diff --git a/pollen-votecounting-cumulative/src/main/java/org/chorem/pollen/votecounting/CumulativeVoteCounting.java b/pollen-votecounting-cumulative/src/main/java/org/chorem/pollen/votecounting/CumulativeVoteCounting.java index 26f4da3f..4bf905ee 100644 --- a/pollen-votecounting-cumulative/src/main/java/org/chorem/pollen/votecounting/CumulativeVoteCounting.java +++ b/pollen-votecounting-cumulative/src/main/java/org/chorem/pollen/votecounting/CumulativeVoteCounting.java @@ -21,9 +21,13 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; +import org.chorem.pollen.votecounting.model.VoteForChoice; +import org.chorem.pollen.votecounting.model.Voter; import java.util.Locale; +import java.util.Objects; import static org.nuiton.i18n.I18n.l; import static org.nuiton.i18n.I18n.n; @@ -48,59 +52,54 @@ public class CumulativeVoteCounting extends AbstractVoteCounting<CumulativeVoteC ); } - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue >= 0 && voteValue <= 100; - } - - - @Override - public boolean isVoteValueNull(Double value) { - return value == null || value == 0; - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 0d; } @Override - public Double getMaximumValue() { - return null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, CumulativeConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); - @Override - public boolean isVoteValueValid(Double voteValue, CumulativeConfig cumulativeConfig) { - return voteValue == null || 0 <= voteValue && voteValue <= cumulativeConfig.getPoints(); - } + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue != null) { + if (voteValue < 0) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.cumulative.voteValue.error.positive", voteValue)); - @Override - public boolean isTotalVoteValueValid(Double totalValues, CumulativeConfig cumulativeConfig) { - return totalValues == null || totalValues == cumulativeConfig.getPoints(); - } + } else if (voteValue > config.getPoints()) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.cumulative.voteValue.error.max", voteValue, config.getPoints())); + } + } + + return errorMap; - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - CumulativeConfig cumulativeConfig) { - // no validation on not null vote value, so no message - return l(locale, "pollen.error.voteValue.cumulative", choiceNumber, voteValue == null ? "" : voteValue.intValue(), cumulativeConfig.getPoints()); } @Override - public String getTotalVoteValueNotValidMessage(Locale locale, CumulativeConfig cumulativeConfig) { - return l(locale, "pollen.error.vote.cumulative", cumulativeConfig.getPoints()); - } + public Multimap<String, String> checkVote(Voter vote, CumulativeConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVote(vote, config, locale); + + double totalValues = vote.getVoteForChoices() + .stream() + .mapToDouble(VoteForChoice::getVoteValue) + .filter(Objects::nonNull) + .sum(); + if (totalValues != config.getPoints()) { + errorMap.put( + "totalValues", + l(locale, "pollen.voteCountingType.cumulative.totalValue.error.equal", totalValues, config.getPoints())); + } + + return errorMap; + } } diff --git a/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_en_GB.properties b/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_en_GB.properties index 66965116..341e51b8 100644 --- a/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_en_GB.properties +++ b/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_en_GB.properties @@ -1,5 +1,6 @@ -pollen.error.vote.cumulative=choices sum must be equals to %1$d -pollen.error.voteValue.cumulative=The value '%2$d' of vote for choice n° %1$s must be between 0 and %3$s pollen.voteCountingType.cumulative=Cumulative pollen.voteCountingType.cumulative.help=Each voter has a given number of points. He distributes this number of points freely between all choices. The choice of the most points wins.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Cumulative_voting' target\='\#doc'>http\://en.wikipedia.org/wiki/Cumulative_voting</a> pollen.voteCountingType.cumulative.shortHelp=Allocate your points between all choices. +pollen.voteCountingType.cumulative.totalValue.error.equal=choices sum (%1$s) must be equals to %2$s. +pollen.voteCountingType.cumulative.voteValue.error.max=The value of vote %1$s must be less than or equal to %2$s. +pollen.voteCountingType.cumulative.voteValue.error.positive=The value of vote %1$s must be positive. diff --git a/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_fr_FR.properties b/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_fr_FR.properties index 240a6e34..677439b3 100644 --- a/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_fr_FR.properties +++ b/pollen-votecounting-cumulative/src/main/resources/i18n/pollen-votecounting-cumulative_fr_FR.properties @@ -1,5 +1,6 @@ -pollen.error.vote.cumulative=La somme de tous les choix doit être égale à %1$d -pollen.error.voteValue.cumulative=La valeur du vote '%2$s' pour le choix n° %1$s doit être comprise entre 0 et %3$s (inclus) pollen.voteCountingType.cumulative=Cumulatif pollen.voteCountingType.cumulative.help=Chaque votant possède un nombre de points donnés. Il distribue ce nombre de points librement entre tous les choix. Le choix récoltant le plus de points gagne. <br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/Vote_cumulatif' target\='\#doc'>http\://fr.wikipedia.org/wiki/Vote_cumulatif</a> pollen.voteCountingType.cumulative.shortHelp=Répartir vos points entre tous les choix. +pollen.voteCountingType.cumulative.totalValue.error.equal=La somme de tous les choix (%1$s) doit être égale à %2$s. +pollen.voteCountingType.cumulative.voteValue.error.max=La valeur du vote %1$s doit être inférieur ou égal à %2$s. +pollen.voteCountingType.cumulative.voteValue.error.positive=La valeur du vote %1$s doit être positive. diff --git a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCounting.java b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCounting.java index 86ae082f..7a31003d 100644 --- a/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCounting.java +++ b/pollen-votecounting-instant-runoff/src/main/java/org/chorem/pollen/votecounting/InstantRunoffVoteCounting.java @@ -21,8 +21,10 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.EmptyVoteCountingConfig; +import org.chorem.pollen.votecounting.model.VoteForChoice; import java.util.Locale; @@ -49,60 +51,32 @@ public class InstantRunoffVoteCounting extends AbstractVoteCounting<InstantRunof ); } - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, EmptyVoteCountingConfig config) { - // no validation on total value, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - EmptyVoteCountingConfig config) { - return l(locale, "pollen.error.vote.invalidInstantRunoffVoteValue", - choiceNumber, - voteValue); - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 1d; } @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, EmptyVoteCountingConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); + + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue == null) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.instantRunoff.voteValue.error.required")); + } else if (voteValue < 0) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.instantRunoff.voteValue.error.positive", voteValue)); + } + + return errorMap; - @Override - public boolean isVoteValueValid(Double voteValue, EmptyVoteCountingConfig config) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, EmptyVoteCountingConfig config) { - // no validation on total value - return true; } } diff --git a/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_en_GB.properties b/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_en_GB.properties index 54952136..a73304be 100644 --- a/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_en_GB.properties +++ b/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_en_GB.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidInstantRunoffVoteValue=The value '%2$s' of vote for choice n° %1$d is not valid, only integer values are authorized. pollen.voteCountingType.instantRunoff=Instant Runoff pollen.voteCountingType.instantRunoff.help=Rank choices by preference order from 1 to N (1\=favorite).<br/>Only the rank is taken into account, not the values. Two choices can have the same value.<br/>In this method, the choice which is first the least times is eliminated round by round until only one remains.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Instant-runoff_voting' target\='\#doc'>http\://en.wikipedia.org/wiki/Instant-runoff_voting</a> pollen.voteCountingType.instantRunoff.shortHelp=Rank choices by preference order from 1 to N (1\=favorite). +pollen.voteCountingType.instantRunoff.voteValue.error.positive=The value %1$s must be positive. +pollen.voteCountingType.instantRunoff.voteValue.error.required=The value is required. diff --git a/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_fr_FR.properties b/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_fr_FR.properties index 379523e2..9d3301e2 100644 --- a/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_fr_FR.properties +++ b/pollen-votecounting-instant-runoff/src/main/resources/i18n/pollen-votecounting-instant-runoff_fr_FR.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidInstantRunoffVoteValue=La valeur du vote '%2$s' pour le choix n° %1$d n'est pas correcte, seul des valeurs entières sont autorisées. pollen.voteCountingType.instantRunoff=Vote alternatif pollen.voteCountingType.instantRunoff.help=Classer les choix par ordre de préférence de 1 à N (1\=préféré).<br/>Seul l'ordre des choix compte, peu importe les valeurs. Deux choix peuvent avoir la même valeur.<br/>Dans cette méthode, le choix qui arrive le moins souvent en premier de liste est éliminé à chaque tour, jusqu'à ce qu'il n'en reste qu'un.<br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/Vote_alternatif' target\='\#doc'>http\://fr.wikipedia.org/wiki/Vote_alter [...] pollen.voteCountingType.instantRunoff.shortHelp=Classer les choix par ordre de préférence de 1 à N (1\=préféré). +pollen.voteCountingType.instantRunoff.voteValue.error.positive=La valeur du vote %1$s doit être positive. +pollen.voteCountingType.instantRunoff.voteValue.error.required=La valeur du vote est obligatoire. diff --git a/pollen-votecounting-majority-judgment/src/main/java/org/chorem/pollen/votecounting/MajorityJudgmentVoteCounting.java b/pollen-votecounting-majority-judgment/src/main/java/org/chorem/pollen/votecounting/MajorityJudgmentVoteCounting.java index 7bef418b..e6748278 100644 --- a/pollen-votecounting-majority-judgment/src/main/java/org/chorem/pollen/votecounting/MajorityJudgmentVoteCounting.java +++ b/pollen-votecounting-majority-judgment/src/main/java/org/chorem/pollen/votecounting/MajorityJudgmentVoteCounting.java @@ -21,7 +21,9 @@ package org.chorem.pollen.votecounting; * #L% */ +import com.google.common.collect.Multimap; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; +import org.chorem.pollen.votecounting.model.VoteForChoice; import java.util.Locale; @@ -45,59 +47,32 @@ public class MajorityJudgmentVoteCounting extends AbstractVoteCounting<MajorityJ ); } - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, MajorityJudgmentConfig config) { - // no validation on total value, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - MajorityJudgmentConfig config) { - return l(locale, "pollen.error.vote.invalidMajorityJudgmentVoteValue", - choiceNumber); - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.SELECT; } - @Override - public boolean canLimitNumberOfChoices() { - return false; - } - @Override public Double getMinimumValue() { return 0d; } @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue >= 0; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null; - } + public Multimap<String, String> checkVoteForChoice(VoteForChoice voteForChoice, MajorityJudgmentConfig config, Locale locale) { + Multimap<String, String> errorMap = super.checkVoteForChoice(voteForChoice, config, locale); + + Double voteValue = voteForChoice.getVoteValue(); + if (voteValue == null) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.majorityJudgment.voteValue.error.gradeRequired")); + } else if ((voteValue != Math.floor(voteValue)) || voteValue < 0 || voteValue >= config.getGrades().size()) { + errorMap.put( + VoteForChoice.PROPERTY_VOTE_VALUE, + l(locale, "pollen.voteCountingType.majorityJudgment.voteValue.error.gradeUnknown", voteValue)); + } + + return errorMap; - @Override - public boolean isVoteValueValid(Double voteValue, MajorityJudgmentConfig config) { - return voteValue != null && voteValue >= 0; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, MajorityJudgmentConfig config) { - // no validation on total value - return true; } } diff --git a/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_en_GB.properties b/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_en_GB.properties index 77c47c20..b908c376 100644 --- a/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_en_GB.properties +++ b/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_en_GB.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidMajorityJudgmentVoteValue=Grade for the choice n° %1$d is required pollen.voteCountingType.majorityJudgment=Majority judgment pollen.voteCountingType.majorityJudgment.help=Voters freely grade each candidate in one of several named ranks. The choice with the highest median grade is the winner.<br/>More about this method\: <a href\='http\://en.wikipedia.org/wiki/Majority_judgment' target\='\#doc'>http\://en.wikipedia.org/wiki/Majority_judgment</a> pollen.voteCountingType.majorityJudgment.shortHelp=Voters freely grade each choice in one of several named ranks. +pollen.voteCountingType.majorityJudgment.voteValue.error.gradeRequired=Grade is required. +pollen.voteCountingType.majorityJudgment.voteValue.error.gradeUnknown=No grade is defined for the value %1$s. diff --git a/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_fr_FR.properties b/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_fr_FR.properties index 95b76f8a..602e64bf 100644 --- a/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_fr_FR.properties +++ b/pollen-votecounting-majority-judgment/src/main/resources/i18n/pollen-votecounting-majority-judgment_fr_FR.properties @@ -1,4 +1,5 @@ -pollen.error.vote.invalidMajorityJudgmentVoteValue=La mention pour le choix n° %1$d est obligatoire. pollen.voteCountingType.majorityJudgment=Jugement majoritaire pollen.voteCountingType.majorityJudgment.help=Attribuer à chaque choix une mention parmi une échelle commune à tous.<br/>Deux choix peuvent avoir la même mention. Pour chaque choix, on détermine la mention médiane, qu'on appelle « mention majoritaire ».<br/>Le choix avec la meilleur mention majoritaire gagne.<br/>Pour en savoir plus \: <a href\='http\://fr.wikipedia.org/wiki/Jugement_majoritaire' target\='\#doc'>http\://fr.wikipedia.org/wiki/Jugement_majoritaire</a> pollen.voteCountingType.majorityJudgment.shortHelp=Attribuer à chaque choix une mention parmi une échelle commune à tous. +pollen.voteCountingType.majorityJudgment.voteValue.error.gradeRequired=La mention est obligatoire. +pollen.voteCountingType.majorityJudgment.voteValue.error.gradeUnknown=Aucune mention n'est définie pour la valeur %1$s. diff --git a/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCounting.java b/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCounting.java index 8890a75b..2b2a68ae 100644 --- a/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCounting.java +++ b/pollen-votecounting-normal/src/main/java/org/chorem/pollen/votecounting/NormalVoteCounting.java @@ -24,8 +24,6 @@ package org.chorem.pollen.votecounting; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.MaxChoicesNumberConfig; -import java.util.Locale; - import static org.nuiton.i18n.I18n.n; /** @@ -34,7 +32,7 @@ import static org.nuiton.i18n.I18n.n; * @author Tony Chemit - dev@tchemit.fr * @since 1.6 */ -public class NormalVoteCounting extends AbstractVoteCounting<NormalVoteCountingStrategy, MaxChoicesNumberConfig> { +public class NormalVoteCounting extends AbstractVoteCountingMaxChoice<NormalVoteCountingStrategy, MaxChoicesNumberConfig> { public static final int ID = 1; @@ -48,61 +46,13 @@ public class NormalVoteCounting extends AbstractVoteCounting<NormalVoteCountingS ); } - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue > 0; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null || voteValue == 0; - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.CHECKBOX; } - @Override - public boolean canLimitNumberOfChoices() { - return true; - } - @Override public Double getMinimumValue() { return null; } - - @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isVoteValueValid(Double voteValue, MaxChoicesNumberConfig config) { - // no validation on not null vote value - return true; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, MaxChoicesNumberConfig config) { - // no validation on total value - return true; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - MaxChoicesNumberConfig config) { - // no validation on not null vote value, so no message - return null; - } - - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, MaxChoicesNumberConfig config) { - // no validation on total values, so no message - return null; - } - } 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 c65455ac..9301068c 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 @@ -45,7 +45,7 @@ public class NormalVoteCountingStrategy extends AbstractVoteCountingStrategy<Max public VoteCountingResult votecount(Set<Voter> voters) { // get empty result by choice - Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters); + Map<String, ChoiceScore> scores = newEmptyChoiceScoreMap(voters, ZERO_D); for (Voter voter : voters) { @@ -64,16 +64,13 @@ public class NormalVoteCountingStrategy extends AbstractVoteCountingStrategy<Max for (VoteForChoice voteForChoice : voter.getVoteForChoices()) { Double voteValue = voteForChoice.getVoteValue(); - if (voteValue != null) { + if (voteValue != null && voteValue != 0) { // get score for choice ChoiceScore score = scores.get(voteForChoice.getChoiceId()); - // compute score to add - double scoreToAdd = voteValue * voterWeight; - // add to score this weighted vote - score.addScoreValue(scoreToAdd); + score.addScoreValue(voterWeight); } } } diff --git a/pollen-votecounting-normal/src/test/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategyTest.java b/pollen-votecounting-normal/src/test/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategyTest.java index 2650b970..afca95a7 100644 --- a/pollen-votecounting-normal/src/test/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategyTest.java +++ b/pollen-votecounting-normal/src/test/java/org/chorem/pollen/votecounting/NormalVoteCountingStrategyTest.java @@ -197,7 +197,7 @@ public class NormalVoteCountingStrategyTest { .containsExactlyInAnyOrder( ChoiceScore.newScore(CHOICE_A, BigDecimal.valueOf(3.0), 0), ChoiceScore.newScore(CHOICE_B, BigDecimal.valueOf(2.0), 1), - ChoiceScore.newScore(CHOICE_C, null, 2)) + ChoiceScore.newScore(CHOICE_C, BigDecimal.valueOf(0.0), 2)) .isSortedAccordingTo(ChoiceScore::compareTo); } @@ -233,7 +233,7 @@ public class NormalVoteCountingStrategyTest { .containsExactlyInAnyOrder( ChoiceScore.newScore(CHOICE_A, BigDecimal.valueOf(3.0), 0), ChoiceScore.newScore(CHOICE_B, BigDecimal.valueOf(3.0), 0), - ChoiceScore.newScore(CHOICE_C, null, 1)) + ChoiceScore.newScore(CHOICE_C, BigDecimal.valueOf(0.0), 1)) .isSortedAccordingTo(ChoiceScore::compareTo); } diff --git a/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCounting.java b/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCounting.java index bb04ae78..72d2fcdf 100644 --- a/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCounting.java +++ b/pollen-votecounting-number/src/main/java/org/chorem/pollen/votecounting/NumberVoteCounting.java @@ -24,8 +24,6 @@ package org.chorem.pollen.votecounting; import org.chorem.pollen.votecounting.model.ChoiceToVoteRenderType; import org.chorem.pollen.votecounting.model.MaxChoicesNumberConfig; -import java.util.Locale; - import static org.nuiton.i18n.I18n.n; /** @@ -34,7 +32,7 @@ import static org.nuiton.i18n.I18n.n; * @author Tony Chemit - dev@tchemit.fr * @since 1.6 */ -public class NumberVoteCounting extends AbstractVoteCounting<NumberVoteCountingStrategy, MaxChoicesNumberConfig> { +public class NumberVoteCounting extends AbstractVoteCountingMaxChoice<NumberVoteCountingStrategy, MaxChoicesNumberConfig> { public static final int ID = 4; @@ -48,61 +46,13 @@ public class NumberVoteCounting extends AbstractVoteCounting<NumberVoteCountingS ); } - @Override - public boolean isChoiceInVote(Double voteValue) { - return voteValue != null && voteValue >= 0; - } - - @Override - public boolean isVoteValueNull(Double voteValue) { - return voteValue == null || voteValue == 0; - } - @Override public ChoiceToVoteRenderType getVoteValueEditorType() { return ChoiceToVoteRenderType.TEXTFIELD; } - @Override - public boolean canLimitNumberOfChoices() { - return true; - } - @Override public Double getMinimumValue() { return null; } - - @Override - public Double getMaximumValue() { - return null; - } - - @Override - public boolean isVoteValueValid(Double voteValue, MaxChoicesNumberConfig config) { - // no validation on not null vote value - return true; - } - - @Override - public boolean isTotalVoteValueValid(Double totalValues, MaxChoicesNumberConfig config) { - // no validation on total value - return true; - } - - @Override - public String getTotalVoteValueNotValidMessage(Locale locale, MaxChoicesNumberConfig config) { - // no validation on total values, so no message - return null; - } - - @Override - public String getVoteValueNotValidMessage(Locale locale, - int choiceNumber, - Double voteValue, - MaxChoicesNumberConfig config) { - // no validation on not null vote value, so no message - return null; - } - } -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.