diff --git a/judge/views/user.py b/judge/views/user.py index 68284a74a6..32c833ced0 100644 --- a/judge/views/user.py +++ b/judge/views/user.py @@ -15,6 +15,8 @@ from django.core.exceptions import PermissionDenied, ValidationError from django.db import transaction from django.db.models import Count, Max, Min +from django.db.models.fields import DateField +from django.db.models.functions import Cast, ExtractYear from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, render from django.urls import reverse @@ -182,6 +184,23 @@ def get_context_data(self, **kwargs): ratio = (max_ever - max_user) / (max_ever - min_ever) if max_ever != min_ever else 1.0 context['max_graph'] = max_user + ratio * delta context['min_graph'] = min_user + ratio * delta - delta + + submissions = ( + self.object.submission_set + .annotate(date_only=Cast('date', DateField())) + .values('date_only').annotate(cnt=Count('id')) + ) + + context['submission_data'] = mark_safe(json.dumps({ + date_counts['date_only'].isoformat(): date_counts['cnt'] for date_counts in submissions + })) + context['submission_metadata'] = mark_safe(json.dumps({ + 'min_year': ( + self.object.submission_set + .annotate(year_only=ExtractYear('date')) + .aggregate(min_year=Min('year_only'))['min_year'] + ), + })) return context diff --git a/resources/table.scss b/resources/table.scss index 919406e974..802fde87f5 100644 --- a/resources/table.scss +++ b/resources/table.scss @@ -1,7 +1,5 @@ @import "vars"; -$table_header_rounding: 6px; - .h-scrollable-table { overflow-x: auto; } diff --git a/resources/users.scss b/resources/users.scss index e87d5a1864..c483efd2cc 100644 --- a/resources/users.scss +++ b/resources/users.scss @@ -1,3 +1,5 @@ +@import "vars"; + #content-left { &.users { flex: unset; @@ -263,3 +265,100 @@ a.edit-profile { color: white; } } + +#submission-activity { + #submission-activity-actions { + text-align: center; + #prev-year-action, #next-year-action { + font-size: 1.75em; + } + #year { + font-size: 1.25em; + color: #444; + } + } + + #submission-activity-display { + border: 1px solid $border_gray; + border-radius: $table_header_rounding; + + .info-bar { + display: flex; + justify-content: space-between; + + .info-table { + width: 15%; + min-width: 130px; + + .info-table-text { + width: 8%; + } + } + } + + .info-text { + font-size: 0.75em; + line-height: 1; + font-weight: 100; + color: #444; + } + + #submission-total-count { + align-self: center; + padding-left: 8%; + font-size: 0.85em; + } + + @media(max-width: 1000px) { + #submission-total-count { + padding-left: 5px; + } + } + + table { + width: 100%; + padding: 5px; + + th.submission-date-col { + width: 8%; + } + + @media (max-width: 1000px) { + th.submission-date-col { + display: none; + } + } + td { + border-radius: 20%; + + div { + margin-top: 100%; + } + + &.activity-label { + position: relative; + white-space: nowrap; + } + + &.activity-blank { + background-color: white; + } + &.activity-0 { + background-color: #ddd; + } + &.activity-1 { + background-color: #9be9a8; + } + &.activity-2 { + background-color: #40c463; + } + &.activity-3 { + background-color: #2f9c4c; + } + &.activity-4 { + background-color: #216e39; + } + } + } + } +} diff --git a/resources/vars.scss b/resources/vars.scss index 3f157ea4a4..53dea28dbd 100644 --- a/resources/vars.scss +++ b/resources/vars.scss @@ -8,4 +8,6 @@ $announcement_red: #ae0000; $base_font_size: 14px; $widget_border_radius: 4px; +$table_header_rounding: 6px; + $monospace-fonts: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; diff --git a/templates/user/user-about.html b/templates/user/user-about.html index 0be72d3a62..bae3cd7c30 100644 --- a/templates/user/user-about.html +++ b/templates/user/user-about.html @@ -45,6 +45,69 @@