def statistics_available_plots(self, request, category, object): """Available plots for given object in given category. Does not check user's permission for viewing those statistics.""" result = [] rtimes = rounds_times(request) def plot_kind(name, object): return (statistics_plot_kinds[name], object) def pi_results_visible(pi): return rtimes[pi.round].public_results_visible(request.timestamp) \ or is_contest_admin(request) or is_contest_observer(request) if category == 'CONTEST': if not object: object = request.contest result.append(plot_kind('SUBMISSIONS_HISTOGRAM_CONTEST', object)) pis = visible_problem_instances(request) # Do not add this plot if it would be empty. if any(pi_results_visible(pi) and not pi.round.is_trial for pi in pis): result.append(plot_kind('POINTS_HISTOGRAM_CONTEST', object)) if category == 'PROBLEM': result.append(plot_kind('POINTS_HISTOGRAM_PROBLEM', object)) result.append(plot_kind('POINTS_TABLE_PROBLEM', object)) result.append(plot_kind('POINTS_TO_SOURCE_LENGTH_PROBLEM', object)) result.append(plot_kind('TEST_SCORES_TABLE_PROBLEM', object)) return result
def statistics_available_plots(self, request, category, object): """Available plots for given object in given category. Does not check user's permission for viewing those statistics.""" result = [] rtimes = rounds_times(request, self.contest) def plot_kind(name, object): return (statistics_plot_kinds[name], object) def pi_results_visible(pi): return rtimes[pi.round].public_results_visible(request.timestamp) \ or is_contest_admin(request) or is_contest_observer(request) if category == 'CONTEST': if not object: object = request.contest result.append(plot_kind('SUBMISSIONS_HISTOGRAM_CONTEST', object)) pis = visible_problem_instances(request) # Do not add this plot if it would be empty. if any( pi_results_visible(pi) and not pi.round.is_trial for pi in pis): result.append(plot_kind('POINTS_HISTOGRAM_CONTEST', object)) if category == 'PROBLEM': result.append(plot_kind('POINTS_HISTOGRAM_PROBLEM', object)) result.append(plot_kind('POINTS_TABLE_PROBLEM', object)) result.append(plot_kind('POINTS_TO_SOURCE_LENGTH_PROBLEM', object)) result.append(plot_kind('TEST_SCORES_TABLE_PROBLEM', object)) return result
def _last_finished_round_id(request): past_rounds = [rt for rt in rounds_times(request).iteritems() if rt[1].is_past(request.timestamp)] if not past_rounds: return None last_round = max(past_rounds, key=lambda x: x[1].end) return last_round[0].id
def contest_dashboard_view(request, round_pk=None): messages = messages_template_context(request, visible_messages(request))[ :NUMBER_OF_RECENT_ACTIONS] queryset = Submission.objects \ .filter(problem_instance__contest=request.contest) \ .order_by('-date') \ .select_related('user', 'problem_instance', 'problem_instance__contest', 'problem_instance__round', 'problem_instance__problem') ss = [submission_template_context(request, s) for s in queryset[:NUMBER_OF_RECENT_ACTIONS]] rtimes = list(rounds_times(request, request.contest).items()) rtimes.sort(key=lambda r_rt: r_rt[0].start_date) if round_pk is None and len(rtimes) > 0: # First active round, or last one if there are no active ones round_pk = next( ((r, rt) for r, rt in rtimes if rt.is_active(request.timestamp)), rtimes[-1])[0].pk context = { 'round_times': rtimes, 'selected_round': None, 'records': messages, 'submissions': ss } if round_pk is not None: context.update(get_round_context(request, round_pk)) return TemplateResponse(request, 'simpleui/contest/contest.html', context)
def _last_finished_round_id(request): past_rounds = [ rt for rt in rounds_times(request).iteritems() if rt[1].is_past(request.timestamp) ] if not past_rounds: return None last_round = max(past_rounds, key=lambda x: x[1].end) return last_round[0].id
def get_round_times(self, request, round): """Determines the times of the round for the user doing the request. The default implementation returns an instance of :class:`RoundTimes` cached by round_times() method. Round must belong to request.contest. :returns: an instance of :class:`RoundTimes` """ return rounds_times(request)[round]
def get_round_times(self, request, round): """Determines the times of the round for the user doing the request. The default implementation returns an instance of :class:`RoundTimes` cached by round_times() method. Round must belong to request.contest. :returns: an instance of :class:`RoundTimes` """ return rounds_times(request)[round]
def contest_dashboard_view(request, round_pk=None): if request.user.is_superuser: return redirect('default_contest_view', contest_id=request.contest.id) messages = messages_template_context( request, visible_messages(request))[:NUMBER_OF_RECENT_ACTIONS] queryset = (Submission.objects.filter( problem_instance__contest=request.contest).order_by( '-date').select_related( 'user', 'problem_instance', 'problem_instance__contest', 'problem_instance__round', 'problem_instance__problem', )) ss = [ submission_template_context(request, s) for s in queryset[:NUMBER_OF_RECENT_ACTIONS] ] rtimes = list(rounds_times(request, request.contest).items()) rtimes.sort(key=lambda r_rt: r_rt[0].start_date) if round_pk is None and len(rtimes) > 0: # First active round, or last one if there are no active ones round_pk = next( ((r, rt) for r, rt in rtimes if rt.is_active(request.timestamp)), rtimes[-1])[0].pk context = { 'round_times': rtimes, 'selected_round': None, 'records': messages, 'submissions': ss, 'contest_dashboard_url_name': 'simpleui_contest_dashboard', 'public_contest': isinstance( request.contest.controller.registration_controller(), PublicContestRegistrationController, ), } if round_pk is not None: context.update(get_round_context(request, round_pk)) return TemplateResponse(request, 'simpleui/contest/contest.html', context)
def can_see_problem_statistics(self, request, pi): controller = pi.controller rtimes = rounds_times(request) can_see_problem = controller.can_see_problem(request, pi) if pi.round: can_see_round_results = rtimes[pi.round].public_results_visible( request.timestamp) else: can_see_round_results = False can_observe = is_contest_admin(request) or is_contest_observer(request) return can_see_problem and (can_see_round_results or can_observe)
def can_see_problem_statistics(self, request, pi): controller = pi.controller rtimes = rounds_times(request, self.contest) can_see_problem = controller.can_see_problem(request, pi) if pi.round: can_see_round_results = rtimes[pi.round].public_results_visible( request.timestamp) else: can_see_round_results = False can_observe = is_contest_admin(request) or is_contest_observer(request) return can_see_problem and (can_see_round_results or can_observe)
def get_submission_relative_time(self, submission): # FIXME: SIO-1387 RoundTimes shouldn't require request # Workaround by mock Request object class DummyRequest(object): def __init__(self, contest, user): self.contest = contest self.user = user rtimes = rounds_times(DummyRequest(self.contest, submission.user)) round_start = rtimes[submission.problem_instance.round].get_start() submission_time = submission.date - round_start # Python2.6 does not support submission_time.total_seconds() return submission_time.days * 24 * 3600 + submission_time.seconds
def get_submission_relative_time(self, submission): # FIXME: SIO-1387 RoundTimes shouldn't require request # Workaround by mock Request object class DummyRequest(object): def __init__(self, user): self.user = user or AnonymousUser() rtimes = rounds_times(DummyRequest(submission.user or AnonymousUser()), self.contest) round_start = rtimes[submission.problem_instance.round].get_start() submission_time = submission.date - round_start # Python2.6 does not support submission_time.total_seconds() seconds = submission_time.days * 24 * 3600 + submission_time.seconds return max(0, seconds)
def get_round_times(self, request, round): """Determines the times of the round for the user doing the request. The default implementation returns an instance of :class:`RoundTimes` cached by round_times() method. Round must belong to request.contest. Request is optional (round extensions won't be included if omitted). :returns: an instance of :class:`RoundTimes` """ if request is not None: return rounds_times(request, self.contest)[round] else: return generic_rounds_times(None, self.contest)[round]
def get_round_times(self, request, round): """Determines the times of the round for the user doing the request. The default implementation returns an instance of :class:`RoundTimes` cached by round_times() method. Round must belong to request.contest. Request is optional (round extensions won't be included if omitted). :returns: an instance of :class:`RoundTimes` """ if request is not None: return rounds_times(request, self.contest)[round] else: return generic_rounds_times(None, self.contest)[round]
def statistics_available_plot_groups(self, request): problem_instances = visible_problem_instances(request) result = [] times = rounds_times(request) can_see_any_problems = False for pi in problem_instances: can_see_round = times[pi.round].results_visible(request.timestamp) if can_see_round: result.append(('PROBLEM', pi.short_name, pi.problem.name)) can_see_any_problems = True if can_see_any_problems: result.insert( 0, ('CONTEST', request.contest.id, request.contest.name)) return result
def contest_dashboard_view(request, round_pk=None): messages = messages_template_context( request, visible_messages(request))[:NUMBER_OF_RECENT_ACTIONS] queryset = Submission.objects \ .filter(problem_instance__contest=request.contest) \ .order_by('-date') \ .select_related('user', 'problem_instance', 'problem_instance__contest', 'problem_instance__round', 'problem_instance__problem') ss = [ submission_template_context(request, s) for s in queryset[:NUMBER_OF_RECENT_ACTIONS] ] rtimes = rounds_times(request).items() rtimes.sort(key=lambda (r, rt): r.start_date) if round_pk is None and len(rtimes) > 0: # First active round, or last one if there are no active ones round_pk = next( ((r, rt) for r, rt in rtimes if rt.is_active(request.timestamp)), rtimes[-1])[0].pk context = { 'round_times': rtimes, 'selected_round': None, 'records': messages, 'submissions': ss } if round_pk is not None: context.update(get_round_context(request, round_pk)) return TemplateResponse(request, 'simpleui/contest/contest.html', context)
def navbar_countdown(context): timestamp = getattr(context['request'], 'timestamp', None) contest = getattr(context['request'], 'contest', None) if not (timestamp and contest): return {} rtimes = rounds_times(context['request']).values() next_rounds_times = [rt for rt in rtimes if rt.is_future(timestamp)] next_rounds_times.sort(key=lambda rt: rt.get_start()) current_rounds_times = [rt for rt in rtimes if rt.is_active(timestamp) and rt.get_end()] current_rounds_times.sort(key=lambda rt: rt.get_end()) if current_rounds_times: countdown_destination = _("end of the round.") remaining_seconds = time.mktime((current_rounds_times[0].get_end()) .timetuple()) - time.mktime(timestamp.timetuple()) round_duration = time.mktime(current_rounds_times[0].get_end() .timetuple()) - time.mktime(current_rounds_times[0].get_start() .timetuple()) elapsed_part = 1 - 1. * remaining_seconds / round_duration elif next_rounds_times: countdown_destination = _("start of the round.") remaining_seconds = time.mktime((next_rounds_times[0].get_start()) .timetuple()) - time.mktime(timestamp.timetuple()) round_duration = 0 else: return {} seconds = remaining_seconds % 60 seconds_str = ungettext('%(seconds)d second ', '%(seconds)d seconds ', seconds) % {'seconds': seconds} remaining_seconds /= 60 minutes = int(remaining_seconds % 60) minutes_str = ungettext('%(minutes)d minute ', '%(minutes)d minutes ', minutes) % {'minutes': minutes} remaining_seconds /= 60 hours = int(remaining_seconds % 24) hours_str = ungettext('%(hours)d hour ', '%(hours)d hours ', hours) % {'hours': hours} remaining_seconds /= 24 days = int(remaining_seconds) days_str = ungettext('%(days)d day ', '%(days)d days ', days) % {'days': days} if days: countdown_days = days_str + hours_str + minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_days)sleft to the %(countdown_dest)s', '%(countdown_days)sleft to the %(countdown_dest)s', days) % {'countdown_days': countdown_days, 'countdown_dest': countdown_destination} elif hours: countdown_hours = hours_str + minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_hours)sleft to the %(countdown_dest)s', '%(countdown_hours)sleft to the %(countdown_dest)s', hours) % {'countdown_hours': countdown_hours, 'countdown_dest': countdown_destination} elif minutes: countdown_minutes = minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_minutes)sleft to the %(countdown_dest)s', '%(countdown_minutes)sleft to the %(countdown_dest)s', minutes) % {'countdown_minutes': countdown_minutes, 'countdown_dest': countdown_destination} elif seconds: countdown_seconds = seconds_str countdown_text = \ ungettext('%(countdown_seconds)sleft to the %(countdown_dest)s', '%(countdown_seconds)sleft to the %(countdown_dest)s', seconds) % {'countdown_seconds': countdown_seconds, 'countdown_dest': countdown_destination} else: if round_duration: countdown_text = _("The round is over!") else: countdown_text = _("The round has started!") if round_duration: if elapsed_part < 0.5: red = int(510 * elapsed_part) green = 255 else: red = 255 green = int(510 * (1 - elapsed_part)) blue = 0 bar_color = 'rgb(%d,%d,%d)' % (red, green, blue) elapsed_part = str(int(elapsed_part * 100)) + '%' else: bar_color = '' elapsed_part = '' return {'countdown_text': countdown_text, 'bar_color': bar_color, 'elapsed_part': elapsed_part}
def navbar_countdown(context): timestamp = getattr(context['request'], 'timestamp', None) contest = getattr(context['request'], 'contest', None) if not (timestamp and contest): return {} rtimes = rounds_times(context['request']).values() next_rounds_times = [rt for rt in rtimes if rt.is_future(timestamp)] next_rounds_times.sort(key=lambda rt: rt.get_start()) current_rounds_times = [ rt for rt in rtimes if rt.is_active(timestamp) and rt.get_end() ] current_rounds_times.sort(key=lambda rt: rt.get_end()) if current_rounds_times: countdown_destination = _("end of the round.") remaining_seconds = time.mktime( (current_rounds_times[0].get_end()).timetuple()) - time.mktime( timestamp.timetuple()) round_duration = time.mktime( current_rounds_times[0].get_end().timetuple()) - time.mktime( current_rounds_times[0].get_start().timetuple()) elapsed_part = 1 - 1. * remaining_seconds / round_duration elif next_rounds_times: countdown_destination = _("start of the round.") remaining_seconds = time.mktime( (next_rounds_times[0].get_start()).timetuple()) - time.mktime( timestamp.timetuple()) round_duration = 0 else: return {} seconds = remaining_seconds % 60 seconds_str = ungettext('%(seconds)d second ', '%(seconds)d seconds ', seconds) % { 'seconds': seconds } remaining_seconds /= 60 minutes = int(remaining_seconds % 60) minutes_str = ungettext('%(minutes)d minute ', '%(minutes)d minutes ', minutes) % { 'minutes': minutes } remaining_seconds /= 60 hours = int(remaining_seconds % 24) hours_str = ungettext('%(hours)d hour ', '%(hours)d hours ', hours) % { 'hours': hours } remaining_seconds /= 24 days = int(remaining_seconds) days_str = ungettext('%(days)d day ', '%(days)d days ', days) % { 'days': days } if days: countdown_days = days_str + hours_str + minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_days)sleft to the %(countdown_dest)s', '%(countdown_days)sleft to the %(countdown_dest)s', days) % {'countdown_days': countdown_days, 'countdown_dest': countdown_destination} elif hours: countdown_hours = hours_str + minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_hours)sleft to the %(countdown_dest)s', '%(countdown_hours)sleft to the %(countdown_dest)s', hours) % {'countdown_hours': countdown_hours, 'countdown_dest': countdown_destination} elif minutes: countdown_minutes = minutes_str + seconds_str countdown_text = \ ungettext('%(countdown_minutes)sleft to the %(countdown_dest)s', '%(countdown_minutes)sleft to the %(countdown_dest)s', minutes) % {'countdown_minutes': countdown_minutes, 'countdown_dest': countdown_destination} elif seconds: countdown_seconds = seconds_str countdown_text = \ ungettext('%(countdown_seconds)sleft to the %(countdown_dest)s', '%(countdown_seconds)sleft to the %(countdown_dest)s', seconds) % {'countdown_seconds': countdown_seconds, 'countdown_dest': countdown_destination} else: if round_duration: countdown_text = _("The round is over!") else: countdown_text = _("The round has started!") if round_duration: if elapsed_part < 0.5: red = int(510 * elapsed_part) green = 255 else: red = 255 green = int(510 * (1 - elapsed_part)) blue = 0 bar_color = 'rgb(%d,%d,%d)' % (red, green, blue) elapsed_part = str(int(elapsed_part * 100)) + '%' else: bar_color = '' elapsed_part = '' return { 'countdown_text': countdown_text, 'bar_color': bar_color, 'elapsed_part': elapsed_part }