Skip to content

Commit

Permalink
Afford Submission visibility settings per Group
Browse files Browse the repository at this point in the history
Administrators can see "Related submissions" on a Submissions Details
page. This change allows a Wagtail administrator to add visibility to
any Group such that members of the Group can also see "Related
submissions." This change embraces Django Group and Wagtail Setting
Models such that the settings can be made within the application.

A Wagtail administrator can add visibility from the `admin` interface
via Settings -> Submission Details Page Settings. To grant visibility,
the administrator checks the "Sees related submissions" box for the
Group specified in the Group dropdown. To revoke visibility, the
administrator unchecks the same. However, if a user is a member of any
group that has been granted visibility, the user will have visibility.

Fixes HyphaApp#3958
  • Loading branch information
bickelj committed Aug 8, 2024
1 parent e7df1b4 commit ce9f737
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ <h5>{% trans "Reviews & assignees" %}</h5>
</div>
{% endblock %}

{% block related %}
{% endblock %}

{% block extra_js %}
{{ reviewer_form.media.js }}
{{ comment_form.media.js }}
Expand Down
23 changes: 10 additions & 13 deletions hypha/apply/funds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
ViewDispatcher,
)

from ..review.views import RelatedSubmissionsMixin
from . import services
from .differ import compare
from .files import generate_private_file_path
Expand Down Expand Up @@ -1143,7 +1144,9 @@ def form_valid(self, form):
return super().form_valid(form)


class AdminSubmissionDetailView(ActivityContextMixin, DelegateableView, DetailView):
class AdminSubmissionDetailView(
RelatedSubmissionsMixin, ActivityContextMixin, DelegateableView, DetailView
):
template_name_suffix = "_admin_detail"
model = ApplicationSubmission
form_views = [
Expand All @@ -1163,17 +1166,7 @@ def dispatch(self, request, *args, **kwargs):
return redirect or super().dispatch(request, *args, **kwargs)

def get_context_data(self, **kwargs):
other_submissions = (
self.model.objects.filter(user=self.object.user)
.current()
.exclude(id=self.object.id)
.order_by("-submit_time")
)
if self.object.next:
other_submissions = other_submissions.exclude(id=self.object.next.id)

return super().get_context_data(
other_submissions=other_submissions,
archive_access_groups=get_archive_view_groups(),
can_archive=can_alter_archived_submissions(self.request.user),
**kwargs,
Expand Down Expand Up @@ -1236,7 +1229,9 @@ def partial_screening_card(request, pk):
return render(request, "funds/includes/screening_status_block.html", ctx)


class ReviewerSubmissionDetailView(ActivityContextMixin, DelegateableView, DetailView):
class ReviewerSubmissionDetailView(
RelatedSubmissionsMixin, ActivityContextMixin, DelegateableView, DetailView
):
template_name_suffix = "_reviewer_detail"
model = ApplicationSubmission
form_views = [CommentFormView]
Expand Down Expand Up @@ -1291,7 +1286,9 @@ def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)


class CommunitySubmissionDetailView(ActivityContextMixin, DelegateableView, DetailView):
class CommunitySubmissionDetailView(
RelatedSubmissionsMixin, ActivityContextMixin, DelegateableView, DetailView
):
template_name_suffix = "_community_detail"
model = ApplicationSubmission
form_views = [CommentFormView]
Expand Down
56 changes: 56 additions & 0 deletions hypha/apply/review/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import UserPassesTestMixin
Expand Down Expand Up @@ -27,11 +29,65 @@
from hypha.apply.todo.views import add_task_to_user, remove_tasks_for_user
from hypha.apply.users.decorators import staff_required
from hypha.apply.utils.image import generate_image_tag
from hypha.apply.utils.models import SubmissionsDetailsSettings
from hypha.apply.utils.views import CreateOrUpdateView

from ..users.groups import REVIEWER_GROUP_NAME
from .models import Review, ReviewOpinion
from .options import DISAGREE

logger = logging.getLogger(__name__)


class RelatedSubmissionsMixin:
def sees_other_submissions(self):
if self.request.user.is_superuser:
logger.debug(
"User is a superuser and therefore should see related submissions"
)
return True
submissions_details_settings = SubmissionsDetailsSettings.objects.filter()
roles_that_view = [
g.group.name
for g in submissions_details_settings
if g.sees_related_submissions
]
for role in self.request.user.roles:
logger.debug(f"Found role {role} associated with user {self.request.user}")
if role in roles_that_view:
logger.debug(f"Found role {role} should see related submissions")
return True
logger.debug("None of this user's roles should see related submissions")
return False

def get_context_data(self, **kwargs):
other_submissions = None
if self.sees_other_submissions():
other_submissions = (
self.model.objects.filter(user=self.object.user)
.current()
.exclude(id=self.object.id)
.order_by("-submit_time")
)
if self.object.next:
other_submissions = other_submissions.exclude(id=self.object.next.id)
assigned_reviewers = self.object.assigned.review_order()

if not self.object.stage.has_external_review:
assigned_reviewers = assigned_reviewers.staff()

# Calculate the recommendation based on role and staff reviews
recommendation = self.object.reviews.by_staff().recommendation()

return super().get_context_data(
hidden_types=[REVIEWER_GROUP_NAME],
staff_reviewers_exist=assigned_reviewers.staff().exists(),
assigned_reviewers=assigned_reviewers,
recommendation=recommendation,
other_submissions=other_submissions,
**kwargs,
)


def get_fields_for_stage(submission, user=None):
forms = submission.get_from_parent("review_forms").all()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Generated by Django 4.2.11 on 2024-06-04 16:40

from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields


class Migration(migrations.Migration):
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("apply_utils", "0001_add_pdf_page_size_setting"),
]

