diff --git a/apps/iiif/manifests/admin.py b/apps/iiif/manifests/admin.py index 50288374c..d0ec3aef2 100644 --- a/apps/iiif/manifests/admin.py +++ b/apps/iiif/manifests/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from django.http import HttpResponseRedirect from django.http.request import HttpRequest +from django.urls import reverse from django.urls.conf import path from import_export import resources, fields from import_export.admin import ImportExportModelAdmin @@ -9,7 +10,7 @@ from django_summernote.admin import SummernoteModelAdmin from .models import Manifest, Note, ImageServer, RelatedLink from .forms import ManifestAdminForm -from .views import AddToCollectionsView +from .views import AddToCollectionsView, MetadataImportView from ..kollections.models import Collection class ManifestResource(resources.ModelResource): @@ -56,6 +57,7 @@ class ManifestAdmin(ImportExportModelAdmin, SummernoteModelAdmin, admin.ModelAdm form = ManifestAdminForm actions = ['add_to_collections_action'] inlines = [RelatedLinksInline] + change_list_template = 'admin/change_list_override.html' def add_to_collections_action(self, request, queryset): """Action choose manifests to add to collections""" @@ -72,6 +74,12 @@ def get_urls(self): self.admin_site.admin_view(AddToCollectionsView.as_view()), {'model_admin': self, }, name="AddManifestsToCollections", + ), + path( + 'manifest_metadata_import/', + self.admin_site.admin_view(MetadataImportView.as_view()), + {'model_admin': self, }, + name="MultiManifestMetadataImport", ) ] return my_urls + urls diff --git a/apps/iiif/manifests/forms.py b/apps/iiif/manifests/forms.py index 6df732c25..0755a12af 100644 --- a/apps/iiif/manifests/forms.py +++ b/apps/iiif/manifests/forms.py @@ -1,9 +1,14 @@ """Django Forms for export.""" +import csv +from io import StringIO import logging from django import forms from django.contrib.admin import site as admin_site, widgets -from .models import Language, Manifest -from ..canvases.models import Canvas +from django.core.validators import FileExtensionValidator + +from apps.iiif.manifests.models import Manifest +from apps.iiif.canvases.models import Canvas +from apps.ingest.services import normalize_header LOGGER = logging.getLogger(__name__) @@ -49,3 +54,42 @@ def __init__(self, *args, **kwargs): self.instance._meta.get_field('collections').remote_field, admin_site, ) + +class ManifestCSVImportForm(forms.Form): + """Form to import a CSV and update the metadata for multiple manifests""" + + csv_file = forms.FileField( + required=True, + validators=[FileExtensionValidator(allowed_extensions=['csv'])], + label="CSV File", + help_text=""" +
Provide a CSV with a pid column, whose value in each row must match + the PID of an existing volume. Additional columns will be used to update the volume's + metadata.
+Columns matching Manifest model field names will update those + fields directly, and any additional columns will be used to populate the volume's + metadata JSON field.
+ """, + ) + + def clean(self): + # check csv has pid column + super().clean() + csv_file = self.cleaned_data.get('csv_file') + if csv_file: + reader = csv.DictReader( + normalize_header( + StringIO(csv_file.read().decode('utf-8')) + ), + ) + if 'pid' not in reader.fieldnames: + self.add_error( + 'metadata_spreadsheet', + forms.ValidationError( + """Spreadsheet must have pid column. Check to ensure there + are no stray characters in the header row.""" + ), + ) + # return back to start of file so we can read again + csv_file.seek(0, 0) + return self.cleaned_data diff --git a/apps/iiif/manifests/templates/admin/change_list_override.html b/apps/iiif/manifests/templates/admin/change_list_override.html new file mode 100644 index 000000000..1f76d59dc --- /dev/null +++ b/apps/iiif/manifests/templates/admin/change_list_override.html @@ -0,0 +1,26 @@ +{% extends "admin/change_list.html" %} + +{% load i18n %} + +{# adapted from django-import-export #} +{% block object-tools %} +