feat(library): register IngestJob admin and link Neo4j views
- Add read-only ModelAdmin for IngestJob with filters, search, and date hierarchy for operational visibility - Inject proxy entries into the admin index for Neo4j-backed entities (Libraries, Concepts, Search, Embedding pipeline) that link to existing CRUD views in library/views.py - Makes library content discoverable from /admin/ without pretending neomodel StructuredNodes are Django ORM models
This commit is contained in:
@@ -1,5 +1,123 @@
|
|||||||
# Library app does not use standard Django admin (neomodel StructuredNodes
|
"""Admin registrations for the library app.
|
||||||
# are not Django ORM models). Custom admin views are provided as regular
|
|
||||||
# app views in library/views.py, rendered within Themis's template structure.
|
Most library content (Library, Collection, Item, Chunk, Concept, Image,
|
||||||
#
|
ImageEmbedding) lives in Neo4j via neomodel and cannot use Django's
|
||||||
# The embedding pipeline dashboard is at /library/embedding/
|
standard ModelAdmin. Those entities have full CRUD pages in
|
||||||
|
``library/views.py``; this module wires proxy rows into the admin index
|
||||||
|
so they are discoverable from ``/admin/``.
|
||||||
|
|
||||||
|
``IngestJob`` is a regular Django ORM model and gets a normal,
|
||||||
|
read-only ModelAdmin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import NoReverseMatch, reverse
|
||||||
|
|
||||||
|
from .models import IngestJob
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(IngestJob)
|
||||||
|
class IngestJobAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"library_uid",
|
||||||
|
"item_uid",
|
||||||
|
"source",
|
||||||
|
"source_ref",
|
||||||
|
"progress",
|
||||||
|
"retry_count",
|
||||||
|
"created_at",
|
||||||
|
"completed_at",
|
||||||
|
)
|
||||||
|
list_filter = ("status", "source", "created_at")
|
||||||
|
search_fields = (
|
||||||
|
"id",
|
||||||
|
"library_uid",
|
||||||
|
"item_uid",
|
||||||
|
"source_ref",
|
||||||
|
"content_hash",
|
||||||
|
"title",
|
||||||
|
"celery_task_id",
|
||||||
|
)
|
||||||
|
date_hierarchy = "created_at"
|
||||||
|
ordering = ("-created_at",)
|
||||||
|
readonly_fields = (
|
||||||
|
"id",
|
||||||
|
"item_uid",
|
||||||
|
"library_uid",
|
||||||
|
"celery_task_id",
|
||||||
|
"status",
|
||||||
|
"progress",
|
||||||
|
"error",
|
||||||
|
"retry_count",
|
||||||
|
"chunks_created",
|
||||||
|
"concepts_extracted",
|
||||||
|
"embedding_model",
|
||||||
|
"content_hash",
|
||||||
|
"source",
|
||||||
|
"source_ref",
|
||||||
|
"s3_key",
|
||||||
|
"title",
|
||||||
|
"file_type",
|
||||||
|
"file_size",
|
||||||
|
"collection_uid",
|
||||||
|
"created_at",
|
||||||
|
"started_at",
|
||||||
|
"completed_at",
|
||||||
|
)
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_change_permission(self, request, obj=None):
|
||||||
|
# View-only; superusers can still open the detail page.
|
||||||
|
return request.method in ("GET", "HEAD")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Neo4j-resident entities: proxy entries on the admin index that link to the
|
||||||
|
# existing CRUD views in library/views.py. No shim models — just an honest
|
||||||
|
# signpost so /admin/ doesn't pretend the library is empty.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_NEO4J_ADMIN_LINKS = [
|
||||||
|
("Libraries", "library:library-list"),
|
||||||
|
("Concepts", "library:concept-list"),
|
||||||
|
("Search", "library:search"),
|
||||||
|
("Embedding pipeline", "library:embedding-dashboard"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
_original_get_app_list = admin.site.get_app_list
|
||||||
|
|
||||||
|
|
||||||
|
def _get_app_list_with_library_links(request, app_label=None):
|
||||||
|
app_list = _original_get_app_list(request, app_label)
|
||||||
|
for app in app_list:
|
||||||
|
if app["app_label"] != "library":
|
||||||
|
continue
|
||||||
|
for name, url_name in _NEO4J_ADMIN_LINKS:
|
||||||
|
try:
|
||||||
|
url = reverse(url_name)
|
||||||
|
except NoReverseMatch:
|
||||||
|
continue
|
||||||
|
app["models"].append(
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
"object_name": name,
|
||||||
|
"perms": {
|
||||||
|
"add": False,
|
||||||
|
"change": True,
|
||||||
|
"delete": False,
|
||||||
|
"view": True,
|
||||||
|
},
|
||||||
|
"admin_url": url,
|
||||||
|
"add_url": None,
|
||||||
|
"view_only": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return app_list
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.get_app_list = _get_app_list_with_library_links
|
||||||
|
|||||||
Reference in New Issue
Block a user