diff --git a/hypha/apply/funds/migrations/0084_add_all_except_dismissed_outcome_reviewersettings.py b/hypha/apply/funds/migrations/0084_add_all_except_dismissed_outcome_reviewersettings.py new file mode 100644 index 0000000000000000000000000000000000000000..4560254962b2eb53c62a95610a0126b484d2c2a2 --- /dev/null +++ b/hypha/apply/funds/migrations/0084_add_all_except_dismissed_outcome_reviewersettings.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.16 on 2020-11-30 09:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0083_remove_screening_status_field'), + ] + + operations = [ + migrations.AlterField( + model_name='reviewersettings', + name='outcome', + field=models.CharField(choices=[('all', 'All Outcomes'), ('all_except_dismissed', 'All Outcomes Except Dismissed'), ('accepted', 'Only Accepted')], default='all', help_text='Submissions outcomes for which reviewers should have access to', max_length=20), + ), + ] diff --git a/hypha/apply/funds/models/reviewer_role.py b/hypha/apply/funds/models/reviewer_role.py index afdca4c700c0194ade9728a093d3ae7f4e05fd06..0ca60e7e3cf9a67f417ebbfeb854fe3f354054b3 100644 --- a/hypha/apply/funds/models/reviewer_role.py +++ b/hypha/apply/funds/models/reviewer_role.py @@ -48,6 +48,7 @@ class ReviewerSettings(BaseSetting): OUTCOMES = [ ('all', 'All Outcomes'), + ('all_except_dismissed', 'All Outcomes Except Dismissed'), ('accepted', 'Only Accepted') ] @@ -69,7 +70,7 @@ class ReviewerSettings(BaseSetting): outcome = models.CharField( choices=OUTCOMES, default='all', - max_length=10, + max_length=20, help_text='Submissions outcomes for which reviewers should have access to' ) assigned = models.BooleanField( diff --git a/hypha/apply/funds/models/submissions.py b/hypha/apply/funds/models/submissions.py index 16f0c35fb634f32ab0acc6c2502c916048570c15..404fabdc91a6561f2868dc7aa3a2af60730ea279 100644 --- a/hypha/apply/funds/models/submissions.py +++ b/hypha/apply/funds/models/submissions.py @@ -54,7 +54,9 @@ from ..workflow import ( STAGE_CHANGE_ACTIONS, WORKFLOWS, UserPermissions, + accepted_statuses, active_statuses, + dismissed_statuses, ext_or_higher_statuses, get_review_active_statuses, review_statuses, @@ -135,7 +137,9 @@ class ApplicationSubmissionQueryset(JSONOrderable): if reviewer_settings.state == 'ext_state_or_higher': qs = qs.filter(status__in=ext_or_higher_statuses) if reviewer_settings.outcome == 'accepted': - qs = qs.filter(status='accepted') + qs = qs.filter(status__in=accepted_statuses) + if reviewer_settings.outcome == 'all_except_dismissed': + qs = qs.exclude(status__in=dismissed_statuses) if reviewer_settings.assigned: qs = qs.filter(reviewers=user) return qs.distinct() diff --git a/hypha/apply/funds/tests/test_views.py b/hypha/apply/funds/tests/test_views.py index cb9393d76e97f85b2ca5203fff7f1491d42a5b2a..f9e7c572d9a8bbae465809e275d17107e41a532a 100644 --- a/hypha/apply/funds/tests/test_views.py +++ b/hypha/apply/funds/tests/test_views.py @@ -632,9 +632,9 @@ class TestReviewerSubmissionView(BaseSubmissionViewTestCase): cls.applicant = ApplicantFactory() cls.reviewer_role = ReviewerRoleFactory() apply_site = ApplySiteFactory() - reviewer_settings, _ = ReviewerSettings.objects.get_or_create(site_id=apply_site.id) - reviewer_settings.use_settings = True - reviewer_settings.save() + cls.reviewer_settings, _ = ReviewerSettings.objects.get_or_create(site_id=apply_site.id) + cls.reviewer_settings.use_settings = True + cls.reviewer_settings.save() def test_cant_see_add_determination_primary_action(self): def assert_add_determination_not_displayed(submission, button_text): @@ -759,6 +759,102 @@ class TestReviewerSubmissionView(BaseSubmissionViewTestCase): DeterminationFactory(submission=submission, author=self.user, accepted=True, submitted=False) assert_view_determination_not_displayed(submission) + def test_can_access_any_submission(self): + """ + Reviewer settings are being used with default values. + """ + submission = ApplicationSubmissionFactory(user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + def test_can_only_access_reviewed_submission(self): + self.reviewer_settings.submission = 'reviewed' + self.reviewer_settings.state = 'all' + self.reviewer_settings.outcome = 'all' + self.reviewer_settings.save() + submission = ApplicationSubmissionFactory(user=self.applicant, reviewers=[self.user]) + response = self.get_page(submission) + self.assertEqual(response.status_code, 403) + + ReviewFactory(submission=submission, author__reviewer=self.user, is_draft=False) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + def test_can_only_access_external_review_or_higher_submission(self): + self.reviewer_settings.submission = 'all' + self.reviewer_settings.state = 'ext_state_or_higher' + self.reviewer_settings.outcome = 'all' + self.reviewer_settings.assigned = False + self.reviewer_settings.save() + + submission = ApplicationSubmissionFactory(user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 403) + + submission = ApplicationSubmissionFactory(with_external_review=True, user=self.applicant) + submission.perform_transition('ext_internal_review', self.user) + submission.perform_transition('ext_post_review_discussion', self.user) + submission.perform_transition('ext_external_review', self.user) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + def test_cant_access_dismissed_submission(self): + self.reviewer_settings.submission = 'all' + self.reviewer_settings.state = 'all' + self.reviewer_settings.outcome = 'all' + self.reviewer_settings.assigned = False + self.reviewer_settings.save() + + submission = ApplicationSubmissionFactory(status='rejected', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + self.reviewer_settings.outcome = 'all_except_dismissed' + self.reviewer_settings.save() + submission = ApplicationSubmissionFactory(status='rejected', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 403) + + def test_can_only_access_accepted_submission(self): + self.reviewer_settings.submission = 'all' + self.reviewer_settings.state = 'all' + self.reviewer_settings.save() + + submission = ApplicationSubmissionFactory(status='rejected', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + self.reviewer_settings.outcome = 'accepted' + self.reviewer_settings.save() + submission = ApplicationSubmissionFactory(status='rejected', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 403) + + submission = ApplicationSubmissionFactory(status='accepted', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + def test_can_only_access_assigned_submission(self): + self.reviewer_settings.submission = 'all' + self.reviewer_settings.state = 'all' + self.reviewer_settings.outcome = 'all' + self.reviewer_settings.save() + + submission = ApplicationSubmissionFactory(status='accepted', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + + self.reviewer_settings.assigned = True + self.reviewer_settings.save() + + submission = ApplicationSubmissionFactory(status='accepted', user=self.applicant) + response = self.get_page(submission) + self.assertEqual(response.status_code, 403) + + submission = ApplicationSubmissionFactory(status='accepted', user=self.applicant, reviewers=[self.user]) + response = self.get_page(submission) + self.assertEqual(response.status_code, 200) + class TestApplicantSubmissionView(BaseSubmissionViewTestCase): user_factory = ApplicantFactory diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 65e9438260479411b99e93a1e1d08680d42f66a5..35bbcec1eec1d476ce5fced239b9673aef58e131 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -1034,8 +1034,26 @@ def get_ext_or_higher_statuses(): return ext_review_or_higher_statuses +def get_accepted_statuses(): + accepted_statuses = set() + for phase_name, phase in PHASES: + if phase.display_name == 'Accepted': + accepted_statuses.add(phase_name) + return accepted_statuses + + +def get_dismissed_statuses(): + dismissed_statuses = set() + for phase_name, phase in PHASES: + if phase.display_name == 'Dismissed': + dismissed_statuses.add(phase_name) + return dismissed_statuses + + ext_or_higher_statuses = get_ext_or_higher_statuses() review_statuses = get_review_statuses() +accepted_statuses = get_accepted_statuses() +dismissed_statuses = get_dismissed_statuses() DETERMINATION_PHASES = list(phase_name for phase_name, _ in PHASES if '_discussion' in phase_name) DETERMINATION_RESPONSE_PHASES = [