Пример #1
0
def given_review_dict(context, pr, lang):
    tfd = UserDownload.objects.filter(user=pr.reviewer.student.user, assignment=pr.assignment)
    if tfd:
        tfd = tfd.first().time_downloaded
    else:
        tfd = DEFAULT_DATE
    r = {
        'object': pr,
        'lang': lang,
        'time_first_download': lang.ftime(tfd),
        'time_submitted': lang.ftime(pr.time_submitted),
        'rev_items': pr.item_list(),
        'pred_hex': encode(pr.assignment.id, context['user_session'].encoder)
    }
    # if predecessor has responded, additional fields must be added
    if pr.time_appraised != DEFAULT_DATE:
        r['time_appraised'] = lang.ftime(pr.time_appraised)
        r['app_icon'] = FACES[pr.appraisal]
        r['app_header'] = lang.phrase('Appraisal_header') % lang.phrase(['ERROR',
            'Quite_happy', 'Mixed_feelings', 'Unhappy'][pr.appraisal])
        r['imp_opinion'] = lang.phrase('Opinion_on_sucessor_version') % lang.phrase(['Pass',
            'No_improvement', 'Minor_changes', 'Good_job'][pr.improvement_appraisal])
    # also indicate whether student has acknowledged
    if pr.time_acknowledged != DEFAULT_DATE:
        r['time_acknowledged'] = lang.ftime(pr.time_acknowledged)
    # in case of an appeal, report the appeal status
    if pr.is_appeal:
        if pr.time_appeal_assigned != DEFAULT_DATE:
            r['time_assigned'] = lang.ftime(pr.time_appeal_assigned)
            ap = Appeal.objects.filter(review=pr).first()
            if ap:
                r['referee'] = prefixed_user_name(ap.referee.user)
                if ap.time_first_viewed != DEFAULT_DATE:
                    r['time_first_viewed'] = lang.ftime(ap.time_first_viewed)
                if ap.time_decided != DEFAULT_DATE:
                    r['time_decided'] = lang.ftime(ap.time_decided)
                    r['ref_rat'] = ap.grade
                    r['ref_motiv'] = ap.grade_motivation
                    r['penalties'] = lang.penalties_as_text(
                        ap.predecessor_penalty, ap.successor_penalty)
                # NOTE: in this case, the student ("you") is the successor ...
                if ap.time_acknowledged_by_successor != DEFAULT_DATE:
                    r['time_your_appr'] = lang.ftime(ap.time_acknowledged_by_successor)
                    r['your_appr_icon'] = FACES[ap.successor_appraisal]
                    r['your_motiv'] = ap.successor_motivation
                    r['you_objected'] = ap.is_contested_by_successor
                # ... and hence the other party the predecessor
                if ap.time_acknowledged_by_predecessor != DEFAULT_DATE:
                    r['time_other_appr'] = lang.ftime(ap.time_acknowledged_by_predecessor)
                    r['other_appr_icon'] = FACES[ap.predecessor_appraisal]
                    r['other_motiv'] = ap.predecessor_motivation
                    r['other_objected'] = ap.is_contested_by_predecessor
    return r
Пример #2
0
def enroll(request, **kwargs):
    context = generic_context(request)
    print context
    # check whether user can have student role
    if not change_role(context, 'Student'):
        return render(request, 'presto/forbidden.html', context)
    # check whether user is enrolling
    cc = kwargs.get('course', '')
    cl = Course.objects.filter(code=cc)
    if not cl:
        warn_user(
            context, 'Unknown course',
            'Course code "<tt>%s</tt>" is not recognized. Please check with your faculty staff.'
            % cc)
    elif cl.first().is_hidden:
        warn_user(context, 'Unknown course',
                  'Course %s is not open for enrollment.' % cl.first().title)
    else:
        c = cl.first()
        context['course'] = {
            'object': c,
            'hex': encode(c.id, context['user_session'].encoder)
        }
        # switch to the course language
        context['lang'] = c.language
        csl = CourseStudent.objects.filter(user=context['user'], course=c)
        if csl and not (has_role(context, 'Instructor')
                        or is_demo_user(context)):
            warn_user(
                context, c.language.phrase('Already_enrolled'),
                c.language.phrase('Enrolled_on') %
                (c.title(), c.language.ftime(csl.first().time_enrolled)))
        else:
            context['enroll'] = c.language.phrase(
                'About_to_enroll') % c.title()

    context['page_title'] = 'Presto Enrollment'
    return render(request, 'presto/enroll.html', context)
