diff --git a/apps/common/views.py b/apps/common/views.py index 2c6c32c7b0..48f27bffd9 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -15,8 +15,8 @@ def password_change_done(request): def multi_object_action_view(request): """ - Proxy view called first when usuing a multi object action, which - then redirects to the appropiate specialized view + Proxy view called first when usuing a multi object action, which + then redirects to the appropiate specialized view """ action = request.GET.get('action', None) diff --git a/apps/documents/admin.py b/apps/documents/admin.py index bc1a0c7c15..e63bfec2af 100644 --- a/apps/documents/admin.py +++ b/apps/documents/admin.py @@ -1,8 +1,8 @@ from django.contrib import admin from documents.models import MetadataType, DocumentType, Document, \ - DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename, \ - MetadataIndex, DocumentPage, MetadataGroup, \ + MetadataSet, MetadataSetItem, DocumentMetadata, \ + DocumentTypeFilename, MetadataIndex, DocumentPage, MetadataGroup, \ MetadataGroupItem, DocumentPageTransformation, RecentDocument from filesystem_serving.admin import DocumentMetadataIndexInline @@ -19,8 +19,8 @@ class MetadataIndexInline(admin.StackedInline): allow_add = True -class DocumentTypeMetadataTypeInline(admin.StackedInline): - model = DocumentTypeMetadataType +class MetadataSetItemInline(admin.StackedInline): + model = MetadataSetItem extra = 1 classes = ('collapse-open',) allow_add = True @@ -35,8 +35,7 @@ class DocumentTypeFilenameInline(admin.StackedInline): class DocumentTypeAdmin(admin.ModelAdmin): inlines = [ - DocumentTypeFilenameInline, DocumentTypeMetadataTypeInline, - MetadataIndexInline + DocumentTypeFilenameInline, MetadataIndexInline ] @@ -86,6 +85,11 @@ class RecentDocumentAdmin(admin.ModelAdmin): date_hierarchy = 'datetime_accessed' +class MetadataSetAdmin(admin.ModelAdmin): + inlines = [MetadataSetItemInline] + #filter_horizontal = ['document_type'] + + admin.site.register(MetadataType, MetadataTypeAdmin) admin.site.register(DocumentType, DocumentTypeAdmin) admin.site.register(Document, DocumentAdmin) @@ -93,3 +97,4 @@ admin.site.register(MetadataGroup, MetadataGroupAdmin) admin.site.register(DocumentPageTransformation, DocumentPageTransformationAdmin) admin.site.register(RecentDocument, RecentDocumentAdmin) +admin.site.register(MetadataSet, MetadataSetAdmin) diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 257a147f64..6f57f38080 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -15,10 +15,11 @@ from common.forms import DetailForm from common.literals import PAGE_SIZE_CHOICES, PAGE_ORIENTATION_CHOICES from common.conf.settings import DEFAULT_PAPER_SIZE from common.conf.settings import DEFAULT_PAGE_ORIENTATION +from common.utils import urlquote from documents.staging import StagingFile -from documents.models import Document, DocumentType, DocumentTypeMetadataType, \ - DocumentPage, DocumentPageTransformation +from documents.models import Document, DocumentType, \ + DocumentPage, DocumentPageTransformation, MetadataSet, MetadataType from documents.conf.settings import AVAILABLE_FUNCTIONS from documents.conf.settings import AVAILABLE_MODELS @@ -154,7 +155,8 @@ class DocumentForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(DocumentForm, self).__init__(*args, **kwargs) if 'initial' in kwargs: - if 'document_type' in kwargs['initial']: + document_type = kwargs['initial'].get('document_type', None) + if document_type: if 'document_type' in self.fields: #To allow merging with DocumentForm_edit self.fields['document_type'].widget = forms.HiddenInput() @@ -167,7 +169,7 @@ class DocumentForm(forms.ModelForm): class Meta: model = Document - exclude = ('description', 'tags') + exclude = ('description', 'tags', 'document_type') new_filename = forms.CharField( label=_('New document filename'), required=False @@ -228,7 +230,8 @@ class StagingDocumentForm(forms.Form): pass if 'initial' in kwargs: - if 'document_type' in kwargs['initial']: + document_type = kwargs['initial'].get('document_type', None) + if document_type: filenames_qs = kwargs['initial']['document_type'].documenttypefilename_set.filter(enabled=True) if filenames_qs.count() > 0: self.fields['document_type_available_filenames'] = forms.ModelChoiceField( @@ -243,7 +246,7 @@ class StagingDocumentForm(forms.Form): class DocumentTypeSelectForm(forms.Form): - document_type = forms.ModelChoiceField(queryset=DocumentType.objects.all()) + document_type = forms.ModelChoiceField(queryset=DocumentType.objects.all(), label=(u'Document type'), required=False) class MetadataForm(forms.Form): @@ -253,9 +256,11 @@ class MetadataForm(forms.Form): #Set form fields initial values if 'initial' in kwargs: self.metadata_type = kwargs['initial'].pop('metadata_type', None) - self.document_type = kwargs['initial'].pop('document_type', None) + #self.document_type = kwargs['initial'].pop('document_type', None) - required = self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required + # FIXME: + #required = self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required + required = False required_string = u'' if required: self.fields['value'].required = True @@ -295,19 +300,28 @@ MetadataFormSet = formset_factory(MetadataForm, extra=0) class DocumentCreateWizard(BoundFormWizard): def generate_metadata_initial_values(self): initial = [] - for item in DocumentTypeMetadataType.objects.filter(document_type=self.document_type): + for metadata_type in self.metadata_types: initial.append({ - 'metadata_type': item.metadata_type, - 'document_type': self.document_type, + 'metadata_type': metadata_type, }) + + for metadata_set in self.metadata_sets: + for metadata_set_item in metadata_set.metadatasetitem_set.all(): + data = { + 'metadata_type': metadata_set_item.metadata_type, + } + if data not in initial: + initial.append(data) + return initial def __init__(self, *args, **kwargs): - self.urldata = [] + self.query_dict = {} self.multiple = kwargs.pop('multiple', True) self.step_titles = kwargs.pop('step_titles', [ - _(u'step 1 of 2: Document type'), - _(u'step 2 of 2: Document metadata'), + _(u'step 1 of 3: Document type'), + _(u'step 2 of 3: Metadata selection'), + _(u'step 3 of 3: Document metadata'), ]) self.document_type = kwargs.pop('document_type', None) @@ -328,25 +342,31 @@ class DocumentCreateWizard(BoundFormWizard): def process_step(self, request, form, step): if isinstance(form, DocumentTypeSelectForm): self.document_type = form.cleaned_data['document_type'] - self.initial = {1: self.generate_metadata_initial_values()} + + if isinstance(form, MetadataSelectionForm): + self.metadata_sets = form.cleaned_data['metadata_sets'] + self.metadata_types = form.cleaned_data['metadata_types'] + self.initial = {2: self.generate_metadata_initial_values()} if isinstance(form, MetadataFormSet): for identifier, metadata in enumerate(form.cleaned_data): - if metadata['value']: - self.urldata.append(('metadata%s_id' % identifier, metadata['id'])) - self.urldata.append(('metadata%s_value' % identifier, metadata['value'])) - + self.query_dict['metadata%s_id' % identifier] = metadata['id'] + self.query_dict['metadata%s_value' % identifier] = metadata['value'] + def get_template(self, step): return 'generic_wizard.html' def done(self, request, form_list): if self.multiple: - view = 'upload_multiple_documents_with_type' + view = 'upload_document_multiple' else: - view = 'upload_document_with_type' - - url = reverse(view, args=[self.document_type.id]) - return HttpResponseRedirect('%s?%s' % (url, urlencode(self.urldata))) + view = 'upload_document' + + if self.document_type: + self.query_dict['document_type_id'] = self.document_type.pk + + url = urlquote(reverse(view), self.query_dict) + return HttpResponseRedirect(url) class MetaDataImageWidget(forms.widgets.Widget): @@ -434,3 +454,8 @@ class PrintForm(forms.Form): custom_page_height = forms.CharField(label=_(u'Custom page height'), required=False) page_orientation = forms.ChoiceField(choices=PAGE_ORIENTATION_CHOICES, initial=DEFAULT_PAGE_ORIENTATION, label=_(u'Page orientation'), required=True) page_range = forms.CharField(label=_(u'Page range'), required=False) + + +class MetadataSelectionForm(forms.Form): + metadata_sets = forms.ModelMultipleChoiceField(queryset=MetadataSet.objects.all(), label=_(u'Metadata sets'), required=False) + metadata_types = forms.ModelMultipleChoiceField(queryset=MetadataType.objects.all(), label=_(u'Metadata'), required=False) diff --git a/apps/documents/metadata.py b/apps/documents/metadata.py index f6fb1f4007..4c891e2ede 100644 --- a/apps/documents/metadata.py +++ b/apps/documents/metadata.py @@ -38,20 +38,22 @@ def save_metadata_list(metadata_list, document): '''Takes a list of metadata values and associates a document to it ''' for item in metadata_list: - if item['value']: - save_metadata(item, document) - else: - #If there is no metadata value, delete the metadata entry - #completely from the document - try: - metadata_type = MetadataType.objects.get(id=item['id']) - document_metadata = DocumentMetadata.objects.get( - document=document, - metadata_type=metadata_type - ) - document_metadata.delete() - except ObjectDoesNotExist: - pass + save_metadata(item, document) + + #if item['value']: + # save_metadata(item, document) + #else: + # #If there is no metadata value, delete the metadata entry + # #completely from the document + # try: + # metadata_type = MetadataType.objects.get(id=item['id']) + # document_metadata = DocumentMetadata.objects.get( + # document=document, + # metadata_type=metadata_type + # ) + # document_metadata.delete() + # except ObjectDoesNotExist: + # pass def save_metadata(metadata_dict, document): diff --git a/apps/documents/models.py b/apps/documents/models.py index 6f069f5f78..355d4f70e9 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -52,7 +52,7 @@ class Document(models.Model): """ Defines a single document with it's fields and properties """ - document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) + document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'), null=True, blank=True) file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file')) uuid = models.CharField(max_length=48, default=UUID_FUNCTION(), blank=True, editable=False) file_mimetype = models.CharField(max_length=64, default='', editable=False) @@ -211,7 +211,7 @@ available_models_string = (_(u' Available models: %s') % u','.join([name for nam class MetadataType(models.Model): - name = models.CharField(max_length=48, verbose_name=_(u'name'), help_text=_(u'Do not use python reserved words.')) + name = models.CharField(max_length=48, verbose_name=_(u'name'), help_text=_(u'Do not use python reserved words, or spaces.')) title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True) default = models.CharField(max_length=128, blank=True, null=True, verbose_name=_(u'default'), @@ -229,21 +229,32 @@ class MetadataType(models.Model): verbose_name_plural = _(u'metadata types') -class DocumentTypeMetadataType(models.Model): +class MetadataSet(models.Model): + title = models.CharField(max_length=48, verbose_name=_(u'title')) + + def __unicode__(self): + return self.title if self.title else self.name + + class Meta: + verbose_name = _(u'metadata set') + verbose_name_plural = _(u'metadata set') + + +class MetadataSetItem(models.Model): """ - Define the set of metadata that relates to a single document type + Define the set of metadata that relates to a set or group of + metadata fields """ - document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) + metadata_set = models.ForeignKey(MetadataSet, verbose_name=_(u'metadata set')) metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type')) - required = models.BooleanField(default=True, verbose_name=_(u'required')) - #TODO: override default for this document type + #required = models.BooleanField(default=True, verbose_name=_(u'required')) def __unicode__(self): return unicode(self.metadata_type) class Meta: - verbose_name = _(u'document type metadata type connector') - verbose_name_plural = _(u'document type metadata type connectors') + verbose_name = _(u'metadata set item') + verbose_name_plural = _(u'metadata set items') available_indexing_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_INDEXING_FUNCTIONS.items()])) if AVAILABLE_INDEXING_FUNCTIONS else u'' diff --git a/apps/documents/urls.py b/apps/documents/urls.py index ea53a547d2..e29ddd1b24 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -14,8 +14,8 @@ urlpatterns = patterns('documents.views', url(r'^document/list/recent/$', 'document_list_recent', (), 'document_list_recent'), url(r'^document/create/from/local/single/$', 'document_create', {'multiple': False}, 'document_create'), url(r'^document/create/from/local/multiple/$', 'document_create', {'multiple': True}, 'document_create_multiple'), - url(r'^document/type/(?P\d+)/upload/single/$', 'upload_document_with_type', {'multiple': False}, 'upload_document_with_type'), - url(r'^document/type/(?P\d+)/upload/multiple/$', 'upload_document_with_type', {'multiple': True}, 'upload_multiple_documents_with_type'), + url(r'^document/type/upload/single/$', 'upload_document_with_type', {'multiple': False}, 'upload_document'), + url(r'^document/type/upload/multiple/$', 'upload_document_with_type', {'multiple': True}, 'upload_document_multiple'), url(r'^document/(?P\d+)/$', 'document_view', (), 'document_view'), url(r'^document/(?P\d+)/simple/$', 'document_view_simple', (), 'document_view_simple'), url(r'^document/(?P\d+)/delete/$', 'document_delete', (), 'document_delete'), diff --git a/apps/documents/views.py b/apps/documents/views.py index 6076abfda1..cf07705faf 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -1,7 +1,6 @@ import os import zipfile import urlparse -import urllib from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponseRedirect @@ -56,10 +55,10 @@ from documents import PERMISSION_DOCUMENT_CREATE, \ from documents.forms import DocumentTypeSelectForm, DocumentCreateWizard, \ DocumentForm, DocumentForm_edit, DocumentForm_view, \ - StagingDocumentForm, DocumentTypeMetadataType, DocumentPreviewForm, \ + StagingDocumentForm, DocumentPreviewForm, \ MetadataFormSet, DocumentPageForm, DocumentPageTransformationForm, \ DocumentContentForm, DocumentPageForm_edit, MetaDataGroupForm, \ - DocumentPageForm_text, PrintForm + DocumentPageForm_text, PrintForm, MetadataSelectionForm from documents.metadata import save_metadata_list, \ decode_metadata_from_url, metadata_repr_as_list @@ -85,15 +84,7 @@ def document_list(request, object_list=None, title=None): def document_create(request, multiple=True): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_CREATE]) - if DocumentType.objects.all().count() == 1: - wizard = DocumentCreateWizard( - document_type=DocumentType.objects.all()[0], - form_list=[MetadataFormSet], multiple=multiple, - step_titles=[ - _(u'document metadata'), - ]) - else: - wizard = DocumentCreateWizard(form_list=[DocumentTypeSelectForm, MetadataFormSet], multiple=multiple) + wizard = DocumentCreateWizard(form_list=[DocumentTypeSelectForm, MetadataSelectionForm, MetadataFormSet], multiple=multiple) return wizard(request) @@ -102,19 +93,21 @@ def document_create_sibling(request, document_id, multiple=True): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_CREATE]) document = get_object_or_404(Document, pk=document_id) - urldata = [] + query_dict = {} for pk, metadata in enumerate(document.documentmetadata_set.all()): - if hasattr(metadata, 'value'): - urldata.append(('metadata%s_id' % pk, metadata.metadata_type_id)) - urldata.append(('metadata%s_value' % pk, metadata.value)) + query_dict['metadata%s_id' % pk] = metadata.metadata_type_id + query_dict['metadata%s_value' % pk] = metadata.value if multiple: - view = 'upload_multiple_documents_with_type' + view = 'upload_document_multiple' else: - view = 'upload_document_with_type' + view = 'upload_document' - url = reverse(view, args=[document.document_type_id]) - return HttpResponseRedirect('%s?%s' % (url, urlencode(urldata))) + if document.document_type_id: + query_dict['document_type_id'] = document.document_type_id + + url = reverse(view) + return HttpResponseRedirect('%s?%s' % (url, urlencode(query_dict))) def _handle_save_document(request, document, form=None): @@ -157,10 +150,15 @@ def _handle_zip_file(request, uploaded_file, document_type): return False -def upload_document_with_type(request, document_type_id, multiple=True): +def upload_document_with_type(request, multiple=True): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_CREATE]) - - document_type = get_object_or_404(DocumentType, pk=document_type_id) + + document_type_id = request.GET.get('document_type_id', None) + if document_type_id: + document_type = get_object_or_404(DocumentType, pk=document_type_id) + else: + document_type = None + local_form = DocumentForm(prefix='local', initial={'document_type': document_type}) if USE_STAGING_DIRECTORY: staging_form = StagingDocumentForm(prefix='staging', @@ -475,8 +473,9 @@ def document_edit_metadata(request, document_id=None, document_id_list=None): for document in documents: RecentDocument.objects.add_document_for_user(request.user, document) - for item in DocumentTypeMetadataType.objects.filter(document_type=document.document_type): - value = document.documentmetadata_set.get(metadata_type=item.metadata_type).value if document.documentmetadata_set.filter(metadata_type=item.metadata_type) else u'' + for item in document.documentmetadata_set.all(): + value = item.value + print item if item.metadata_type in metadata: if value not in metadata[item.metadata_type]: metadata[item.metadata_type].append(value) @@ -487,7 +486,6 @@ def document_edit_metadata(request, document_id=None, document_id_list=None): for key, value in metadata.items(): initial.append({ 'metadata_type': key, - 'document_type': document.document_type, 'value': u', '.join(value) }) @@ -496,13 +494,14 @@ def document_edit_metadata(request, document_id=None, document_id_list=None): formset = MetadataFormSet(request.POST) if formset.is_valid(): for document in documents: - save_metadata_list(formset.cleaned_data, document) try: document_delete_fs_links(document) except Exception, e: messages.error(request, _(u'Error deleting filesystem links for document: %(document)s; %(error)s') % { 'document': document, 'error': e}) + save_metadata_list(formset.cleaned_data, document) + messages.success(request, _(u'Metadata for document %s edited successfully.') % document) try: @@ -1089,7 +1088,7 @@ def transform_page(request, document_page_id, zoom_function=None, rotation_funct return HttpResponseRedirect( u'?'.join([ reverse(view, args=[document_page.pk]), - urllib.urlencode({'zoom': zoom, 'rotation': rotation}) + urlencode({'zoom': zoom, 'rotation': rotation}) ]) ) diff --git a/apps/filesystem_serving/api.py b/apps/filesystem_serving/api.py index 06c0a9227c..eda6b843bd 100644 --- a/apps/filesystem_serving/api.py +++ b/apps/filesystem_serving/api.py @@ -26,7 +26,7 @@ def document_create_fs_links(document): if not document.exists(): raise Exception(_(u'Not creating metadata indexing, document not found in document storage')) metadata_dict = {'document': document} - metadata_dict.update(dict([(metadata.metadata_type.name, SLUGIFY_FUNCTION(metadata.value)) for metadata in document.documentmetadata_set.all()])) + metadata_dict.update(dict([(metadata.metadata_type.name, SLUGIFY_FUNCTION(metadata.value)) for metadata in document.documentmetadata_set.all() if metadata.value])) for metadata_index in document.document_type.metadataindex_set.all(): if metadata_index.enabled: