From 1588fe6c2796733f366a6b7ff7a101287e610ee1 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 9 Aug 2023 10:23:50 -0400 Subject: [PATCH 1/2] feat: Download questionnaire responses (#4981) --- ietf/nomcom/tests.py | 27 ++++++++-- ietf/nomcom/views.py | 49 ++++++++++++------- ietf/templates/nomcom/download_questio.txt | 9 ++++ .../nomcom/reclassify_feedback_item.html | 2 +- .../nomcom/view_feedback_nominee.html | 7 ++- 5 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 ietf/templates/nomcom/download_questio.txt diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index b68b0beb8c..72dabb0de3 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -2870,7 +2870,6 @@ class ReclassifyFeedbackTests(TestCase): def setUp(self): super().setUp() setup_test_public_keys_dir(self) - nomcom_test_data() self.nc = NomComFactory.create(**nomcom_kwargs_for_year()) self.chair = self.nc.group.role_set.filter(name='chair').first().person self.member = self.nc.group.role_set.filter(name='member').first().person @@ -2882,6 +2881,28 @@ def tearDown(self): teardown_test_public_keys_dir(self) super().tearDown() + def test_download_feedback_nominee(self): + # not really a reclassification test, but in closely adjacent code + fb = FeedbackFactory.create(nomcom=self.nc,type_id='questio') + fb.positions.add(self.position) + fb.nominees.add(self.nominee) + fb.save() + self.assertEqual(Feedback.objects.questionnaires().count(), 1) + + url = reverse('ietf.nomcom.views.view_feedback_nominee', kwargs={'year':self.nc.year(), 'nominee_id':self.nominee.id}) + login_testing_unauthorized(self,self.member.user.username,url) + provide_private_key_to_test_client(self) + response = self.client.post(url, {'feedback_id': fb.id, 'submit': 'download'}) + self.assertEqual(response.status_code, 403) + + self.client.logout() + self.client.login(username=self.chair.user.username, password=self.chair.user.username + "+password") + provide_private_key_to_test_client(self) + + response = self.client.post(url, {'feedback_id': fb.id, 'submit': 'download'}) + self.assertEqual(response.status_code, 200) + self.assertIn('questio', response['Content-Disposition']) + def test_reclassify_feedback_nominee(self): fb = FeedbackFactory.create(nomcom=self.nc,type_id='comment') fb.positions.add(self.position) @@ -2892,14 +2913,14 @@ def test_reclassify_feedback_nominee(self): url = reverse('ietf.nomcom.views.view_feedback_nominee', kwargs={'year':self.nc.year(), 'nominee_id':self.nominee.id}) login_testing_unauthorized(self,self.member.user.username,url) provide_private_key_to_test_client(self) - response = self.client.post(url, {'feedback_id': fb.id, 'type': 'obe'}) + response = self.client.post(url, {'feedback_id': fb.id, 'type': 'obe', 'submit': 'reclassify'}) self.assertEqual(response.status_code, 403) self.client.logout() self.client.login(username=self.chair.user.username, password=self.chair.user.username + "+password") provide_private_key_to_test_client(self) - response = self.client.post(url, {'feedback_id': fb.id, 'type': 'obe'}) + response = self.client.post(url, {'feedback_id': fb.id, 'type': 'obe', 'submit': 'reclassify'}) self.assertEqual(response.status_code, 200) fb = Feedback.objects.get(id=fb.id) diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index b36f664500..042637ab89 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -19,6 +19,7 @@ from django.template.loader import render_to_string from django.urls import reverse from django.utils.encoding import force_bytes, force_str +from django.utils.text import slugify from email.errors import HeaderParseError @@ -1023,24 +1024,38 @@ def view_feedback_nominee(request, year, nominee_id): return HttpResponseForbidden('Restricted to roles: Nomcom Chair, Nomcom Advisor') feedback_id = request.POST.get('feedback_id', None) feedback = get_object_or_404(Feedback, id=feedback_id) - type = request.POST.get('type', None) - if type: - if type == 'unclassified': - feedback.type = None - feedback.nominees.clear() - messages.success(request, 'The selected feedback has been de-classified. Please reclassify it in the Pending emails tab.') + submit = request.POST.get('submit', None) + if submit == 'download': + fn = f'questio-{slugify(nominee.name())}-{feedback.time.date()}.txt' + response = render_to_string('nomcom/download_questio.txt', + {'year': year, + 'nominee': nominee, + 'feedback': feedback, + 'positions': ','.join([str(p) for p in feedback.positions.all()]), + }, + request=request) + response = HttpResponse(response, content_type='text/plain') + response['Content-Disposition'] = f'attachment; filename="{fn}"' + return response + elif submit == 'reclassify': + type = request.POST.get('type', None) + if type: + if type == 'unclassified': + feedback.type = None + feedback.nominees.clear() + messages.success(request, 'The selected feedback has been de-classified. Please reclassify it in the Pending emails tab.') + else: + feedback.type = FeedbackTypeName.objects.get(slug=type) + messages.success(request, f'The selected feedback has been reclassified as {feedback.type.name}.') + feedback.save() else: - feedback.type = FeedbackTypeName.objects.get(slug=type) - messages.success(request, f'The selected feedback has been reclassified as {feedback.type.name}.') - feedback.save() - else: - return render(request, 'nomcom/view_feedback_nominee.html', - {'year': year, - 'nomcom': nomcom, - 'feedback_types': feedback_types, - 'reclassify_feedback': feedback, - 'is_chair_task': True, - }) + return render(request, 'nomcom/view_feedback_nominee.html', + {'year': year, + 'nomcom': nomcom, + 'feedback_types': feedback_types, + 'reclassify_feedback': feedback, + 'is_chair_task': True, + }) last_seen = FeedbackLastSeen.objects.filter(reviewer=request.user.person,nominee=nominee).first() last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1, month=1, day=1, tzinfo=datetime.timezone.utc) diff --git a/ietf/templates/nomcom/download_questio.txt b/ietf/templates/nomcom/download_questio.txt new file mode 100644 index 0000000000..71514095c8 --- /dev/null +++ b/ietf/templates/nomcom/download_questio.txt @@ -0,0 +1,9 @@ +{# Copyright The IETF Trust 2023, All Rights Reserved #}{% autoescape off %}{% load nomcom_tags %}Questionnaire response from {{ nominee.person.name }} + +From: {{ feedback.author|formatted_email|default:"Anonymous" }} +Date: {{ feedback.time|date:"Y-m-d" }} +Positions: {{ positions }}{% if feedback.subject %} +Subject: {{ feedback.subject }}{% endif %} + +{% decrypt feedback.comments request year 1 %} +{% endautoescape %} \ No newline at end of file diff --git a/ietf/templates/nomcom/reclassify_feedback_item.html b/ietf/templates/nomcom/reclassify_feedback_item.html index efa71dc104..fb967493cf 100644 --- a/ietf/templates/nomcom/reclassify_feedback_item.html +++ b/ietf/templates/nomcom/reclassify_feedback_item.html @@ -99,5 +99,5 @@

Reclassify feedback item

- + diff --git a/ietf/templates/nomcom/view_feedback_nominee.html b/ietf/templates/nomcom/view_feedback_nominee.html index 5408880da4..bc59d7ae82 100644 --- a/ietf/templates/nomcom/view_feedback_nominee.html +++ b/ietf/templates/nomcom/view_feedback_nominee.html @@ -89,7 +89,12 @@

Feedback about {{ nominee }}

{% csrf_token %} -

+ {% endif %} +
From e6d8f41bcd3cb86800ad32cad11f8043895e41c8 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Mon, 14 Aug 2023 11:48:39 -0400 Subject: [PATCH 2/2] style: Expand "questio" to "questionnaire" --- ietf/nomcom/tests.py | 2 +- ietf/nomcom/views.py | 4 ++-- .../{download_questio.txt => download_questionnaire.txt} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename ietf/templates/nomcom/{download_questio.txt => download_questionnaire.txt} (100%) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 72dabb0de3..3f0ff6e82d 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -2901,7 +2901,7 @@ def test_download_feedback_nominee(self): response = self.client.post(url, {'feedback_id': fb.id, 'submit': 'download'}) self.assertEqual(response.status_code, 200) - self.assertIn('questio', response['Content-Disposition']) + self.assertIn('questionnaire-', response['Content-Disposition']) def test_reclassify_feedback_nominee(self): fb = FeedbackFactory.create(nomcom=self.nc,type_id='comment') diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index 042637ab89..6bd85dad81 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -1026,8 +1026,8 @@ def view_feedback_nominee(request, year, nominee_id): feedback = get_object_or_404(Feedback, id=feedback_id) submit = request.POST.get('submit', None) if submit == 'download': - fn = f'questio-{slugify(nominee.name())}-{feedback.time.date()}.txt' - response = render_to_string('nomcom/download_questio.txt', + fn = f'questionnaire-{slugify(nominee.name())}-{feedback.time.date()}.txt' + response = render_to_string('nomcom/download_questionnaire.txt', {'year': year, 'nominee': nominee, 'feedback': feedback, diff --git a/ietf/templates/nomcom/download_questio.txt b/ietf/templates/nomcom/download_questionnaire.txt similarity index 100% rename from ietf/templates/nomcom/download_questio.txt rename to ietf/templates/nomcom/download_questionnaire.txt