Пример #3
0
                        log_message('Success - message #%d can be deleted' % (i + 1), context['user'])
                        mailbox.dele(i + 1)
                    except Exception, e:
                        log_message('FAILURE -- error message: ' + str(e), context['user'])
        # close mailbox connection -- marked messages will now be deleted
        mailbox.quit()
    except Exception, e:
        warn_user(context, 'Error while retrieving mail', 'System message: ' + str(e))

    # add picture queue mail address
    context['pq_mail'] = settings.PICTURE_QUEUE_MAIL
    
    # add course data to context
    context['course'] = {
        'object': c,
        'hex':  encode(c.id, context['user_session'].encoder),
        'start': c.language.fdate(c.start_date),
        'end': c.language.fdate(c.end_date),
        'manager': prefixed_user_name(c.manager),                         
        'instructors': ', '.join([prefixed_user_name(i) for i in c.instructors.all()])
    }

    # add list of queue pictures for this course
    context['pictures'] = [{
        'object': qp,
        'hex': encode(qp.id, day_code(PQ_DAY_CODE), True), # NOTE: deterministic encoding!
        'time_received': qp.time_received.strftime('%H:%M'),
        'sender': qp.mail_from_name if qp.mail_from_name else qp.mail_from_address
        } for qp in QueuePicture.objects.filter(course=c).order_by('time_received', 'id')
    ]
    context['page_title'] = 'Presto Picture Queue' 
Пример #4
0
def history_view(request, **kwargs):
    h = kwargs.get('hex', '')
    context = generic_context(request, h)
    # check whether user can have student role
    if not change_role(context, 'Student'):
        return render(request, 'presto/forbidden.html', context)
        
    # check whether user is enrolled in any courses
    cl = Course.objects.filter(coursestudent__user=context['user']
        ).annotate(count=Count('coursestudent'))
    # add this list in readable form to the context (showing multiple enrollments)
    context['course_list'] = ',&nbsp;&nbsp; '.join(
        [c.title() + (' <span style="font-weight: 800; color: red">%d&times;</span>' %
            c.count if c.count > 1 else '') for c in cl
        ])

    # student (but also instructor in that role) may be enrolled in several courses
    # NOTE: "dummy" students are included, but not the "instructor" students
    csl = CourseStudent.objects.filter(user=context['user'], dummy_index__gt=-1)

    # get the estafettes for all the student's courses (even if they are not active)
    cel = CourseEstafette.objects.filter(
        is_deleted=False, is_hidden=False, course__in=[cs.course.id for cs in csl])
    # add this list in readable form to the context
    context['estafette_list'] = ',&nbsp;&nbsp; '.join([ce.title() for ce in cel])

    # get the set of all the course student's current participations
    pl = Participant.objects.filter(estafette__is_deleted=False,
        estafette__is_hidden=False, student__in=[cs.id for cs in csl]
        ).order_by('-estafette__start_time')
    
    # if user is a "focused" dummy user, retain only this course user's participations
    if context.has_key('alias'):
        pl = pl.filter(student=context['csid'])

    # if h is not set, show the list of participations as a menu
    if h == '':
        # start with an empty list (0 participations)
        context['participations'] = []
        # for each participation, create a context entry with properties to be displayed
        for p in pl:
            lang = p.estafette.course.language  # estafettes "speak" the language of their course
            steps = p.estafette.estafette.template.nr_of_legs()
            part = {'object': p,
                    'lang': lang,
                    'start': lang.ftime(p.estafette.start_time),
                    'end': lang.ftime(p.estafette.end_time),
                    'next_deadline': p.estafette.next_deadline(),
                    'steps': steps,
                    'hex': encode(p.id, context['user_session'].encoder),
                    'progress': '%d/%d' % (p.submitted_steps(), steps),
                    }
            context['participations'].append(part)
        # and show the list as a menu
        context['page_title'] = 'Presto History' 
        return render(request, 'presto/history_view.html', context)

    # if we get here, h is set, which means that a specific estafette has been selected
    try:
        # first validate the hex code
        pid = decode(h, context['user_session'].decoder)
        p = Participant.objects.get(pk=pid)
        context['object'] = p
        # encode again, because used to get progress chart
        context['hex'] = encode(p.id, context['user_session'].encoder)
        # add progress bar data
        context['things_to_do'] = p.things_to_do()
        # do not add participant name popups
        context['identify'] = False
        # add context fields to be displayed when rendering the template
        set_history_properties(context, p)
        # show the full estafette history using the standard page template
        context['page_title'] = 'Presto History' 
        return render(request, 'presto/estafette_history.html', context)        
            
    except Exception, e:
        report_error(context, e)
        return render(request, 'presto/error.html', context)