operations = [
migrations.CreateModel(
name="SubmissionsDetailsSetting",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
options={
"verbose_name": "Submissions Details Page Setting",
},
),
migrations.CreateModel(
name="SubmissionsDetailsSettings",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"sort_order",
models.IntegerField(blank=True, editable=False, null=True),
),
(
"sees_related_submissions",
models.BooleanField(
default=False,
help_text="Should members of the above Group see the related submissions side panel when reviewing a submission?",
),
),
(
"group",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="auth.group"
),
),
(
"setting",
modelcluster.fields.ParentalKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="submissions_details_settings",
to="apply_utils.submissionsdetailssetting",
),
),
],
options={
"verbose_name": "Submissions Details Page Settings",
},
),
migrations.AddConstraint(
model_name="submissionsdetailssettings",
constraint=models.UniqueConstraint(
fields=("setting", "group"), name="unique_site_group"
),
),
]
59 changes: 57 additions & 2 deletions hypha/apply/utils/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
from django.contrib.auth.models import Group
from django.db import models
from django.utils.translation import gettext_lazy as _
from wagtail.admin.panels import FieldPanel
from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from wagtail.admin.panels import FieldPanel, InlinePanel
from wagtail.contrib.settings.models import (
BaseGenericSetting,
BaseSiteSetting,
register_setting,
)
from wagtail.models import Orderable


@register_setting
Expand All @@ -28,3 +36,50 @@ class Meta:
panels = [
FieldPanel("download_page_size"),
]


@register_setting
class SubmissionsDetailsSetting(BaseGenericSetting, ClusterableModel):
"""Allows settings per Group to be specified in Wagtail Settings and retrieved by the Submissions Details code.
This class affords a setting for a single Site to be referenced by the SubmissionsDetailsSettings model. This way
the BaseSetting can be extended such that a setting appears in the Wagtail Settings menu but there can also be up to
one row per group for settings."""

class Meta:
verbose_name = "Submissions Details Page Setting"

panels = [InlinePanel("submissions_details_settings")]


class SubmissionsDetailsSettings(Orderable):
class Meta:
verbose_name = "Submissions Details Page Settings"
constraints = [
# There is a one-to-one relation for setting-to-site. Therefore, "setting" can be thought of as "site" here.
models.UniqueConstraint(
fields=["setting", "group"], name="unique_site_group"
),
]

setting = ParentalKey(
to=SubmissionsDetailsSetting,
on_delete=models.CASCADE,
related_name="submissions_details_settings",
)
group = models.ForeignKey(
to=Group,
on_delete=models.CASCADE,
)

# The actual settings that can apply to a given site/group combination follow.
sees_related_submissions = models.BooleanField(
default=False,
help_text=_(
"Should members of the above Group see the related submissions side panel when reviewing a submission?"
),
)

panels = [
FieldPanel("group"),
FieldPanel("sees_related_submissions"),
]

0 comments on commit ce9f737

Please sign in to comment.