mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-21 13:18:25 +02:00
Add button to download all solutions and syntheses in a ZIP file
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
@ -399,7 +399,7 @@ class Pool(models.Model):
|
||||
|
||||
@property
|
||||
def solutions(self):
|
||||
return Solution.objects.filter(participation__in=self.participations, final_solution=self.tournament.final)
|
||||
return [passage.defended_solution for passage in self.passages.all()]
|
||||
|
||||
def average(self, participation):
|
||||
return sum(passage.average(participation) for passage in self.passages.all()) \
|
||||
|
@ -36,8 +36,30 @@
|
||||
<dt class="col-sm-3">{% trans "Defended solutions:" %}</dt>
|
||||
<dd class="col-sm-9">
|
||||
{% for passage in pool.passages.all %}
|
||||
<a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}{% if not forloop.last %}, {% endif %}</a>
|
||||
<a href="{{ passage.defended_solution.file.url }}">{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }}</a>{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
<a href="{% url 'participation:pool_download_solutions' pk=pool.pk %}" class="badge rounded-pill text-bg-secondary">
|
||||
<i class="fas fa-download"></i> {% trans "Download all" %}
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-3">{% trans "Syntheses:" %}</dt>
|
||||
<dd class="col-sm-9">
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for passage in pool.passages.all %}
|
||||
<li class="list-group-item">
|
||||
{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} :
|
||||
{% for synthesis in passage.syntheses.all %}
|
||||
<a href="{{ synthesis.file.url }}">{{ synthesis.participation.team.trigram }} ({{ synthesis.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
|
||||
{% empty %}
|
||||
{% trans "No synthesis was uploaded yet." %}
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<a href="{% url 'participation:pool_download_syntheses' pk=pool.pk %}" class="badge rounded-pill text-bg-secondary">
|
||||
<i class="fas fa-download"></i> {% trans "Download all" %}
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-3">{% trans "BigBlueButton link:" %}</dt>
|
||||
|
@ -6,9 +6,9 @@ from django.views.generic import TemplateView
|
||||
|
||||
from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \
|
||||
MyTeamDetailView, NoteUpdateView, ParticipationDetailView, PassageCreateView, PassageDetailView, \
|
||||
PassageUpdateView, PoolAddJurysView, PoolCreateView, PoolDetailView, PoolNotesTemplateView, PoolUpdateTeamsView, \
|
||||
PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, SynthesisUploadView, \
|
||||
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
|
||||
PassageUpdateView, PoolAddJurysView, PoolCreateView, PoolDetailView, PoolDownloadView, PoolNotesTemplateView, \
|
||||
PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, \
|
||||
SynthesisUploadView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
|
||||
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
|
||||
TournamentListView, TournamentUpdateView
|
||||
|
||||
@ -37,6 +37,8 @@ urlpatterns = [
|
||||
path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
|
||||
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
|
||||
path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"),
|
||||
path("pools/<int:pk>/solutions/", PoolDownloadView.as_view(), name="pool_download_solutions"),
|
||||
path("pools/<int:pk>/syntheses/", PoolDownloadView.as_view(), name="pool_download_syntheses"),
|
||||
path("pools/<int:pk>/notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"),
|
||||
path("pools/<int:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"),
|
||||
path("pools/<int:pk>/update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
|
||||
|
@ -733,6 +733,42 @@ class PoolUpdateTeamsView(VolunteerMixin, UpdateView):
|
||||
return self.handle_no_permission()
|
||||
|
||||
|
||||
class PoolDownloadView(VolunteerMixin, DetailView):
|
||||
"""
|
||||
Download all solutions or syntheses as a ZIP archive.
|
||||
"""
|
||||
model = Pool
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
if request.user.registration.is_admin or request.user.registration.is_volunteer \
|
||||
and (self.get_object().tournament in request.user.registration.organized_tournaments.all()
|
||||
or request.user.registration in self.get_object().juries.all()):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
return self.handle_no_permission()
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
pool = self.get_object()
|
||||
|
||||
is_solution = 'solutions' in request.path
|
||||
|
||||
output = BytesIO()
|
||||
zf = ZipFile(output, "w")
|
||||
for s in (pool.solutions if is_solution else Synthesis.objects.filter(passage__pool=pool).all()):
|
||||
zf.write("media/" + s.file.name, f"{s}.pdf")
|
||||
|
||||
zf.close()
|
||||
response = HttpResponse(content_type="application/zip")
|
||||
filename = _("Solutions for pool {pool} of tournament {tournament}.zip") \
|
||||
if is_solution else _("Syntheses for pool {pool} of tournament {tournament}.zip")
|
||||
filename = filename.format(pool=pool.get_letter_display() + str(pool.round), tournament=pool.tournament.name)
|
||||
response["Content-Disposition"] = "attachment; filename=\"{filename}\"" \
|
||||
.format(filename=filename)
|
||||
response.write(output.getvalue())
|
||||
return response
|
||||
|
||||
|
||||
class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
|
||||
"""
|
||||
This view lets organizers set jurys for a pool, without multiplying clicks.
|
||||
|
Reference in New Issue
Block a user