diff --git a/enterprise_catalog/apps/catalog/admin.py b/enterprise_catalog/apps/catalog/admin.py
index a7217f27..415e3acb 100644
--- a/enterprise_catalog/apps/catalog/admin.py
+++ b/enterprise_catalog/apps/catalog/admin.py
@@ -1,6 +1,7 @@
from django.contrib import admin
from django.urls import reverse
-from django.utils.html import format_html
+from django.utils.html import format_html, format_html_join
+from django.utils.safestring import mark_safe
from edx_rbac.admin import UserRoleAssignmentAdmin
from enterprise_catalog.apps.catalog.constants import (
@@ -15,9 +16,20 @@
ContentMetadata,
EnterpriseCatalog,
EnterpriseCatalogRoleAssignment,
+ RestrictedCourseMetadata,
+ RestrictedRunAllowedForRestrictedCourse,
)
+def _html_list_from_objects(objs, viewname, str_callback=None):
+ str_callback = str_callback or str
+ return format_html_join(
+ sep=mark_safe('
'),
+ format_string='{}',
+ args_generator=((reverse(viewname, args=[obj.pk]), str_callback(obj)) for obj in objs),
+ )
+
+
class UnchangeableMixin(admin.ModelAdmin):
"""
Mixin for disabling changing models through the admin
@@ -59,17 +71,99 @@ class ContentMetadataAdmin(UnchangeableMixin):
)
readonly_fields = (
'associated_content_metadata',
- 'catalog_queries',
- 'get_catalog',
+ 'get_catalog_queries',
+ 'get_catalogs',
+ 'get_restricted_courses',
'modified',
)
+ exclude = (
+ 'catalog_queries',
+ )
+
+ @admin.display(description='Catalog Queries')
+ def get_catalog_queries(self, obj):
+ catalog_queries = obj.catalog_queries.all()
+ return _html_list_from_objects(
+ objs=catalog_queries,
+ viewname="admin:catalog_catalogquery_change",
+ str_callback=lambda cq: cq.short_str_for_listings(),
+ )
@admin.display(description='Enterprise Catalogs')
- def get_catalog(self, obj):
+ def get_catalogs(self, obj):
catalogs = EnterpriseCatalog.objects.filter(
catalog_query_id__in=obj.catalog_queries.all().values_list('id')
)
- return f"{list(catalogs)}"
+ return _html_list_from_objects(catalogs, "admin:catalog_enterprisecatalog_change")
+
+ @admin.display(description='Restricted For Courses')
+ def get_restricted_courses(self, obj):
+ restricted_runs_allowed_for_restricted_course = RestrictedRunAllowedForRestrictedCourse.objects.select_related(
+ 'course',
+ ).filter(
+ run=obj,
+ )
+ restricted_courses = (relationship.course for relationship in restricted_runs_allowed_for_restricted_course)
+ return _html_list_from_objects(restricted_courses, "admin:catalog_contentmetadata_change")
+
+
+@admin.register(RestrictedCourseMetadata)
+class RestrictedCourseMetadataAdmin(UnchangeableMixin):
+ """ Admin configuration for the custom RestrictedCourseMetadata model. """
+ list_display = (
+ 'content_key',
+ 'get_catalog_query_for_list',
+ 'get_unrestricted_parent',
+ )
+ search_fields = (
+ 'content_key',
+ 'catalog_query',
+ )
+ readonly_fields = (
+ 'get_catalog_query',
+ 'get_catalogs',
+ 'get_restricted_runs_allowed',
+ 'modified',
+ )
+ exclude = (
+ 'catalog_query',
+ )
+
+ @admin.display(
+ description='Catalog Query'
+ )
+ def get_catalog_query_for_list(self, obj):
+ link = reverse("admin:catalog_catalogquery_change", args=[obj.catalog_query.id])
+ return format_html('{}', link, obj.catalog_query.short_str_for_listings())
+
+ @admin.display(
+ description='Catalog Query'
+ )
+ def get_catalog_query(self, obj):
+ link = reverse("admin:catalog_catalogquery_change", args=[obj.catalog_query.id])
+ return format_html('{}', link, obj.catalog_query.pretty_print_content_filter())
+
+ @admin.display(
+ description='Unrestricted Parent'
+ )
+ def get_unrestricted_parent(self, obj):
+ link = reverse("admin:catalog_contentmetadata_change", args=[obj.unrestricted_parent.id])
+ return format_html('{}', link, str(obj.unrestricted_parent))
+
+ @admin.display(description='Enterprise Catalogs')
+ def get_catalogs(self, obj):
+ catalogs = EnterpriseCatalog.objects.filter(catalog_query=obj.catalog_query)
+ return _html_list_from_objects(catalogs, "admin:catalog_enterprisecatalog_change")
+
+ @admin.display(description='Restricted Runs Allowed')
+ def get_restricted_runs_allowed(self, obj):
+ restricted_runs_allowed_for_restricted_course = RestrictedRunAllowedForRestrictedCourse.objects.select_related(
+ 'run',
+ ).filter(
+ course=obj,
+ )
+ restricted_runs = (relationship.run for relationship in restricted_runs_allowed_for_restricted_course)
+ return _html_list_from_objects(restricted_runs, "admin:catalog_contentmetadata_change")
@admin.register(CatalogQuery)
diff --git a/enterprise_catalog/apps/catalog/migrations/0042_alter_restrictedcoursemetadata_display_name.py b/enterprise_catalog/apps/catalog/migrations/0042_alter_restrictedcoursemetadata_display_name.py
new file mode 100644
index 00000000..d3ea1168
--- /dev/null
+++ b/enterprise_catalog/apps/catalog/migrations/0042_alter_restrictedcoursemetadata_display_name.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.16 on 2024-10-11 00:52
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('catalog', '0041_restrictedcoursemetadata_and_more'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='historicalrestrictedcoursemetadata',
+ options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Restricted Course Metadata', 'verbose_name_plural': 'historical Restricted Course Metadata'},
+ ),
+ migrations.AlterModelOptions(
+ name='restrictedcoursemetadata',
+ options={'verbose_name': 'Restricted Course Metadata', 'verbose_name_plural': 'Restricted Course Metadata'},
+ ),
+ ]
diff --git a/enterprise_catalog/apps/catalog/models.py b/enterprise_catalog/apps/catalog/models.py
index 8d561bf5..aac2fb10 100644
--- a/enterprise_catalog/apps/catalog/models.py
+++ b/enterprise_catalog/apps/catalog/models.py
@@ -166,6 +166,14 @@ def __str__(self):
f"and content_filter '{self.pretty_print_content_filter()}'>"
)
+ def short_str_for_listings(self):
+ """
+ Return *short* human-readable string representation for listings.
+ """
+ return (
+ f""
+ )
+
class EnterpriseCatalog(TimeStampedModel):
"""
@@ -715,11 +723,7 @@ def __str__(self):
"""
Return human-readable string representation.
"""
- return (
- "".format(
- content_key=self.content_key
- )
- )
+ return f"<{self.__class__.__name__} for '{self.content_key}'>"
class ContentMetadata(BaseContentMetadata):
@@ -775,8 +779,8 @@ class RestrictedCourseMetadata(BaseContentMetadata):
.. no_pii:
"""
class Meta:
- verbose_name = _("Restricted Content Metadata")
- verbose_name_plural = _("Restricted Content Metadata")
+ verbose_name = _("Restricted Course Metadata")
+ verbose_name_plural = _("Restricted Course Metadata")
app_label = 'catalog'
unique_together = ('content_key', 'catalog_query')
@@ -810,6 +814,12 @@ class Meta:
)
history = HistoricalRecords()
+ def __str__(self):
+ """
+ Return human-readable string representation.
+ """
+ return f"<{self.__class__.__name__} for '{self.content_key}' and CatalogQuery ({self.catalog_query.id})>"
+
class RestrictedRunAllowedForRestrictedCourse(TimeStampedModel):
"""