From 19c4978ae34c7c362bd0b2ebb7e427c6ec5cd266 Mon Sep 17 00:00:00 2001 From: Lucas McCullum Date: Thu, 11 Feb 2021 08:41:40 -0500 Subject: [PATCH] Adds option to reject credential applications based on template --- physionet-django/console/forms.py | 27 ++++++++ .../process_credential_application.html | 22 +++++++ physionet-django/console/views.py | 42 ++++++++++++- physionet-django/notification/utility.py | 62 ++++++++++++++----- 4 files changed, 138 insertions(+), 15 deletions(-) diff --git a/physionet-django/console/forms.py b/physionet-django/console/forms.py index fd10d7f13d..6fe6c31b47 100644 --- a/physionet-django/console/forms.py +++ b/physionet-django/console/forms.py @@ -60,6 +60,26 @@ (None, 'N/A or Undetermined') ) +REJECT_REASONS = ( + (15, 'CITI: Incorrect CITI course / wrong PDF'), + (14, 'CITI: CITI Certificate'), + (13, 'CITI: No HIPAA section'), + (12, 'CITI: Failed HIPAA section'), + (11, 'CITI: Inconsistent CITI report email'), + (10, 'Concerns: DUA violation'), + (9, 'Identitiy: Inconsistent information'), + (8, 'Identitiy: Can not verify identity'), + (7, 'Institution: Industry Researcher without institution'), + (6, 'Institution: Hospital Researcher without institution'), + (5, 'Institution: Student without institution'), + (4, 'Institution: MIT Affiliates as institution'), + (3, 'Reference: Incorrect reference name'), + (2, 'Reference: Listed self as reference'), + (1, 'Reference: Inactive reference email'), + (0, 'Other'), +) + + class AssignEditorForm(forms.Form): """ Assign an editor to a project under submission @@ -341,6 +361,13 @@ class ContactCredentialRefForm(forms.Form): body = forms.CharField(widget=forms.Textarea) +class CredentialRejectForm(forms.Form): + """ + Decide the reason for rejecting a credentialing application. + """ + reason = forms.ChoiceField(choices=REJECT_REASONS) + + class ProcessCredentialForm(forms.ModelForm): """ Form to respond to a credential application diff --git a/physionet-django/console/templates/console/process_credential_application.html b/physionet-django/console/templates/console/process_credential_application.html index e890057bb4..fa35b01b2f 100644 --- a/physionet-django/console/templates/console/process_credential_application.html +++ b/physionet-django/console/templates/console/process_credential_application.html @@ -179,8 +179,30 @@

Reference comments:

