From c40f0665d9315d4d149b20a51a5fa60897d15fcc Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Tue, 4 Feb 2025 17:46:04 +0530 Subject: [PATCH] Update some model admins with Snippets --- hypha/apply/funds/admin.py | 217 +++++++++++++++------------ hypha/apply/funds/edit_handlers.py | 8 + hypha/apply/funds/models/__init__.py | 35 +++++ hypha/apply/funds/models/forms.py | 29 ++++ hypha/apply/funds/wagtail_hooks.py | 4 - hypha/apply/utils/admin.py | 2 +- 6 files changed, 191 insertions(+), 104 deletions(-) diff --git a/hypha/apply/funds/admin.py b/hypha/apply/funds/admin.py index 30bdfa3706..c5c50da6e0 100644 --- a/hypha/apply/funds/admin.py +++ b/hypha/apply/funds/admin.py @@ -1,7 +1,13 @@ -from django.urls import re_path +from django.urls import reverse from django.utils.safestring import mark_safe -from wagtail.contrib.modeladmin.helpers import PermissionHelper -from wagtail.contrib.modeladmin.options import ModelAdmin, ModelAdminGroup +from wagtail.admin.menu import MenuItem +from wagtail.contrib.modeladmin.options import ( + ModelAdmin, + ModelAdminGroup, + modeladmin_register, +) +from wagtail.snippets.models import register_snippet +from wagtail.snippets.views.snippets import SnippetViewSet from hypha.apply.categories.admin import CategoryAdmin, MetaTermAdmin from hypha.apply.determinations.admin import ( @@ -11,33 +17,57 @@ ) from hypha.apply.funds.models import ReviewerRole, ReviewerSettings, ScreeningStatus from hypha.apply.review.admin import ReviewFormAdmin -from hypha.apply.utils.admin import AdminIcon, ListRelatedMixin, RelatedFormsMixin +from hypha.apply.utils.admin import AdminIcon, RelatedFormsMixin from hypha.core.wagtail.admin.options import SettingModelAdmin from .admin_helpers import ( - ApplicationFormButtonHelper, ButtonsWithPreview, - FormsFundRoundListFilter, RoundAdminURLHelper, RoundFundChooserView, RoundStateListFilter, ) -from .admin_views import ( - CopyApplicationFormViewClass, - CreateApplicationFormView, - EditApplicationFormView, -) from .models import ( ApplicationForm, ApplicationSettings, - FundType, - LabType, RequestForPartners, Round, SealedRound, ) +class ScreeningStatusAdmin(SnippetViewSet): + model = ScreeningStatus + icon = str(AdminIcon.SCREENING_STATUS) + list_display = ("title", "yes", "default") + + def user_can_edit(self, user): + return user.is_superuser + + def user_can_delete(self, user): + return user.is_superuser + + +class ReviewerRoleAdmin(SnippetViewSet): + model = ReviewerRole + icon = str(AdminIcon.REVIEWER_ROLE) + menu_label = "Reviewer Roles" + + +# having issues with Locale during save +# class FundAdmin(SnippetViewSet, RelatedFormsMixin): +# model = FundType +# icon = str(AdminIcon.FUND) +# menu_label = "Funds" +# list_display = ("title", "application_forms", "get_review_forms", "get_determination_forms") +# +# +# class LabAdmin(SnippetViewSet, RelatedFormsMixin): +# model = LabType +# icon = str(AdminIcon.LAB) +# menu_label = "Labs" +# list_display = ("title", "application_forms", "get_review_forms", "get_determination_forms") + + class BaseRoundAdmin(ModelAdmin): choose_parent_view_class = RoundFundChooserView choose_parent_template_name = "funds/admin/parent_chooser.html" @@ -67,30 +97,6 @@ def fund(self, obj): return mark_safe(url_tag) -class ScreeningStatusPermissionHelper(PermissionHelper): - def user_can_edit_obj(self, user, obj): - """ - Return a boolean to indicate whether `user` is permitted to 'change' - a specific `self.model` instance. - """ - return user.is_superuser - - def user_can_delete_obj(self, user, obj): - """ - Return a boolean to indicate whether `user` is permitted to 'delete' - a specific `self.model` instance. - """ - return user.is_superuser - - -class ScreeningStatusAdmin(ModelAdmin): - model = ScreeningStatus - menu_icon = str(AdminIcon.SCREENING_STATUS) - list_display = ("title", "yes", "default") - permission_helper_class = ScreeningStatusPermissionHelper - list_display = ("title", "yes", "default") - - class SealedRoundAdmin(BaseRoundAdmin): model = SealedRound menu_icon = str(AdminIcon.SEALED_ROUND) @@ -98,75 +104,51 @@ class SealedRoundAdmin(BaseRoundAdmin): list_display = ("title", "fund", "start_date", "end_date") -class FundAdmin(ModelAdmin, RelatedFormsMixin): - model = FundType - menu_icon = str(AdminIcon.FUND) - menu_label = "Funds" - list_display = ("title", "application_forms", "review_forms", "determination_forms") - - +# class FundAdmin(ModelAdmin, RelatedFormsMixin): +# model = FundType +# menu_icon = str(AdminIcon.FUND) +# menu_label = "Funds" +# list_display = ("title", "application_forms", "review_forms", "determination_forms") +# +# class RFPAdmin(ModelAdmin): model = RequestForPartners menu_icon = str(AdminIcon.REQUEST_FOR_PARTNERS) menu_label = "Request For Partners" -class LabAdmin(ModelAdmin, RelatedFormsMixin): - model = LabType - menu_icon = str(AdminIcon.LAB) - menu_label = "Labs" - list_display = ("title", "application_forms", "review_forms", "determination_forms") +# +# class LabAdmin(ModelAdmin, RelatedFormsMixin): +# model = LabType +# menu_icon = str(AdminIcon.LAB) +# menu_label = "Labs" +# list_display = ("title", "application_forms", "review_forms", "determination_forms") +# -class ReviewerRoleAdmin(ModelAdmin): - model = ReviewerRole - menu_icon = str(AdminIcon.REVIEWER_ROLE) - menu_label = "Reviewer Roles" +class ApplicationFormAdmin(SnippetViewSet): + model = ApplicationForm + menu_icon = str(AdminIcon.APPLICATION_FORM) + list_display = ("name", "used_by") + def get_queryset(self, request): + # Explicitly define the queryset for this snippet + qs = self.model.objects.all() -class DeletePermission(PermissionHelper, ListRelatedMixin): - related_models = [ - ("applicationbaseform", "application"), - ("roundbaseform", "round"), - ("labbaseform", "lab"), - ] + # Prefetch related fields for the 'used_by' logic + related = [ + "applicationbaseform_set__application", + "roundbaseform_set__round", + "labbaseform_set__lab", + ] + return qs.prefetch_related(*related) - def user_can_delete_obj(self, user, obj): - if str(self.used_by(obj)): - return False - return True - -class ApplicationFormAdmin(ListRelatedMixin, ModelAdmin): - model = ApplicationForm - menu_icon = str(AdminIcon.APPLICATION_FORM) - list_display = ("name", "used_by") - list_filter = (FormsFundRoundListFilter,) - permission_helper_class = DeletePermission - button_helper_class = ApplicationFormButtonHelper - create_view_class = CreateApplicationFormView - edit_view_class = EditApplicationFormView - - related_models = [ - ("applicationbaseform", "application"), - ("roundbaseform", "round"), - ("labbaseform", "lab"), - ] - - def copy_form_view(self, request, instance_pk): - kwargs = {"model_admin": self, "form_pk": instance_pk} - view_class = CopyApplicationFormViewClass - return view_class.as_view(**kwargs)(request) - - def get_admin_urls_for_registration(self): - """Add the url for creating form copy.""" - urls = super().get_admin_urls_for_registration() - copy_form_url = re_path( - self.url_helper.get_action_url_pattern("copy_form"), - self.copy_form_view, - name=self.url_helper.get_action_url_name("copy_form"), - ) - return urls + (copy_form_url,) +register_snippet(ApplicationFormAdmin) +register_snippet(ScreeningStatusAdmin) +register_snippet(ReviewerRoleAdmin) +# register_snippet(FundAdmin) +# register_snippet(LabAdmin) class ApplicationSettingAdmin(SettingModelAdmin): @@ -183,10 +165,7 @@ class ApplyAdminGroup(ModelAdminGroup): items = ( RoundAdmin, SealedRoundAdmin, - FundAdmin, - LabAdmin, RFPAdmin, - ApplicationFormAdmin, ApplicationSettingAdmin, ReviewFormAdmin, ReviewerSettingAdmin, @@ -194,7 +173,47 @@ class ApplyAdminGroup(ModelAdminGroup): DeterminationMessageSettingsAdmin, DeterminationFormSettingsAdmin, CategoryAdmin, - ScreeningStatusAdmin, - ReviewerRoleAdmin, MetaTermAdmin, ) + + def get_submenu_items(self): + # Get existing items + submenu_items = super().get_submenu_items() + + snippets = [ + { + "model": ReviewerRole, + "menu_label": "Reviewer Roles", + "icon_name": str(AdminIcon.REVIEWER_ROLE), + }, + # {"model": FundType, "menu_label": "Funds", "icon_name": str(AdminIcon.FUND)}, + # {"model": LabType, "menu_label": "Labs", "icon_name": str(AdminIcon.LAB)}, + # {"model": RequestForPartners, "menu_label": "Request For Partners", "icon_name": str(AdminIcon.REQUEST_FOR_PARTNERS)}, + { + "model": ScreeningStatus, + "menu_label": "Screening Decisions", + "icon_name": str(AdminIcon.SCREENING_STATUS), + }, + { + "model": ApplicationForm, + "menu_label": "Application Forms", + "icon_name": str(AdminIcon.APPLICATION_FORM), + }, + ] + + for snippet in snippets: + snippet_url = reverse( + f"wagtailsnippets_{snippet['model']._meta.app_label}_{snippet['model']._meta.model_name}:list", + ) + submenu_items.append( + MenuItem( + snippet["menu_label"], + snippet_url, + icon_name=snippet["icon_name"], + ) + ) + + return submenu_items + + +modeladmin_register(ApplyAdminGroup) diff --git a/hypha/apply/funds/edit_handlers.py b/hypha/apply/funds/edit_handlers.py index 3b01944d8a..cb519aac4e 100644 --- a/hypha/apply/funds/edit_handlers.py +++ b/hypha/apply/funds/edit_handlers.py @@ -3,12 +3,20 @@ from django.urls import reverse from wagtail.admin.panels import FieldPanel, Panel from wagtail.models import Page +from wagtail.snippets.models import get_snippet_models def reverse_edit(obj): if isinstance(obj, Page): return reverse("wagtailadmin_pages:edit", args=(obj.id,)) + snippet_models = get_snippet_models() + if obj.__class__ in snippet_models: + return reverse( + f"wagtailsnippets_{obj._meta.app_label}_{obj._meta.model_name}:edit", + args=(obj.id,), + ) + url_name = f"{obj._meta.app_label}_{obj._meta.model_name}_modeladmin_edit" return reverse(url_name, args=(obj.id,)) diff --git a/hypha/apply/funds/models/__init__.py b/hypha/apply/funds/models/__init__.py index 09afb0d643..fdd128ef66 100644 --- a/hypha/apply/funds/models/__init__.py +++ b/hypha/apply/funds/models/__init__.py @@ -1,3 +1,4 @@ +from django.contrib.admin import display from django.utils.translation import gettext_lazy as _ from .application_revisions import ApplicationRevision @@ -35,6 +36,23 @@ class FundType(ApplicationBase): class Meta: verbose_name = _("Fund") + @property + def application_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.forms.all()) + + @property + @display(description=_("Review Forms")) + def get_review_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.review_forms.all()) + + @property + @display(description=_("Determination Forms")) + def get_determination_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.determination_forms.all()) + class RequestForPartners(ApplicationBase): subpage_types = ["funds.Round", "funds.SealedRound"] @@ -58,3 +76,20 @@ def __init__(self, *args, **kwargs): class LabType(LabBase): class Meta: verbose_name = _("Lab") + + @property + def application_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.forms.all()) + + @property + @display(description=_("Review Forms")) + def get_review_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.review_forms.all()) + + @property + @display(description=_("Determination Forms")) + def get_determination_forms(self): + # Return related forms as a comma-separated list + return ", ".join(form.form.name for form in self.determination_forms.all()) diff --git a/hypha/apply/funds/models/forms.py b/hypha/apply/funds/models/forms.py index a0d8c36b5b..0c77753ecb 100644 --- a/hypha/apply/funds/models/forms.py +++ b/hypha/apply/funds/models/forms.py @@ -1,4 +1,6 @@ from django.db import models +from django.urls import reverse +from django.utils.safestring import mark_safe from modelcluster.fields import ParentalKey from wagtail.admin.panels import FieldPanel from wagtail.fields import StreamField @@ -23,6 +25,33 @@ class ApplicationForm(models.Model): def __str__(self): return self.name + @property + def used_by(self): + rows = [] + related_models = [ + ("applicationbaseform", "application"), + ("roundbaseform", "round"), + ("labbaseform", "lab"), + ] + + for form, field in related_models: + related_values = getattr(self, f"{form}_set").values( + f"{field}__title", f"{field}__id" + ) + for value in related_values: + title = value.get(f"{field}__title") + obj_id = value.get(f"{field}__id") + if title and obj_id: + edit_url = reverse("wagtailadmin_pages:edit", args=(obj_id,)) + rows.append( + f'' + f' {title}' + ) + elif title: + rows.append(f"{title}") + return mark_safe(", ".join(rows)) + class AbstractRelatedForm(Orderable): FIRST_STAGE = 1 diff --git a/hypha/apply/funds/wagtail_hooks.py b/hypha/apply/funds/wagtail_hooks.py index 3dbbafdc6f..0516e22b06 100644 --- a/hypha/apply/funds/wagtail_hooks.py +++ b/hypha/apply/funds/wagtail_hooks.py @@ -1,13 +1,9 @@ from django.contrib.auth.models import Permission from wagtail import hooks -from wagtail.contrib.modeladmin.options import modeladmin_register -from .admin import ApplyAdminGroup from .admin_views import custom_admin_round_copy_view from .models import RoundBase -modeladmin_register(ApplyAdminGroup) - @hooks.register("before_create_page") def before_create_page(request, parent_page, page_class): diff --git a/hypha/apply/utils/admin.py b/hypha/apply/utils/admin.py index 6f0d633f7b..ee034bdfff 100644 --- a/hypha/apply/utils/admin.py +++ b/hypha/apply/utils/admin.py @@ -118,7 +118,7 @@ def application_forms(self, obj): def build_urls(application_forms): for application_form in application_forms: url = reverse( - "funds_applicationform_modeladmin_edit", + "wagtailsnippets_funds_applicationform:edit", args=[application_form.form.id], ) yield f'{application_form}'