From f52f9d6fb389a5c9448d218e08ad5236e721df3a Mon Sep 17 00:00:00 2001 From: gmurtaza00 <111306331+gmurtaza00@users.noreply.github.com> Date: Fri, 28 Apr 2023 02:48:30 -0400 Subject: [PATCH] Export applications by round (#3313) Staff/admin user can download all the applications in a specific round whether open or closed. Export option will be visible when we hover over the round on http://hypha.test/apply/submissions/ Exported File name will be **Round name.csv**. Exported File will not contain the **round name, application phase and the file fields present in the application form.** Closes #3312 --- .../funds/includes/round-block-listing.html | 1 + hypha/apply/funds/urls.py | 2 + hypha/apply/funds/views.py | 54 ++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/hypha/apply/funds/templates/funds/includes/round-block-listing.html b/hypha/apply/funds/templates/funds/includes/round-block-listing.html index 322ad2d89..5fbd25c29 100644 --- a/hypha/apply/funds/templates/funds/includes/round-block-listing.html +++ b/hypha/apply/funds/templates/funds/includes/round-block-listing.html @@ -21,6 +21,7 @@ {% endif %} </p> <a class="round-block__view" href="{% url 'apply:rounds:detail' pk=round.pk %}">{% trans 'View' %}</a> + <a class="round-block__view" href="{% url 'apply:rounds:export' pk=round.pk %}">{% trans 'Export' %}</a> </li> {% else %} <li class="round-block__item round-block__item--more"> diff --git a/hypha/apply/funds/urls.py b/hypha/apply/funds/urls.py index 02962ba71..a20169547 100644 --- a/hypha/apply/funds/urls.py +++ b/hypha/apply/funds/urls.py @@ -4,6 +4,7 @@ from hypha.apply.projects import urls as projects_urls from .views import ( AwaitingReviewSubmissionsListView, + ExportSubmissionsByRound, GroupingApplicationsListView, ReminderDeleteView, ReviewerLeaderboard, @@ -84,6 +85,7 @@ submission_urls = ([ rounds_urls = ([ path('', RoundListView.as_view(), name="list"), path('<int:pk>/', SubmissionsByRound.as_view(), name="detail"), + path('export/<int:pk>/', ExportSubmissionsByRound.as_view(), name="export"), ], 'rounds') diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index dd13d7751..9c756db9d 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -1,5 +1,7 @@ +import csv from copy import copy from datetime import timedelta +from io import StringIO import django_tables2 as tables from django.conf import settings @@ -10,7 +12,7 @@ from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.humanize.templatetags.humanize import intcomma from django.core.exceptions import PermissionDenied from django.db.models import Count, F, Q -from django.http import FileResponse, Http404, HttpResponseRedirect +from django.http import FileResponse, Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils import timezone @@ -561,6 +563,56 @@ class SubmissionUserFlaggedView(UserPassesTestMixin, BaseAdminSubmissionsTable): def test_func(self): return self.request.user.is_apply_staff or self.request.user.is_reviewer +@method_decorator(staff_required, name='dispatch') +class ExportSubmissionsByRound(BaseAdminSubmissionsTable): + + def export_submissions(self, round_id): + csv_stream = StringIO() + writer = csv.writer(csv_stream) + header_row,values = [],[] + index = 0 + check = False + + for submission in ApplicationSubmission.objects.filter(round=round_id): + for field_id in submission.question_text_field_ids: + question_field = submission.serialize(field_id) + field_name = question_field['question'] + field_value = question_field['answer'] + if field_id not in submission.named_blocks: + header_row.append(field_name) if not check else header_row + values.append(field_value) + else: + header_row.insert(index,field_name) if not check else header_row + values.insert(index,field_value) + index = index + 1 + + if not check: + writer.writerow(header_row) + check = True + + writer.writerow(values) + values.clear() + index = 0 + + csv_stream.seek(0) + return csv_stream + + def get_queryset(self): + try: + self.obj = Page.objects.get(pk=self.kwargs.get('pk')).specific + except Page.DoesNotExist as exc: + raise Http404(_("No Round or Lab found matching the query")) from exc + + if not isinstance(self.obj, (LabBase, RoundBase)): + raise Http404(_("No Round or Lab found matching the query")) + return super().get_queryset().filter(Q(round=self.obj) | Q(page=self.obj)) + + def get(self, request, pk): + self.get_queryset() + csv_data = self.export_submissions(pk) + response = HttpResponse(csv_data.readlines(), content_type="text/csv") + response['Content-Disposition'] = 'inline; filename=' + str(self.obj) + '.csv' + return response @method_decorator(staff_required, name='dispatch') class SubmissionsByRound(BaseAdminSubmissionsTable, DelegateableListView): -- GitLab