Пример #5
0
def instructor(request, **kwargs):
    context = generic_context(request)
    # check whether user can have instructor role
    if not change_role(context, 'Instructor'):
        return render(request, 'presto/forbidden.html', context)

    # create list of courses in which the user is manager/instructor
    context['courses'] = []
    c_set = Course.objects.filter(
        Q(instructors=context['user'])
        | Q(manager=context['user'])).distinct()
    for c in c_set:
        context['courses'].append({
            'object':
            c,
            'start':
            c.start_date.strftime(DATE_FORMAT),
            'end':
            c.end_date.strftime(DATE_FORMAT),
            'manager':
            prefixed_user_name(c.manager),
            'estafette_count':
            CourseEstafette.objects.filter(course=c).count(),
            'hex':
            encode(c.id, context['user_session'].encoder)
        })

    # create list of estafettes of which the user is creator/editor
    context['estafettes'] = [{
        'object':
        e,
        'edits':
        EDIT_STRING %
        (prefixed_user_name(e.last_editor), timezone.localtime(
            e.time_last_edit).strftime(DATE_TIME_FORMAT)),
        'template':
        e.template.name,
        'case_count':
        EstafetteCase.objects.filter(estafette=e).count(),
        'hex':
        encode(e.id, context['user_session'].encoder)
    } for e in Estafette.objects.filter(
        Q(editors=context['user']) | Q(creator=context['user'])).distinct()]

    # create list of active project relays in which the user is instructor
    context['running_relays'] = [{
        'object':
        ce,
        'start_time':
        c.language.ftime(ce.start_time),
        'end_time':
        c.language.ftime(ce.end_time),
        'next_deadline':
        ce.next_deadline(),
        'participant_count':
        Participant.objects.filter(estafette=ce,
                                   student__dummy_index__gt=-1).count(),
        'active_count':
        Participant.objects.filter(estafette=ce,
                                   student__dummy_index__gt=-1).filter(
                                       time_last_action__gte=timezone.now() -
                                       timedelta(days=1)).count(),
        'demo_code':
        ce.demonstration_code(),
        'hex':
        encode(ce.id, context['user_session'].encoder)
    } for ce in CourseEstafette.objects.filter(
        course__in=c_set, is_deleted=False).exclude(
            start_time__gte=timezone.now()).exclude(
                end_time__lte=timezone.now())]

    # create list of estafette templates that the user can choose from
    context['templates'] = [{
        'object':
        et,
        'hex':
        encode(et.id, context['user_session'].encoder)
    } for et in EstafetteTemplate.objects.filter(published=True)]

    context['page_title'] = 'Presto Instructor'
    return render(request, 'presto/instructor.html', context)
