def _prepare_round_params(coord_dao, request_dict): rnd_dict = {} req_columns = ['jurors', 'name', 'vote_method', 'deadline_date'] extra_columns = ['description', 'config', 'directions', 'show_stats'] valid_vote_methods = ['ranking', 'rating', 'yesno'] for column in req_columns + extra_columns: val = request_dict.get(column) if not val and column in req_columns: raise InvalidAction('%s is required to create a round (got %r)' % (column, val)) if column is 'vote_method' and val not in valid_vote_methods: raise InvalidAction('%s is an invalid vote method' % val) if column is 'deadline_date': val = js_isoparse(val) if column is 'jurors': juror_names = val rnd_dict[column] = val default_quorum = len(rnd_dict['jurors']) rnd_dict['quorum'] = request_dict.get('quorum', default_quorum) rnd_dict['jurors'] = [] for juror_name in juror_names: juror = coord_dao.get_or_create_user(juror_name, 'juror') rnd_dict['jurors'].append(juror) return rnd_dict
def finalize_campaign(user_dao, campaign_id): # TODO: add some docs coord_dao = CoordinatorDAO.from_campaign(user_dao, campaign_id) last_rnd = coord_dao.campaign.active_round if not last_rnd: raise InvalidAction('no active rounds') if last_rnd.vote_method != 'ranking': raise InvalidAction('only ranking rounds can be finalized') campaign_summary = coord_dao.finalize_ranking_round(last_rnd.id) coord_dao.finalize_campaign() return campaign_summary
def autodisqualify(user_dao, round_id, request_dict): coord_dao = CoordinatorDAO.from_round(user_dao, round_id) rnd = coord_dao.get_round(round_id) if rnd.status != 'paused': raise InvalidAction('round must be paused to disqualify entries') dq_by_upload_date = request_dict.get('dq_by_upload_date') dq_by_resolution = request_dict.get('dq_by_resolution') dq_by_uploader = request_dict.get('dq_by_uploader') dq_by_filetype = request_dict.get('dq_by_filetype') round_entries = [] if rnd.config.get('dq_by_upload_date') or dq_by_upload_date: dq_upload_date = coord_dao.autodisqualify_by_date(round_id) round_entries += dq_upload_date if rnd.config.get('dq_by_resolution') or dq_by_resolution: dq_resolution = coord_dao.autodisqualify_by_resolution(round_id) round_entries += dq_resolution if rnd.config.get('dq_by_uploader') or dq_by_uploader: dq_uploader = coord_dao.autodisqualify_by_uploader(round_id) round_entries += dq_uploader if rnd.config.get('dq_by_filetype') or dq_by_filetype: dq_filetype = coord_dao.autodisqualify_by_filetype(round_id) round_entries += dq_filetype data = [re.to_dq_details() for re in round_entries] return {'data': data}
def get_file_info_by_category(request_dict): try: category_name = request_dict['name'] except Exception: raise InvalidAction('missing name=category_name query parameter') files = get_files(category_name) return {'file_infos': files}
def get_files_info_by_name(request_dict): try: file_names = request_dict['names'] except Exception: raise InvalidAction('must provide a list of names') files = [] for file_name in file_names: file_info = get_file_info(file_name) files.append(file_info) return {'file_infos': files}
def skip_rating(user_dao, round_id, request, request_dict): juror_dao = JurorDAO(user_dao) try: vote_id = request_dict['vote_id'] except Exception as e: import pdb pdb.set_trace() raise InvalidAction('must provide skip id') juror_dao.skip_voting(vote_id) next_tasks = get_tasks_from_round(user_dao, round_id, request) return next_tasks
def create_campaign(user_dao, request_dict): """ Summary: Post a new campaign Request model: campaign_name: type: string Response model: AdminCampaignDetails """ org_dao = OrganizerDAO(user_dao) name = request_dict.get('name') if not name: raise InvalidAction('name is required to create a campaign, got %r' % name) now = datetime.datetime.utcnow().isoformat() open_date = request_dict.get('open_date', now) if open_date: open_date = js_isoparse(open_date) close_date = request_dict.get('close_date') if close_date: close_date = js_isoparse(close_date) url = request_dict['url'] series_id = request_dict.get('series_id', 1) coord_names = request_dict.get('coordinators') coords = [user_dao.user ] # Organizer is included as a coordinator by default for coord_name in coord_names: coord = org_dao.get_or_create_user(coord_name, 'coordinator') coords.append(coord) campaign = org_dao.create_campaign(name=name, open_date=open_date, close_date=close_date, series_id=series_id, url=url, coords=set(coords)) # TODO: need completion info for each round data = campaign.to_details_dict() return {'data': data}
def submit_rating(user_dao, request_dict): # TODO: Check permissions juror_dao = JurorDAO(user_dao) vote_id = request_dict['vote_id'] rating = float(request_dict['rating']) task = juror_dao.get_task(vote_id) rnd = task.round_entry.round rnd.confirm_active() if rnd.vote_method == 'rating': if rating not in VALID_RATINGS: raise InvalidAction('rating expected one of %s, not %r' % (VALID_RATINGS, rating)) elif rnd.vote_method == 'yesno': if rating not in VALID_YESNO: raise InvalidAction('rating expected one of %s, not %r' % (VALID_YESNO, rating)) if task.user != user: # TODO: this should be handled by the dao get raise PermissionDenied() if task.status == 'active': juror_dao.apply_rating(task, rating) # What should this return? return {'data': {'vote_id': vote_id, 'rating': rating}}
def get_round_results_preview(user_dao, round_id): coord_dao = CoordinatorDAO.from_round(user_dao, round_id) rnd = coord_dao.get_round(round_id) round_counts = rnd.get_count_map() is_closeable = round_counts['total_open_tasks'] == 0 data = { 'round': rnd.to_info_dict(), 'counts': round_counts, 'is_closeable': is_closeable } if rnd.vote_method in ('yesno', 'rating'): data['ratings'] = coord_dao.get_round_average_rating_map(round_id) try: data['thresholds'] = get_threshold_map(data['ratings']) except: # import pdb;pdb.post_mortem() raise elif rnd.vote_method == 'ranking': if not is_closeable: # TODO: What should this return for ranking rounds? The ranking # round is sorta an all-or-nothing deal, unlike the rating rounds # where you can take a peek at in-progress results # import pdb;pdb.set_trace() raise InvalidAction('round must be closeable to preview results') rankings = coord_dao.get_round_ranking_list(round_id) data['rankings'] = [r.to_dict() for r in rankings] else: raise NotImplementedError() return {'data': data}
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?