{% endif %} +
+
+ Reject with a template. +
+
+ +
+
+ {% if modal_id == "reject-template-modal" %} + {% include "console/email_modal.html" with form=form app_user=app_user modal_id=modal_id modal_title=modal_title submit_name=submit_name submit_value=submit_value submit_text=submit_text %} + {% endif %} +{% if modal_id == "reject-template-modal" %} + +{% endif %} {% endblock %} diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py index aa83af7b23..1dbb492752 100644 --- a/physionet-django/console/views.py +++ b/physionet-django/console/views.py @@ -1153,6 +1153,7 @@ def process_credential_application(request, application_slug): process_credential_form = forms.ProcessCredentialReviewForm(responder=request.user, instance=application) + reject_credential_form = forms.CredentialRejectForm() ref_email = notification.contact_reference(request, application, send=False, wordwrap=False) @@ -1327,6 +1328,30 @@ def process_credential_application(request, application_slug): responder=request.user, instance=application) else: messages.error(request, 'Invalid review. See form below.') + elif 'reject_template' in request.POST: + cred_reject_form = forms.CredentialRejectForm( + data=request.POST) + if cred_reject_form.is_valid(): + template_choice = int(cred_reject_form.cleaned_data['reason']) + reject_email = notification.process_credential_complete( + request, application, send=False, response_choice=template_choice) + form = forms.ContactCredentialRefForm(initial=reject_email) + app_user = application.user + modal_id = 'reject-template-modal' + modal_title = 'Reject with a template' + submit_name = 'reject_applicant' + submit_value = application.user.id + submit_text = 'Reject with a template' + return render(request, 'console/process_credential_application.html', + {'application': application, 'app_user': application.user, + 'intermediate_credential_form': intermediate_credential_form, + 'process_credential_form': process_credential_form, + 'processing_credentials_nav': True, 'page_title': page_title, + 'contact_cred_ref_form': contact_cred_ref_form, + 'reject_credential_form': reject_credential_form, + 'form':form, 'app_user':app_user, 'modal_id':modal_id, + 'modal_title':modal_title, 'submit_name':submit_name, + 'submit_value':submit_value, 'submit_text':submit_text}) elif 'approve_response_all' in request.POST: if request.POST['decision'] == '0': messages.error(request, 'You selected Reject. Did you mean to Approve All?') @@ -1344,6 +1369,20 @@ def process_credential_application(request, application_slug): page_title = title_dict[application.credential_review.status] intermediate_credential_form = forms.ProcessCredentialReviewForm( responder=request.user, instance=application) + elif 'reject_applicant' in request.POST: + contact_cred_ref_form = forms.ContactCredentialRefForm( + data=request.POST) + if contact_cred_ref_form.is_valid(): + application.status = 1 + application.responder = request.user + application.reject(application.responder) + application.save() + subject = contact_cred_ref_form.cleaned_data['subject'] + body = contact_cred_ref_form.cleaned_data['body'] + notification.process_credential_complete(request, application, + subject=subject, body=body) + return render(request, 'console/process_credential_complete.html', + {'application':application}) elif 'contact_reference' in request.POST: contact_cred_ref_form = forms.ContactCredentialRefForm( data=request.POST) @@ -1374,7 +1413,8 @@ def process_credential_application(request, application_slug): 'process_credential_form': process_credential_form, 'processing_credentials_nav': True, 'page_title': page_title, 'contact_cred_ref_form': contact_cred_ref_form, - 'known_active': known_active, 'known_cred': known_cred}) + 'known_active': known_active, 'known_cred': known_cred, + 'reject_credential_form': reject_credential_form}) @login_required diff --git a/physionet-django/notification/utility.py b/physionet-django/notification/utility.py index 352d373722..ca07982d2f 100644 --- a/physionet-django/notification/utility.py +++ b/physionet-django/notification/utility.py @@ -14,7 +14,24 @@ from project.models import DataAccessRequest, License RESPONSE_ACTIONS = {0:'rejected', 1:'accepted'} - +RESPONSE_COMMENTS = { + 15: 'It looks like you did not complete the correct CITI course. The correct course is "Data or Specimens Only Research". Please resubmit with the correct information.', + 14: 'You submitted the CITI Certificate instead of the Completion Report (it should include a list of courses and scores). Please resubmit with the correct report.', + 13: 'The HIPAA module is missing from your CITI completion report.', + 12: 'Please retake the CITI course paying particular attention to the section on HIPAA Privacy Protections.', + 11: 'The email on your CITI report does not match your application email. You can not share CITI training reports.', + 10: 'Access is provided to individuals. Your research summary suggests that data may be shared within the group. Please resubmit your application, making it clear that the Data Use Agreement will be followed. If your colleagues plan to access the data, then they will need to apply for access independently.', + 9: 'Some of the information on your application does not make sense. Please resubmit with the correct information.', + 8: 'We cannot identify you or your reference in our web search. Can you resubmit and provide references to your publications, personal webpage, or associations with known organizations?', + 7: 'You list your research category as Industry Researcher but did not list an institution. Please update your institution or if you are not associated with an institution or select Independent Researcher as your research category.', + 6: 'You listed yourself as a Hospital Researcher but did not list an institution. Please resubmit with the correct information.', + 5: 'You listed yourself as a Student but did not list an institution. Please resubmit with the correct information.', + 4: '"Massachusetts Institute of Technology Affiliates" should not be listed as your institution. This is for the CITI course step only. Please resubmit with the correct information.', + 3: 'The name for your reference does not seem correct. Please resubmit with the correct information.', + 2: 'You listed yourself as a reference. Please resubmit with another valid reference.', + 1: 'The email address listed for your reference is not active (emails sent to this address are undelivered).', + 0: '' +} def mailto_url(*recipients, **params): """ @@ -631,23 +648,36 @@ def mailto_administrators(project, error): send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [settings.CONTACT_EMAIL], fail_silently=False) -def process_credential_complete(request, application, comments=True): +def process_credential_complete(request, application, send=True, comments=True, + response_choice=-1, subject='', body=''): """ Notify user of credentialing decision """ applicant_name = application.get_full_name() response = 'rejected' if application.status == 1 else 'accepted' - subject = 'Your application for PhysioNet credentialing' - body = loader.render_to_string( - 'notification/email/process_credential_complete.html', { - 'application': application, - 'applicant_name': applicant_name, - 'domain': get_current_site(request), - 'url_prefix': get_url_prefix(request), - 'comments': comments, - 'signature': email_signature(), - 'footer': email_footer() - }) + if not subject: + subject = 'Your application for PhysioNet credentialing' + if (comments == True) and (response_choice >= 0): + response = 'rejected' + application.status = 1 + application.responder_comments = RESPONSE_COMMENTS[response_choice] + application.save() + + if not body: + body = loader.render_to_string( + 'notification/email/process_credential_complete.html', { + 'application': application, + 'applicant_name': applicant_name, + 'domain': get_current_site(request), + 'url_prefix': get_url_prefix(request), + 'comments': comments, + 'signature': email_signature(), + 'footer': email_footer() + }) + + if (comments == True) and (response_choice >= 0) and (not send): + application.status = 0 + application.save() message = EmailMessage( subject=subject, @@ -656,7 +686,11 @@ def process_credential_complete(request, application, comments=True): to=[application.user.email], bcc=[settings.CREDENTIAL_EMAIL] ) - message.send(fail_silently=False) + + if send: + message.send(fail_silently=False) + + return {'subject': subject, 'body': body} def credential_application_request(request, application): """