Пример #6
0
def set_history_properties(context, p):
    # switch to the course language
    lang = p.estafette.course.language
    # add the language-sensitive properties to the context
    context['lang'] = lang
    context['last_action'] = lang.ftime(p.student.last_action)
    context['start'] = lang.ftime(p.estafette.start_time)
    context['end'] = lang.ftime(p.estafette.end_time)
    context['desc'] = p.estafette.estafette.description
    context['next_deadline'] = p.estafette.next_deadline()
    context['decisions'] = []
    
    # NOTE: first see whether participant has decided on appeals in this estafette
    ap_list = Appeal.objects.filter(referee__user=p.student.user
        ).filter(review__reviewer__estafette=p.estafette).exclude(time_decided=DEFAULT_DATE)
    if ap_list:
        # create a list of decided appeals for each course estafette
        ap_nr = 0
        for ap in ap_list:
            ap_nr += 1
            # add the appeal that has been decided
            ar = ap.review
            a = ar.assignment
            ap_dict = {
                'nr': ap_nr,
                'appeal': ap,
                'appeal_title': '%s %s%s <span style="margin-left: 1em; font-weight: 500">(%s)</span>' % (
                    lang.phrase('Appeal_case_decision'), a.case.letter, str(a.leg.number),
                    lang.ftime(ap.time_decided)),
                # show names only to instructors
                'show_names': has_role(context, 'Instructor'),
                # add assignment properties
                'case_title': '%s %s: %s' % (lang.phrase('Case'), a.case.letter, a.case.name),
                'case_desc': a.case.description,
                'step_title': '%s %s: %s' % (lang.phrase('Step'), a.leg.number, a.leg.name),
                'step_desc': a.leg.description,
                'author': a.participant.student.dummy_name(),
                'time_uploaded': lang.ftime(a.time_uploaded),
                'pred_file_list': a.leg.file_list,
                'pred_hex': encode(a.id, context['user_session'].encoder),
                # add properties of the review that is appealed against
                'review': ar,
                'rev_items': ar.item_list(),
                'reviewer': ar.reviewer.student.dummy_name(),
                'time_reviewed': lang.ftime(ar.time_submitted),
                'time_appealed': lang.ftime(ar.time_appraised),
                # add properties concerning the appeal (but attributes of the PeerReview object)
                'imp_opinion': lang.phrase('Pred_opinion_succ_work') % lang.phrase(['Pass',
                    'No_improvement', 'Minor_changes', 'Good_job'][ar.improvement_appraisal]),
                'time_assigned': lang.ftime(ar.time_appeal_assigned),
                # add appeal properties
                'time_viewed': lang.ftime(ap.time_first_viewed),
                'penalties_as_text': lang.penalties_as_text(ap.predecessor_penalty, ap.successor_penalty),
                'hex': encode(ap.id, context['user_session'].encoder)
            }
            # pass hex for case if it has a file attached
            if a.case.upload:
                ap_dict['case_hex'] = encode(a.case.id, context['user_session'].encoder)
            # pass predecessor's appreciation of the decision (if submitted)
            if ap.time_acknowledged_by_predecessor != DEFAULT_DATE:
                ap_dict['time_pred_appr'] = lang.ftime(ap.time_acknowledged_by_predecessor)
                ap_dict['pred_appr_icon'] = FACES[ap.predecessor_appraisal]
                ap_dict['pred_appr_color'] = COLORS[ap.predecessor_appraisal]
                ap_dict['pred_motiv'] = ap.predecessor_motivation
                ap_dict['pred_objected'] = ap.is_contested_by_predecessor
            # pass predecessor's appreciation of the decision (if submitted)
            if ap.time_acknowledged_by_successor != DEFAULT_DATE:
                ap_dict['time_succ_appr'] = lang.ftime(ap.time_acknowledged_by_successor)
                ap_dict['succ_appr_icon'] = FACES[ap.successor_appraisal]
                ap_dict['succ_appr_color'] = COLORS[ap.successor_appraisal]
                ap_dict['succ_motiv'] = ap.successor_motivation
                ap_dict['succ_objected'] = ap.is_contested_by_successor
            context['decisions'].append(ap_dict)

    # now compile a consecutive list of all the student's actions in this estafette
    # (1) get list of all assignments for the student so far (except rejections and clones!)
    a_list = Assignment.objects.filter(participant=p).exclude(is_rejected=True
        ).filter(clone_of__isnull=True).order_by('leg__number')
    # from this list, derive a list with only the primary keys of the assignments
    aid_list = [a.id for a in a_list]
    # (2) get list of all reviews the student has written so far
    pr_given = PeerReview.objects.filter(reviewer=p).exclude(time_submitted=DEFAULT_DATE)
    # (3) also get list of all reviews the student has received so far
    pr_received = PeerReview.objects.filter(assignment__id__in=aid_list
        ).exclude(time_submitted=DEFAULT_DATE)
    # (4) also get the "oldest" user download objects for the assignment set
    ud_set = UserDownload.objects.filter(user=p.student.user, assignment__id__in=aid_list
            ).annotate(first_download_at=Min('time_downloaded'))
    # process the assignments in their number sequence
    steps = []
    for a in a_list:
        # show all the student's assignments (i.e., also those without file uploads)
        step_nr = a.leg.number
        step = {
            'step_nr': step_nr,
            'assignment': a,
            'header': lang.phrase('Step_header') % (step_nr, a.leg.name),
            # indicate whether the student did upload this step (or file list should not be shown)
            'uploaded': (a.time_uploaded != DEFAULT_DATE),
            'time': lang.ftime(a.time_uploaded),
            'case_title': '%s %s: %s' % (lang.phrase('Case'), a.case.letter, a.case.name),
            'case': a.case.description,
            'desc': a.leg.description,
            'Assigned_to_you': lang.phrase('Assigned_to_you') % lang.ftime(a.time_assigned),
            'You_uploaded': lang.phrase('You_uploaded') % lang.ftime(a.time_uploaded),
            'upl_items': a.item_list(),
            'own_file_list': a.leg.file_list(),
            'pred_file_list': [],
            'succ_file_list': [],
            'pred_reviews': [],
            'succ_reviews': [],
            'own_hex': encode(a.id, context['user_session'].encoder)
        }
        # pass hex for case if it has a file attached
        if a.case.upload:
            step['case_hex'] = encode(a.case.id, context['user_session'].encoder)
        # add half point bonus statement if such bonus was earned
        if a.on_time_for_bonus():
            step['time_bonus'] = lang.phrase('Half_point_bonus')

        # for all but the first step, also show the review(s) the student has given on preceding steps
        if step_nr > 1:
            # the file list is step-specific, but not review-specific
            step['pred_file_list'] = a.predecessor.leg.file_list()
            step['pred_hex'] = encode(a.predecessor.id, context['user_session'].encoder)
            # get the first user download (if any)
            fud = ud_set.filter(assignment__id=a.id).first()
            if fud:
                step['downloaded'] = lang.ftime(fud.first_download_at)
            # the student may have given several reviews: rejections and second opinions
            prgl = pr_given.filter(assignment__leg__number=step_nr - 1).order_by('time_submitted')
            # add these reviews to this step
            for pr in prgl:
                step['pred_reviews'].append(given_review_dict(context, pr, lang))

        # the student may also have received reviews of own work
        prrl = pr_received.filter(assignment__leg__number=step_nr).order_by('time_submitted')
        for pr in prrl:
            # do not show reviews that have been saved, but for which the successor did NOT
            # upload yet (unless it is a rejection or a FINAL review or a second opinion)
            if not (pr.is_rejection or pr.is_second_opinion or pr.final_review_index):
                # to prevent all error, double-check whether there IS a successor
                # NOTE: if reviewer is "instructor student", then skip the upload check
                if pr.assignment.successor and not (pr.reviewer.student.dummy_index < 0):
                    if pr.assignment.successor.time_uploaded == DEFAULT_DATE:
                        continue  # skip this review, but continue looping
            r = {
                'object': pr,
                'time_submitted': lang.ftime(pr.time_submitted),
                'rev_items': pr.item_list()
                # no 'pred_hex' field, as the reviewed work is the student's own step
            }
            # if student has responded, additional fields must be added
            if pr.time_appraised != DEFAULT_DATE:
                r['time_appraised'] = lang.ftime(pr.time_appraised)
                r['app_icon'] = FACES[pr.appraisal]
                r['app_header'] = lang.phrase('Your_appraisal_header') % lang.phrase(['ERROR',
                    'You_quite_happy', 'Mixed_feelings', 'You_unhappy'][pr.appraisal])
            # student can see reviewer's work only if review is not a rejection,
            # a second opinion, or a final review (which has by definition no successor)
            if not (pr.is_rejection or pr.is_second_opinion or pr.final_review_index):
                # to prevent all error, double-check whether there IS a successor
                if pr.assignment.successor:
                    step['succ_file_list'] = pr.assignment.successor.leg.file_list()
                    step['succ_hex'] = encode(pr.assignment.successor.id, context['user_session'].encoder)
                r['imp_opinion'] = lang.phrase('Your_opinion_on_improvement') % lang.phrase(['Pass',
                    'No_improvement', 'Minor_changes', 'Good_job'][pr.improvement_appraisal])
            # in case of an appeal, report the appeal status
            if pr.is_appeal:
                if pr.time_appeal_assigned != DEFAULT_DATE:
                    r['time_assigned'] = lang.ftime(pr.time_appeal_assigned)
                    ap = Appeal.objects.filter(review=pr).first()
                    if ap:
                        r['referee'] = prefixed_user_name(ap.referee.user)
                        if ap.time_first_viewed != DEFAULT_DATE:
                            r['time_first_viewed'] = lang.ftime(ap.time_first_viewed)
                        if ap.time_decided != DEFAULT_DATE:
                            r['time_decided'] = lang.ftime(ap.time_decided)
                            r['ref_rat'] = ap.grade
                            r['ref_motiv'] = ap.grade_motivation
                            r['penalties'] = lang.penalties_as_text(
                                ap.predecessor_penalty, ap.successor_penalty)
                        # NOTE: in this case, the student ("you") is the predecessor ...
                        if ap.time_acknowledged_by_predecessor != DEFAULT_DATE:
                            r['time_your_appr'] = lang.ftime(ap.time_acknowledged_by_predecessor)
                            r['your_appr_icon'] = FACES[ap.predecessor_appraisal]
                            r['your_motiv'] = ap.predecessor_motivation
                            r['you_objected'] = ap.is_contested_by_predecessor
                        # ... and hence the other party the successor
                        if ap.time_acknowledged_by_successor != DEFAULT_DATE:
                            r['time_other_appr'] = lang.ftime(ap.time_acknowledged_by_successor)
                            r['other_appr_icon'] = FACES[ap.successor_appraisal]
                            r['other_motiv'] = ap.successor_motivation
                            r['other_objected'] = ap.is_contested_by_successor
            # add the review to the list
            step['succ_reviews'].append(r)
        # now all step properties have been set, so the step is added to the list
        steps.append(step)            
    # and finally the step list is added to the context
    context['steps'] = steps

    # the student may also have given final reviews
    context['final_reviews'] = []
    prgl = pr_given.filter(final_review_index__gt=0).order_by('final_review_index')
    # add these reviews to this step
    n = 0
    for pr in prgl:
        r = given_review_dict(context, pr, lang)
        n += 1
        r['nr'] = n
        a = pr.assignment
        r['header'] = lang.phrase('Final_review_header') % (step_nr, a.leg.name)
        r['case_title'] = '%s %s: %s' % (lang.phrase('Case'), a.case.letter, a.case.name)
        r['case'] = a.case.description
        # pass hex for case if it has a file attached
        if a.case.upload:
            r['case_hex'] = encode(a.case.id, context['user_session'].encoder)
        context['final_reviews'].append(r)
