def get_round(user_dao, round_id): """ Summary: Get juror-level details for a round, identified by round ID. """ juror_dao = JurorDAO(user_dao) rnd = juror_dao.get_round(round_id) rnd_stats = juror_dao.get_round_task_counts(round_id) data = make_juror_round_details(rnd, rnd_stats) # TODO: add to Round model return {'data': data}
def get_rankings_from_round(user_dao, round_id, request): juror_dao = JurorDAO(user_dao) rnd = juror_dao.get_round(round_id) if rnd.vote_method != 'ranking': return { 'data': None, 'status': 'failure', 'errors': 'round %s is not a ranking round' % round_id } ret = get_votes_from_round(user_dao, round_id, request, rnd=rnd) return ret
def get_tasks_from_round(user_dao, round_id, request): count = request.values.get('count', 15) offset = request.values.get('offset', 0) juror_dao = JurorDAO(user_dao) juror_dao.confirm_active(round_id) rnd = juror_dao.get_round(round_id) if rnd.vote_method == 'ranking': count = MAX_RATINGS_SUBMIT # TODO: better constant tasks = juror_dao.get_tasks_from_round(round_id, num=count, offset=offset) stats = juror_dao.get_round_task_counts(round_id) data = {'stats': stats, 'tasks': []} for task in tasks: data['tasks'].append(task.to_details_dict()) return {'data': data}
def get_votes_from_round(user_dao, round_id, request, rnd=None): count = request.values.get('count', 15) offset = request.values.get('offset', 0) order_by = request.values.get('order_by', 'date') sort = request.values.get('sort', 'asc') juror_dao = JurorDAO(user_dao) if not rnd: rnd = juror_dao.get_round(round_id) if rnd.vote_method in ('yesno', 'rating'): ratings = juror_dao.get_ratings_from_round(round_id, num=count, offset=offset, sort=sort, order_by=order_by) data = [r.to_details_dict() for r in ratings] else: rankings = juror_dao.get_rankings_from_round(round_id) data = [r.to_details_dict() for r in rankings] data.sort(key=lambda x: x['value']) return {'data': data}
def get_votes_stats_from_round(user_dao, round_id): juror_dao = JurorDAO(user_dao) rnd = juror_dao.get_round(round_id) stats = None if rnd.vote_method == 'yesno': # dict: raw value => yes/no vote_map = {0.0: 'no', 1.0: 'yes'} stats = _get_summarized_votes_stats(juror_dao, round_id, vote_map) elif rnd.vote_method == 'rating': # dict: raw value => number of stars vote_map = {0.0: 1, 0.25: 2, 0.5: 3, 0.75: 4, 1.0: 5} stats = _get_summarized_votes_stats(juror_dao, round_id, vote_map) result = None if stats is not None: result = {'method': rnd.vote_method, 'stats': stats} return result
def submit_ratings(user_dao, request_dict): """message format: {"ratings": [{"vote_id": 10, "value": 0.0}, {"vote_id": 11, "value": 1.0}]} this function is used to submit ratings _and_ rankings. when submitting rankings does not support ranking ties at the moment """ # TODO: can jurors change their vote? juror_dao = JurorDAO(user_dao) r_dicts = request_dict['ratings'] if len(r_dicts) > MAX_RATINGS_SUBMIT: raise InvalidAction('can submit up to 100 ratings at once, not %r' % len(r_dicts)) elif not r_dicts: return {} # submitting no ratings = immediate return review_map = {} for rd in r_dicts: review = rd.get('review') if review: review_stripped = review.strip() task_id = rd.get('vote_id') or rd['task_id'] if len(review_stripped) > 8192: raise ValueError('review must be less than 8192 ' 'chars, not %r' % len(review_stripped)) review_map[task_id] = review_stripped try: id_map = dict([(r['vote_id'], r['value']) for r in r_dicts]) except KeyError as e: # fallback for old versions id_map = dict([(r['task_id'], r['value']) for r in r_dicts]) if not len(id_map) == len(r_dicts): pass # duplicate values tasks = juror_dao.get_tasks_by_id(id_map.keys()) task_map = dict([(t.id, t) for t in tasks]) round_id_set = set([t.round_entry.round_id for t in tasks]) if not len(round_id_set) == 1: raise InvalidAction('can only submit ratings for one round at a time') round_id = list(round_id_set)[0] rnd = juror_dao.get_round(round_id) rnd.confirm_active() style = rnd.vote_method # validation if style == 'rating': invalid = [r for r in id_map.values() if r not in VALID_RATINGS] if invalid: raise InvalidAction('rating expected one of %s, not %r' % (VALID_RATINGS, sorted(set(invalid)))) elif style == 'yesno': invalid = [r not in VALID_YESNO for r in id_map.values()] if not all([r in VALID_YESNO for r in id_map.values()]): raise InvalidAction('yes/no rating expected one of %s, not %r' % (VALID_YESNO, sorted(set(invalid)))) elif style == 'ranking': invalid = [r for r in id_map.values() if r != int(r) or r < 0] if invalid: raise InvalidAction('ranking expects whole numbers >= 0, not %r' % (sorted(set(invalid)))) ranks = sorted([int(v) for v in id_map.values()]) last_rank = max(ranks) len_rnd_entries = len(rnd.entries) max_ok_rank = len_rnd_entries - 1 if last_rank > max_ok_rank: raise InvalidAction('ranking for round #%s expects ranks 0 - %s,' ' not %s' % (rnd.id, max_ok_rank, last_rank)) if len_rnd_entries != len(id_map): raise InvalidAction('must submit all rankings at once.' ' (expected %s submissions, got %s.)' % (len_rnd_entries, len(id_map))) if style in ('rating', 'yesno'): for t in tasks: val = id_map[t.id] review = review_map.get(t.id) juror_dao.edit_rating(t, val, review=review) elif style == 'ranking': # This part is designed to support ties ok though """ [{"vote_id": 123, "value": 0, "review": "The light dances across the image."}] """ ballot = [] is_edit = False for rd in r_dicts: cur = dict(rd) vote_id = rd.get('vote_id') if not vote_id: vote_id = rd.get( 'task_id') # fallback for old versions of the client cur['vote'] = task_map[vote_id] if cur['vote'].status == 'complete': is_edit = True elif is_edit: raise InvalidAction('all tasks must be complete or incomplete') ballot.append(cur) juror_dao.apply_ranking(ballot) return {} # TODO?