From 94d1d61fd2aa5840a97e9473ce63f21a0739abd8 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Mon, 14 Apr 2014 09:15:57 -0300 Subject: [PATCH] added check for duplicated voting --- luncho/blueprints/voting.py | 97 +++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/luncho/blueprints/voting.py b/luncho/blueprints/voting.py index a62f379..52758fe 100644 --- a/luncho/blueprints/voting.py +++ b/luncho/blueprints/voting.py @@ -86,6 +86,30 @@ class PlaceDoesntBelongToGroupException(LunchoException): self.json['places'] = self.places +class PlacesVotedMoreThanOnceException(LunchoException): + """The indicated places were voted more than once. Only a vote + per place is allowed. + + .. sourcecode:: http + HTTP/1.1 409 Conflict + Content-Type: text/json + + { "status": "ERROR", + "message": "Places voted more than once", + "places": [, , ...]} + """ + def __init__(self, places): + super(PlacesVotedMoreThanOnceException, self).__init__() + self.status = 409 + self.message = 'Places voted more than once' + self.places = places + return + + def _json(self): + super(PlacesVotedMoreThanOnceException, self)._json() + self.json['places'] = self.places + + # ---------------------------------------------------------------------- # Voting # ---------------------------------------------------------------------- @@ -107,42 +131,77 @@ def cast_vote(group_id): LOG.debug('User is not member') raise UserIsNotMemberException() + choices = request.as_json.get('choices') + # check if the user voted today already, for any group + _already_voted(request.user.username) + + # check if the user is trying to vote in the same place twice + _check_duplicates(choices) + + # check the number of votes the user casted + _check_place_count(choices, group.places) + + # check if the places exist and are part of the group + # (don't vote yet, so we can stop the whole thing if there is anything + # wrong) + _check_places(choices, group.places) + + # finally, cast the vote + vote = Vote(request.user, group_id) + db.session.add(vote) + db.session.commit() # so vote gets an id + for (pos, place_id) in enumerate(request.as_json.get('choices')): + place = CastedVote(vote, pos, place_id) + db.session.add(place) + + db.session.commit() + + return jsonify(status='OK') + + +# ---------------------------------------------------------------------- +# Helpers +# ---------------------------------------------------------------------- + +def _already_voted(username): + """Check if the user already voted today.""" today = datetime.date.today() - today_vote = Vote.query.filter_by(user=request.user.username, + today_vote = Vote.query.filter_by(user=username, created_at=today).first() if today_vote: LOG.debug('User already voted today') raise VoteAlreadyCastException() + return - # check the number of votes the user casted - choices = request.as_json.get('choices') + +def _check_place_count(choices, group_places): + """Check if the user voted in the right number of places.""" + # maybe the group have less than PLACES_IN_VOTE choices... max_places = min(current_app.config['PLACES_IN_VOTE'], - len(group.places)) + len(group_places)) if len(choices) != max_places: LOG.debug('Max places = {max_places}, voted for {choices}', max_places=max_places, choices=len(choices)) raise InvalidNumberOfPlacesCastedException() + return - # check if the places exist and are part of the group - # (don't vote yet, so we can stop the whole thing if there is anything - # wrong) - for place_id in request.as_json.get('choices'): + +def _check_places(choices, group_places): + """Check if the places the user voted exist and belong to the group.""" + for place_id in choices: place = Place.query.get(place_id) if not place: raise ElementNotFoundException('Place') - if not place in group.places: + if place not in group_places: raise PlaceDoesntBelongToGroupException(place_id) + return - # finally, cast the vote - vote = Vote(request.user, group_id) - db.session.add(vote) - db.session.commit() # so vote gets an id - for (pos, place_id) in enumerate(request.as_json.get('choices')): - place = CastedVote(vote, pos, place_id) - db.session.add(place) - - db.session.commit() - return jsonify(status='OK') +def _check_duplicates(choices): + """Check if the places the user voted are listed more than once.""" + duplicates = set([x for x in choices if choices.count(x) > 1]) + if len(duplicates) > 0: + raise PlacesVotedMoreThanOnceException(duplicates) + return