Пример #7
0
    if kwargs.get('action', '') == 'delete-template':
        try:
            h = kwargs.get('hex', '')
            context = generic_context(request, h)
            etid = decode(h, context['user_session'].decoder)
            et = EstafetteTemplate.objects.get(pk=etid)
            log_message('Deleting template %s' % et.name, context['user'])
            et.delete()
        except Exception, e:
            report_error(request, context, e)
            return render(request, 'presto/error.html', context)

    # pass list of estafette templates that the user is (co-)authoring
    context['templates'] = [{
        'object': t,
        'hex': encode(t.id, context['user_session'].encoder),
        'edits': EDIT_STRING % (prefixed_user_name(t.last_editor),
                 timezone.localtime(t.time_last_edit).strftime(DATE_TIME_FORMAT)),
        'leg_count': EstafetteLeg.objects.filter(template=t).count()
        } for t in EstafetteTemplate.objects.filter(
            Q(editors=context['user']) | Q(creator=context['user'])).distinct()
    ]

    # TO DO: also pass list of questionnaire templates that the user is (co-)authoring
    context['questionnaires'] = [{
        'object': q,
        'hex': encode(q.id, context['user_session'].encoder),
        'edits': EDIT_STRING % (prefixed_user_name(q.last_editor),
                 timezone.localtime(q.time_last_edit).strftime(DATE_TIME_FORMAT)),
        'item_count': 0, # to be changed!!
        } for q in QuestionnaireTemplate.objects.filter(
Пример #8
0
def awards(request):
    context = generic_context(request)

    # check awardable achievements, and create/update badges if appropriate

    # (1) get participant badges issued so far
    b_list = PrestoBadge.objects.filter(participant__isnull=False).filter(
        participant__student__user=context['user']).distinct()
    badge_dict = {}
    for b in b_list:
        # get the badge relevant properties as a dict
        d = b.as_dict()
        # and add to this dict the badge object and its ID hex code
        d['object'] = b
        d['hex'] = encode(b.id, context['user_session'].encoder)
        badge_dict[b.id] = d

    # (2) get user's finished steps in relays that issue badges
    # NOTE: no badges for instructor participants (dummy index must be 0)
    p_list = Participant.objects.filter(student__user=context['user'],
                                        student__dummy_index=0,
                                        estafette__with_badges=True)
    # ignore "clone" assignments, as only their original represents the actual work
    # NOTE: this also prevents rewards for a "selfie" review
    a_list = Assignment.objects.filter(participant__in=p_list,
                                       time_uploaded__gt=DEFAULT_DATE,
                                       clone_of__isnull=True)
    r_list = PeerReview.objects.filter(assignment__in=a_list,
                                       time_submitted__gt=DEFAULT_DATE,
                                       grade__gt=2).distinct()

    # (3) create badges for those steps not already rewarded
    for r in r_list:
        # NOTE: only consider reviews of assignments that have an uploaded successor,
        #       or are final reviews, or are instructor reviews
        if (r.reviewer.student.dummy_index < 0 or r.final_review_index > 0
                or (r.assignment.successor != None
                    and r.assignment.successor.time_uploaded != DEFAULT_DATE)):
            # badge must match participant and level
            if not lookup_badge(badge_dict, r.assignment.leg.number,
                                r.assignment.participant):
                b = PrestoBadge.objects.create(
                    course=r.assignment.participant.student.course,
                    participant=r.assignment.participant,
                    referee=None,
                    levels=r.assignment.leg.template.nr_of_legs(),
                    attained_level=r.assignment.leg.number)
                d = b.as_dict()
                d['object'] = b
                d['hex'] = encode(b.id, context['user_session'].encoder)
                badge_dict[b.id] = d

    # (4) get referee decisions that revised the user's grade to 3+ stars
    r_list = PeerReview.objects.filter(
        assignment__in=a_list,
        is_appeal=True,
        time_appeal_assigned__gt=DEFAULT_DATE).distinct()
    a_list = Appeal.objects.filter(review__in=r_list,
                                   time_decided__gt=DEFAULT_DATE,
                                   grade__gt=2).distinct()

    # (5) create badges for those steps not already rewarded
    for a in a_list:
        ara = a.review.assignment
        # badge must match participant and level
        if not lookup_badge(badge_dict, ara.leg.number, ara.participant):
            b = PrestoBadge.objects.create(
                course=ara.participant.student.course,
                participant=ara.participant,
                referee=None,
                levels=ara.leg.template.nr_of_legs(),
                attained_level=ara.leg.number)
            d = b.as_dict()
            d['object'] = b
            d['hex'] = encode(b.id, context['user_session'].encoder)
            badge_dict[b.id] = d

    # (6) add list of entries to context, sorted by template name and then levels attained
    context['participant_badges'] = badge_dict.values()
    context['participant_badges'].sort(cmp=compare_badge_info)

    # (7) get referee badges issued so far
    # NOTE: referee badges are awarded only upon passing a referee exam, hence not created here
    ref_badge_dict = {}
    for b in PrestoBadge.objects.filter(referee__isnull=False).filter(
            referee__user=context['user']):
        d = b.as_dict()
        d['object'] = b
        d['hex'] = encode(b.id, context['user_session'].encoder)
        ref_badge_dict[b.referee.id] = d

    # add list of entries to context, sorted by template name and then levels attained
    context['referee_badges'] = ref_badge_dict.values()
    context['referee_badges'].sort(cmp=compare_badge_info)

    # get referee LoA's issued so far (with ID encoded as hex)
    l_dict = {}
    # first add participant LoA's
    # NOTE: keys prefixed by P to distinguish participant IDs from referee IDs
    for l in LetterOfAcknowledgement.objects.filter(
            participant__student__user=context['user']):
        l_dict['P%d' % l.participant.id] = {
            'object': l,
            'hex': encode(l.id, context['user_session'].encoder)
        }
    # NOTE: referee LoA's have their estafette ID as key
    for l in LetterOfAcknowledgement.objects.filter(
            referee__user=context['user']):
        l_dict[l.estafette.id] = {
            'object': l,
            'hex': encode(l.id, context['user_session'].encoder)
        }

    # get the user's referee object instances (created upon passing a referee exam)
    r_list = Referee.objects.filter(user=context['user'])

    # get decided appeal cases, and tally them (1) per course estafette (2) per leg
    re_dict = {}
    a_list = Appeal.objects.filter(referee__in=r_list,
                                   time_decided__gt=DEFAULT_DATE)

    # NOTE: to qualify for a referee letter of acknowledgement, referees must have
    #       made at least 1 appeal decision (so re_dict remains empty if a_list is empty)
    for a in a_list:
        e = a.review.assignment.participant.estafette
        l = a.review.assignment.leg.number
        # update step count if estafette already has an entry ...
        if re_dict.has_key(e.id):
            if re_dict[e.id].has_key(l):
                re_dict[e.id][l] += 1
            else:
                re_dict[e.id][l] = 1
            re_dict[e.id]['first'] = min(re_dict[e.id]['first'],
                                         a.time_decided)
            re_dict[e.id]['last'] = max(re_dict[e.id]['last'], a.time_decided)
        # ... or create a new entry
        else:
            re_dict[e.id] = {
                'ce': e,
                'ref': a.referee,
                'first': a.time_decided,
                'last': a.time_decided,
                'sum': 0,
                'cnt': 0,
                l: 1
            }
        # collect statistics on appraisal of the decisions (by default, assume neutral: 2)
        if a.predecessor_appraisal > 0:
            re_dict[e.id]['sum'] += a.predecessor_appraisal
        else:
            re_dict[e.id]['sum'] += 2
        if a.successor_appraisal > 0:
            re_dict[e.id]['sum'] += a.successor_appraisal
        else:
            re_dict[e.id]['sum'] += 2
        re_dict[e.id]['cnt'] += 2

    # re_dict now contains all "entitlements" for referee LoA's
    for e in re_dict.keys():
        # count refereed steps while making a list of refereed step numbers
        ns = re_dict[e]['ce'].estafette.template.nr_of_legs()
        acc = 0
        rs = []
        for i in range(1, ns + 1):
            if re_dict[e].has_key(i):
                rs.append(i)
                acc += re_dict[e][i]
        # format this as a nice phrase, e.g. "steps 1, 2 and 4 (of 4)"
        if ns == 1:
            steps = 'the single step'
        elif len(rs) == 1:
            steps = 'step %d (of %d)' % (rs[0], ns)
        elif len(rs) == ns:
            steps = 'all %d steps' % ns
        else:
            steps = 'steps %s and %d (of %d)' % (', '.join(
                [str(s) for s in rs[:-1]]), rs[-1], ns)
        # compute the average appreciation on a scale from -1 to 1
        appr = 0
        print 'count=%d' % re_dict[e]['cnt']
        print 'sum=%d' % re_dict[e]['sum']
        if re_dict[e]['cnt'] > 0:
            appr = re_dict[e]['sum'] / float(re_dict[e][
                'cnt']) - 2  # subtract 2 because frown = 1 and smile = 3
        if l_dict.has_key(e):
            # upgrade letter if needed
            changed = False
            l = l_dict[e]['object']
            if l.appeal_case_count != acc:
                l.appeal_case_count = acc
                l.extra_hours = acc * re_dict[e][
                    'ce'].estafette.hours_per_appeal
                l.time_first_case = re_dict[e]['first']
                l.time_last_case = re_dict[e]['last']
                changed = True
            if l.average_appreciation != appr:
                l.average_appreciation = appr
                changed = True
            if changed:
                l.save()
        else:
            # create new letter if user has indeed decided on one or more appeal cases
            l = LetterOfAcknowledgement.objects.create(
                estafette=re_dict[e]['ce'],
                referee=re_dict[e]['ref'],
                step_list=steps,
                time_first_case=re_dict[e]['first'],
                time_last_case=re_dict[e]['last'],
                appeal_case_count=acc,
                extra_hours=acc * re_dict[e]['ce'].estafette.hours_per_appeal,
                average_appreciation=appr)
            l_dict[e] = {
                'object': l,
                'hex': encode(l.id, context['user_session'].encoder)
            }

    context['letters'] = l_dict

    context['page_title'] = 'Presto Awards'
    return render(request, 'presto/awards.html', context)
Пример #9
0
     else:
         jd['error'] = 'Cannot change badge design because course badges have been issued'
 elif a in ['list instructor', 'add instructor', 'remove instructor']:
     cid = decode(request.POST.get('h', ''), user_session.encoder)
     c = Course.objects.get(pk=cid)
     # prevent error messages when instructor hex is not passed
     i = request.POST.get('i', '')
     if a != 'list instructor' and len(i) == 32:
         uid = decode(i, user_session.encoder)
         u = User.objects.get(pk=uid)
         if a == 'add instructor':
             c.instructors.add(u)
         else:
             c.instructors.remove(u)
         c.save()
     jd['il'] = [{'hex': encode(i.pk, user_session.encoder), 'name': prefixed_user_name(i)}
         for i in c.instructors.all()]
 elif a == 'toggle pq':
     cid = decode(request.POST.get('h', ''), user_session.encoder)
     c = Course.objects.get(pk=cid)
     p = request.POST.get('p', '')
     if p == 'open':
         c.picture_queue_open = not c.picture_queue_open
         c.save()
         jd['p'] = c.picture_queue_open
     elif p == 'strict':
         c.picture_queue_strict = not c.picture_queue_strict
         c.save()
         jd['p'] = c.picture_queue_strict
 elif a == 'rotate picture':
     qpid = decode(request.POST.get('h', ''), user_session.encoder)
Пример #10
0
    # add course properties that need conversion to context
    context['course'] = {
        'object':
        c,
        'start':
        c.language.fdate(c.start_date),
        'end':
        c.language.fdate(c.end_date),
        'manager':
        prefixed_user_name(c.manager),
        'owned':
        c.manager == context['user'],
        'instructors': [{
            'name': prefixed_user_name(i),
            'hex': encode(i.id, context['user_session'].encoder)
        } for i in c.instructors.all()],
        'hex':
        encode(c.id, context['user_session'].encoder),
        'disc': (c.badge_color >> 24) & 15,
        'red': (c.badge_color >> 16) & 255,
        'green': (c.badge_color >> 8) & 255,
        'blue':
        c.badge_color & 255
    }

    # add available language codes (for drop-down in form)
    context['lang_codes'] = UI_LANGUAGE_CODES

    # add hex IDs of all users having instructor role (except course manager)
    i_role = Role.objects.get(name='Instructor')
Пример #11
0
                                              last_editor=context['user'],
                                              time_last_edit=timezone.now())

    # when no errors, create list of cases in this estafette
    ec_list = EstafetteCase.objects.filter(estafette=e)
    e_cases = []
    for ec in ec_list:
        e_cases.append({
            'object':
            ec,
            'edits':
            EDIT_STRING %
            (prefixed_user_name(ec.last_editor),
             timezone.localtime(ec.time_last_edit).strftime(DATE_TIME_FORMAT)),
            'hex':
            encode(ec.id, context['user_session'].encoder)
        })
    context['estafette'] = {
        'object':
        e,
        'edits':
        EDIT_STRING %
        (prefixed_user_name(e.last_editor), timezone.localtime(
            e.time_last_edit).strftime(DATE_TIME_FORMAT)),
        'hex':
        encode(e.id, context['user_session'].encoder),
        'owner':
        prefixed_user_name(e.creator),
        'owned':
        e.creator == context['user'],
        # pass the names and hex IDs of editors