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
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)
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'
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'] = ', '.join( [c.title() + (' <span style="font-weight: 800; color: red">%d×</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'] = ', '.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)
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)
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)
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(
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)
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)
# 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')
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