Пример #1
0
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
Пример #2
0
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
Пример #3
0
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}
Пример #4
0
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}
Пример #5
0
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}
Пример #6
0
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
Пример #7
0
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}
Пример #8
0
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}}
Пример #9
0
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}
Пример #10
0
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?