From e03a90883ab7400c71e2bbc7d6234a52e3dfc0b8 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 9 May 2011 02:25:09 -0400 Subject: [PATCH 01/50] Commited new metadata/document_type paradirm --- apps/common/views.py | 4 +- apps/documents/admin.py | 17 +++++--- apps/documents/forms.py | 73 +++++++++++++++++++++++----------- apps/documents/metadata.py | 30 +++++++------- apps/documents/models.py | 29 +++++++++----- apps/documents/urls.py | 4 +- apps/documents/views.py | 55 +++++++++++++------------ apps/filesystem_serving/api.py | 2 +- 8 files changed, 128 insertions(+), 86 deletions(-) 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: From 65fb5dcc9ec397afa86b6470dad11235c3cbed03 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 9 May 2011 02:40:01 -0400 Subject: [PATCH 02/50] Updated wizard to skip step 3 if there is no metadata selected, updated metadata and metadata set widget to improve visuals --- apps/documents/forms.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 6f57f38080..2a868c543c 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -346,7 +346,11 @@ class DocumentCreateWizard(BoundFormWizard): 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()} + initial_data = self.generate_metadata_initial_values() + self.initial = {2: initial_data} + if not initial_data: + # If there is no metadata selected end wizard + self.form_list=[DocumentTypeSelectForm, MetadataSelectionForm] if isinstance(form, MetadataFormSet): for identifier, metadata in enumerate(form.cleaned_data): @@ -457,5 +461,16 @@ class PrintForm(forms.Form): 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) + metadata_sets = forms.ModelMultipleChoiceField( + queryset=MetadataSet.objects.all(), + label=_(u'Metadata sets'), + required=False, + widget=forms.widgets.SelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) + ) + + metadata_types = forms.ModelMultipleChoiceField( + queryset=MetadataType.objects.all(), + label=_(u'Metadata'), + required=False, + widget=forms.widgets.SelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) + ) From 69ec591fde5bcf73ad663a80774734cf13849f15 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 9 May 2011 03:01:29 -0400 Subject: [PATCH 03/50] Removed print statement, form line cleanup --- apps/documents/forms.py | 2 +- apps/documents/views.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 2a868c543c..a321f39cc6 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -294,8 +294,8 @@ class MetadataForm(forms.Form): name = forms.CharField(label=_(u'Name'), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) value = forms.CharField(label=_(u'Value'), required=False) -MetadataFormSet = formset_factory(MetadataForm, extra=0) +MetadataFormSet = formset_factory(MetadataForm, extra=0) class DocumentCreateWizard(BoundFormWizard): def generate_metadata_initial_values(self): diff --git a/apps/documents/views.py b/apps/documents/views.py index cf07705faf..da29c7ee65 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -475,7 +475,6 @@ def document_edit_metadata(request, document_id=None, document_id_list=None): 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) From 94134bc83eada4e21e9182f3407228de34b367b5 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 9 May 2011 20:04:26 -0400 Subject: [PATCH 04/50] Added page per document statistics --- apps/documents/statistics.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/documents/statistics.py b/apps/documents/statistics.py index 1f465b8abb..1c0dec27bc 100644 --- a/apps/documents/statistics.py +++ b/apps/documents/statistics.py @@ -4,6 +4,7 @@ from common.utils import pretty_size, pretty_size_10 from documents.conf.settings import STORAGE_BACKEND from documents.models import Document, DocumentType, DocumentPage +from django.db.models import Avg, Count, Min, Max def get_used_size(path, file_list): @@ -50,10 +51,16 @@ def get_statistics(): except NotImplementedError: pass - paragraphs.append( - _(u'Document pages in database: %d') % DocumentPage.objects.only('pk',).count(), + paragraphs.extend( + [ + _(u'Document pages in database: %d') % DocumentPage.objects.only('pk',).count(), + _(u'Minimum amount of pages per document: %(page_count__min)d') % Document.objects.annotate(page_count=Count('documentpage')).aggregate(Min('page_count')), + _(u'Maximum amount of pages per document: %(page_count__max)d') % Document.objects.annotate(page_count=Count('documentpage')).aggregate(Max('page_count')), + _(u'Average amount of pages per document: %(page_count__avg)f') % Document.objects.annotate(page_count=Count('documentpage')).aggregate(Avg('page_count')), + ] ) - + #[(day_count['date_added'].strftime('%Y-%m-%d'), day_count['id__count']) for day_count in Document.objects.values('date_added').annotate(Count("id"))] + return { 'title': _(u'Document statistics'), 'paragraphs': paragraphs From b6d2654cc7ce46cdacee4463c533e8c1ddd6f264 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 9 May 2011 23:57:23 -0400 Subject: [PATCH 05/50] Added changes to expand the scope of the document grouping functionality --- apps/documents/admin.py | 13 ++++++------- apps/documents/classes.py | 12 ++++++++++++ apps/documents/metadata.py | 33 ++++++++++++++++----------------- apps/documents/models.py | 27 +++++++++++++++++---------- apps/filesystem_serving/api.py | 13 ++++++++----- 5 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 apps/documents/classes.py diff --git a/apps/documents/admin.py b/apps/documents/admin.py index e63bfec2af..c1cb7d22e1 100644 --- a/apps/documents/admin.py +++ b/apps/documents/admin.py @@ -2,8 +2,8 @@ from django.contrib import admin from documents.models import MetadataType, DocumentType, Document, \ MetadataSet, MetadataSetItem, DocumentMetadata, \ - DocumentTypeFilename, MetadataIndex, DocumentPage, MetadataGroup, \ - MetadataGroupItem, DocumentPageTransformation, RecentDocument + DocumentTypeFilename, MetadataIndex, DocumentPage, DocumentGroup, \ + DocumentGroupItem, DocumentPageTransformation, RecentDocument from filesystem_serving.admin import DocumentMetadataIndexInline @@ -65,15 +65,15 @@ class DocumentAdmin(admin.ModelAdmin): list_display = ('uuid', 'file_filename', 'file_extension') -class MetadataGroupItemInline(admin.StackedInline): - model = MetadataGroupItem +class DocumentGroupItemInline(admin.StackedInline): + model = DocumentGroupItem extra = 1 classes = ('collapse-open',) allow_add = True -class MetadataGroupAdmin(admin.ModelAdmin): - inlines = [MetadataGroupItemInline] +class DocumentGroupAdmin(admin.ModelAdmin): + inlines = [DocumentGroupItemInline] filter_horizontal = ['document_type'] @@ -87,7 +87,6 @@ class RecentDocumentAdmin(admin.ModelAdmin): class MetadataSetAdmin(admin.ModelAdmin): inlines = [MetadataSetItemInline] - #filter_horizontal = ['document_type'] admin.site.register(MetadataType, MetadataTypeAdmin) diff --git a/apps/documents/classes.py b/apps/documents/classes.py new file mode 100644 index 0000000000..b8476a744f --- /dev/null +++ b/apps/documents/classes.py @@ -0,0 +1,12 @@ +from django.utils.translation import ugettext_lazy as _ + + +class MetadataObject(object): + def __init__(self, dictionary): + self.dictionary = dictionary + + def __getattr__(self, name): + if name in self.dictionary: + return self.dictionary.get(name) + else: + raise AttributeError(_(u'\'metadata\' object has no attribute \'%s\'') % name) diff --git a/apps/documents/metadata.py b/apps/documents/metadata.py index 4c891e2ede..9b5aaf5b1a 100644 --- a/apps/documents/metadata.py +++ b/apps/documents/metadata.py @@ -1,17 +1,16 @@ -'''Metadata handling commonalities -''' +"""Metadata handling commonalities""" from urllib import unquote_plus from django.shortcuts import get_object_or_404 from django.core.exceptions import ObjectDoesNotExist +from django.utils.translation import ugettext_lazy as _ from documents.models import DocumentMetadata, MetadataType def decode_metadata_from_url(url_dict): - '''Parses a URL query string to a list of metadata - ''' + """Parse a URL query string to a list of metadata""" metadata_dict = { 'id': {}, 'value': {} @@ -35,8 +34,9 @@ def decode_metadata_from_url(url_dict): def save_metadata_list(metadata_list, document): - '''Takes a list of metadata values and associates a document to it - ''' + """ + Take a list of metadata values and associate a document to it + """ for item in metadata_list: save_metadata(item, document) @@ -57,9 +57,8 @@ def save_metadata_list(metadata_list, document): def save_metadata(metadata_dict, document): - '''save metadata_dict - ''' - #Use matched metadata now to create document metadata + """save metadata_dict""" + # Use matched metadata now to create document metadata document_metadata, created = DocumentMetadata.objects.get_or_create( document=document, metadata_type=get_object_or_404( @@ -67,24 +66,24 @@ def save_metadata(metadata_dict, document): pk=metadata_dict['id'] ), ) - #Handle 'plus sign as space' in the url + # Handle 'plus sign as space' in the url - #unquote_plus handles utf-8?!? - #http://stackoverflow.com/questions/4382875/handling-iri-in-django + # unquote_plus handles utf-8?!? + # http://stackoverflow.com/questions/4382875/handling-iri-in-django #.decode('utf-8') document_metadata.value = unquote_plus(metadata_dict['value']) document_metadata.save() def metadata_repr(metadata_list): - '''Return a printable representation of a metadata list - ''' - return ', '.join(metadata_repr_as_list(metadata_list)) + """Return a printable representation of a metadata list""" + return u', '.join(metadata_repr_as_list(metadata_list)) def metadata_repr_as_list(metadata_list): - '''Turn a list of metadata into a list of printable representations - ''' + """ + Turn a list of metadata into a list of printable representations + """ output = [] for metadata_dict in metadata_list: try: diff --git a/apps/documents/models.py b/apps/documents/models.py index 355d4f70e9..8f023ff5f7 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -25,6 +25,7 @@ from documents.conf.settings import STORAGE_BACKEND from documents.conf.settings import AVAILABLE_TRANSFORMATIONS from documents.conf.settings import DEFAULT_TRANSFORMATIONS from documents.conf.settings import RECENT_COUNT +from documents.classes import MetadataObject def get_filename_from_uuid(instance, filename): @@ -352,8 +353,11 @@ class MetadataGroupManager(models.Manager): metadata_groups = {} metadata_dict = {} for document_metadata in document.documentmetadata_set.all(): - metadata_dict['metadata_%s' % document_metadata.metadata_type.name] = document_metadata.value - + metadata_dict[document_metadata.metadata_type.name] = document_metadata.value + eval_dict = {} + eval_dict['document'] = document + eval_dict['metadata'] = MetadataObject(metadata_dict) + if group_obj: groups_qs = MetadataGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True) & Q(pk=group_obj.pk)) else: @@ -363,7 +367,7 @@ class MetadataGroupManager(models.Manager): total_query = Q() for item in group.metadatagroupitem_set.filter(enabled=True): try: - value_query = Q(**{'value__%s' % item.operator: eval(item.expression, metadata_dict)}) + value_query = Q(**{'value__%s' % item.operator: eval(item.expression, eval_dict)}) if item.negated: query = (Q(metadata_type__id=item.metadata_type_id) & ~value_query) else: @@ -390,10 +394,10 @@ class MetadataGroupManager(models.Manager): return metadata_groups, errors -class MetadataGroup(models.Model): +class DocumentGroup(models.Model): document_type = models.ManyToManyField(DocumentType, null=True, blank=True, verbose_name=_(u'document type'), help_text=_(u'If left blank, all document types will be matched.')) - name = models.CharField(max_length=32, verbose_name=_(u'name')) + #name = models.CharField(max_length=32, verbose_name=_(u'name')) label = models.CharField(max_length=32, verbose_name=_(u'label')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) @@ -433,14 +437,17 @@ OPERATOR_CHOICES = ( (u'iregex', _(u'is in regular expression (case insensitive)')), ) +#LOCAL_SOURCE_CHOICES = ( +# (u' -class MetadataGroupItem(models.Model): - metadata_group = models.ForeignKey(MetadataGroup, verbose_name=_(u'metadata group')) +class DocumentGroupItem(models.Model): + metadata_group = models.ForeignKey(DocumentGroup, verbose_name=_(u'metadata group')) inclusion = models.CharField(default=INCLUSION_AND, max_length=16, choices=INCLUSION_CHOICES, help_text=_(u'The inclusion is ignored for the first item.')) - metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type'), help_text=_(u'This represents the metadata of all other documents.')) + foreign_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_foreign', verbose_name=_(u'foreign metadata'), help_text=_(u'This represents the metadata of all other documents.')) operator = models.CharField(max_length=16, choices=OPERATOR_CHOICES) - expression = models.CharField(max_length=128, - verbose_name=_(u'expression'), help_text=_(u'This expression will be evaluated against the current selected document. The document metadata is available as variables of the same name but with the "metadata_" prefix added their name.')) + + local_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_local', verbose_name=_(u'local metadata'), help_text=_(u'This represents the metadata of the current document.')) + expression = models.TextField(verbose_name=_(u'expression'), help_text=_(u'This expression will be evaluated against the current selected document. The document metadata is available as variables `metadata` and document properties under the variable `document`.')) negated = models.BooleanField(default=False, verbose_name=_(u'negated'), help_text=_(u'Inverts the logic of the operator.')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) diff --git a/apps/filesystem_serving/api.py b/apps/filesystem_serving/api.py index eda6b843bd..c003f3101c 100644 --- a/apps/filesystem_serving/api.py +++ b/apps/filesystem_serving/api.py @@ -5,6 +5,7 @@ from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ from documents.conf.settings import AVAILABLE_INDEXING_FUNCTIONS +from documents.classes import MetadataObject from filesystem_serving.conf.settings import FILESERVING_ENABLE from filesystem_serving.conf.settings import FILESERVING_PATH @@ -25,13 +26,15 @@ def document_create_fs_links(document): if FILESERVING_ENABLE: 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() if metadata.value])) - + eval_dict = {} + eval_dict['document'] = document + metadata_dict = dict([(metadata.metadata_type.name, SLUGIFY_FUNCTION(metadata.value)) for metadata in document.documentmetadata_set.all() if metadata.value]) + eval_dict['metadata'] = MetadataObject(metadata_dict) + for metadata_index in document.document_type.metadataindex_set.all(): if metadata_index.enabled: try: - fabricated_directory = eval(metadata_index.expression, metadata_dict, AVAILABLE_INDEXING_FUNCTIONS) + fabricated_directory = eval(metadata_index.expression, eval_dict, AVAILABLE_INDEXING_FUNCTIONS) target_directory = os.path.join(FILESERVING_PATH, fabricated_directory) try: os.makedirs(target_directory) @@ -48,7 +51,7 @@ def document_create_fs_links(document): #This should be a warning not an error #pass except Exception, exc: - raise Exception(_(u'Unable to create metadata indexing directory: %s') % exc) + warnings.append(_(u'Unable to create metadata indexing directory: %s') % exc) return warnings From b4daa2d52b402d27604785913ff858aa40fecf9c Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:40:50 -0400 Subject: [PATCH 06/50] Fixed permission grant revoke button text no appearing --- apps/{documents/metadata.py => metadata/api.py} | 0 apps/{documents => metadata}/classes.py | 0 apps/permissions/views.py | 5 +++-- 3 files changed, 3 insertions(+), 2 deletions(-) rename apps/{documents/metadata.py => metadata/api.py} (100%) rename apps/{documents => metadata}/classes.py (100%) diff --git a/apps/documents/metadata.py b/apps/metadata/api.py similarity index 100% rename from apps/documents/metadata.py rename to apps/metadata/api.py diff --git a/apps/documents/classes.py b/apps/metadata/classes.py similarity index 100% rename from apps/documents/classes.py rename to apps/metadata/classes.py diff --git a/apps/permissions/views.py b/apps/permissions/views.py index d479f03b9f..8bcfc275fe 100644 --- a/apps/permissions/views.py +++ b/apps/permissions/views.py @@ -1,4 +1,5 @@ from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext @@ -44,12 +45,12 @@ def _role_permission_link(requester, permission, permission_list): return template % { 'url': reverse('permission_revoke', args=[permission.id, ct.app_label, ct.model, requester.id]), - 'icon': 'delete', 'text': _(u'Revoke')} + 'icon': 'delete', 'text': ugettext(u'Revoke')} else: return template % { 'url': reverse('permission_grant', args=[permission.id, ct.app_label, ct.model, requester.id]), - 'icon': 'add', 'text': _(u'Grant')} + 'icon': 'add', 'text': ugettext(u'Grant')} def role_permissions(request, role_id): From ea20cde59728320d418abcc37d1a8a3f0d5f51f9 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:41:35 -0400 Subject: [PATCH 07/50] Created new metadata app and move all code relating metadata to it --- apps/documents/__init__.py | 8 +- apps/documents/admin.py | 31 +--- apps/documents/conf/settings.py | 12 -- apps/documents/forms.py | 53 +------ apps/documents/managers.py | 52 +++++++ apps/documents/models.py | 134 +---------------- apps/documents/urls.py | 2 - apps/documents/views.py | 93 +----------- apps/metadata/__init__.py | 28 ++++ apps/metadata/admin.py | 30 ++++ apps/metadata/api.py | 14 +- apps/metadata/conf/__init__.py | 1 + apps/metadata/conf/settings.py | 27 ++++ apps/metadata/forms.py | 67 +++++++++ apps/metadata/models.py | 74 ++++++++++ apps/metadata/tests.py | 23 +++ apps/metadata/urls.py | 10 ++ apps/metadata/views.py | 251 ++++++++++++++++++++++++++++++++ settings.py | 10 +- urls.py | 1 + 20 files changed, 601 insertions(+), 320 deletions(-) create mode 100644 apps/documents/managers.py create mode 100644 apps/metadata/__init__.py create mode 100644 apps/metadata/admin.py create mode 100644 apps/metadata/conf/__init__.py create mode 100644 apps/metadata/conf/settings.py create mode 100644 apps/metadata/forms.py create mode 100644 apps/metadata/models.py create mode 100644 apps/metadata/tests.py create mode 100644 apps/metadata/urls.py create mode 100644 apps/metadata/views.py diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index d6d1da3810..3515837b0b 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -15,7 +15,6 @@ from documents.conf.settings import ENABLE_SINGLE_DOCUMENT_UPLOAD PERMISSION_DOCUMENT_CREATE = 'document_create' PERMISSION_DOCUMENT_PROPERTIES_EDIT = 'document_properties_edit' PERMISSION_DOCUMENT_EDIT = 'document_edit' -PERMISSION_DOCUMENT_METADATA_EDIT = 'document_metadata_edit' PERMISSION_DOCUMENT_VIEW = 'document_view' PERMISSION_DOCUMENT_DELETE = 'document_delete' PERMISSION_DOCUMENT_DOWNLOAD = 'document_download' @@ -26,7 +25,6 @@ register_permissions('documents', [ {'name': PERMISSION_DOCUMENT_CREATE, 'label': _(u'Create document')}, {'name': PERMISSION_DOCUMENT_PROPERTIES_EDIT, 'label': _(u'Edit document properties')}, {'name': PERMISSION_DOCUMENT_EDIT, 'label': _(u'Edit document')}, - {'name': PERMISSION_DOCUMENT_METADATA_EDIT, 'label': _(u'Edit document metadata')}, {'name': PERMISSION_DOCUMENT_VIEW, 'label': _(u'View document')}, {'name': PERMISSION_DOCUMENT_DELETE, 'label': _(u'Delete document')}, {'name': PERMISSION_DOCUMENT_DOWNLOAD, 'label': _(u'Download document')}, @@ -44,8 +42,6 @@ document_view_simple = {'text': _(u'details (simple)'), 'view': 'document_view_s document_delete = {'text': _(u'delete'), 'view': 'document_delete', 'args': 'object.id', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_delete', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]}} -document_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_edit_metadata', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}} -document_multiple_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_multiple_edit_metadata', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}} document_preview = {'text': _(u'preview'), 'class': 'fancybox', 'view': 'document_preview', 'args': 'object.id', 'famfam': 'magnifier', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_download = {'text': _(u'download'), 'view': 'document_download', 'args': 'object.id', 'famfam': 'page_save', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}} document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_refresh', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} @@ -83,10 +79,10 @@ metadata_group_create_sibling = {'text': _(u'upload new document using same meta staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': 'object.id', 'famfam': 'drive_magnify'} staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': 'object.id', 'famfam': 'drive_delete'} -register_links(Document, [document_view_simple, document_view, document_edit, document_edit_metadata, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) +register_links(Document, [document_view_simple, document_view, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) register_links(Document, [document_create_sibling], menu_name='sidebar') -register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_edit_metadata, document_multiple_delete]) +register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete]) if ENABLE_SINGLE_DOCUMENT_UPLOAD: register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list_recent, document_list, document_create, document_create_multiple], menu_name='sidebar') diff --git a/apps/documents/admin.py b/apps/documents/admin.py index c1cb7d22e1..215923a88e 100644 --- a/apps/documents/admin.py +++ b/apps/documents/admin.py @@ -1,17 +1,14 @@ from django.contrib import admin -from documents.models import MetadataType, DocumentType, Document, \ - MetadataSet, MetadataSetItem, DocumentMetadata, \ +from metadata.admin import DocumentMetadataInline + +from documents.models import DocumentType, Document, \ DocumentTypeFilename, MetadataIndex, DocumentPage, DocumentGroup, \ DocumentGroupItem, DocumentPageTransformation, RecentDocument from filesystem_serving.admin import DocumentMetadataIndexInline -class MetadataTypeAdmin(admin.ModelAdmin): - list_display = ('name', 'title', 'default', 'lookup') - - class MetadataIndexInline(admin.StackedInline): model = MetadataIndex extra = 1 @@ -19,13 +16,6 @@ class MetadataIndexInline(admin.StackedInline): allow_add = True -class MetadataSetItemInline(admin.StackedInline): - model = MetadataSetItem - extra = 1 - classes = ('collapse-open',) - allow_add = True - - class DocumentTypeFilenameInline(admin.StackedInline): model = DocumentTypeFilename extra = 1 @@ -39,13 +29,6 @@ class DocumentTypeAdmin(admin.ModelAdmin): ] -class DocumentMetadataInline(admin.StackedInline): - model = DocumentMetadata - extra = 0 - classes = ('collapse-open',) - allow_add = False - - class DocumentPageTransformationAdmin(admin.ModelAdmin): model = DocumentPageTransformation @@ -85,15 +68,9 @@ class RecentDocumentAdmin(admin.ModelAdmin): date_hierarchy = 'datetime_accessed' -class MetadataSetAdmin(admin.ModelAdmin): - inlines = [MetadataSetItemInline] - - -admin.site.register(MetadataType, MetadataTypeAdmin) admin.site.register(DocumentType, DocumentTypeAdmin) admin.site.register(Document, DocumentAdmin) -admin.site.register(MetadataGroup, MetadataGroupAdmin) +admin.site.register(DocumentGroup, DocumentGroupAdmin) admin.site.register(DocumentPageTransformation, DocumentPageTransformationAdmin) admin.site.register(RecentDocument, RecentDocumentAdmin) -admin.site.register(MetadataSet, MetadataSetAdmin) diff --git a/apps/documents/conf/settings.py b/apps/documents/conf/settings.py index d504fef996..cae9ccc2c2 100644 --- a/apps/documents/conf/settings.py +++ b/apps/documents/conf/settings.py @@ -1,11 +1,9 @@ """Configuration options for the documents app""" -import datetime import hashlib import uuid from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User from common.utils import proper_name from storage.backends.filebasedstorage import FileBasedStorage @@ -21,14 +19,6 @@ def default_uuid(): """unicode(uuid.uuid4())""" return unicode(uuid.uuid4()) -default_available_functions = { - 'current_date': datetime.datetime.now().date, -} - -default_available_models = { - 'User': User -} - available_transformations = { 'rotate': {'label': _(u'Rotate [degrees]'), 'arguments': [{'name': 'degrees'}]} } @@ -42,8 +32,6 @@ register_settings( module=u'documents.conf.settings', settings=[ # Definition - {'name': u'AVAILABLE_FUNCTIONS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_FUNCTIONS', 'default': default_available_functions}, - {'name': u'AVAILABLE_MODELS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_MODELS', 'default': default_available_models}, {'name': u'AVAILABLE_INDEXING_FUNCTIONS', 'global_name': u'DOCUMENTS_INDEXING_AVAILABLE_FUNCTIONS', 'default': available_indexing_functions}, # Upload {'name': u'USE_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_USE_STAGING_DIRECTORY', 'default': False}, diff --git a/apps/documents/forms.py b/apps/documents/forms.py index a321f39cc6..94e3474a2c 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -16,12 +16,11 @@ 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 metadata.models import MetadataSet, MetadataType from documents.staging import StagingFile from documents.models import Document, DocumentType, \ - DocumentPage, DocumentPageTransformation, MetadataSet, MetadataType -from documents.conf.settings import AVAILABLE_FUNCTIONS -from documents.conf.settings import AVAILABLE_MODELS + DocumentPage, DocumentPageTransformation class DocumentPageTransformationForm(forms.ModelForm): @@ -249,54 +248,6 @@ class DocumentTypeSelectForm(forms.Form): document_type = forms.ModelChoiceField(queryset=DocumentType.objects.all(), label=(u'Document type'), required=False) -class MetadataForm(forms.Form): - def __init__(self, *args, **kwargs): - super(MetadataForm, self).__init__(*args, **kwargs) - - #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) - - # 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 - required_string = ' (%s)' % ugettext(u'required') - else: - #TODO: FIXME: not working correctly - self.fields['value'].required = False - - self.fields['name'].initial = '%s%s' % ((self.metadata_type.title if self.metadata_type.title else self.metadata_type.name), required_string) - self.fields['id'].initial = self.metadata_type.id - if self.metadata_type.default: - try: - self.fields['value'].initial = eval(self.metadata_type.default, AVAILABLE_FUNCTIONS) - except Exception, err: - self.fields['value'].initial = err - - if self.metadata_type.lookup: - try: - choices = eval(self.metadata_type.lookup, AVAILABLE_MODELS) - self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label) - choices = zip(choices, choices) - if not required: - choices.insert(0, ('', '------')) - self.fields['value'].choices = choices - self.fields['value'].required = required - except Exception, err: - self.fields['value'].initial = err - self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'}) - - id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput) - name = forms.CharField(label=_(u'Name'), - required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) - value = forms.CharField(label=_(u'Value'), required=False) - -MetadataFormSet = formset_factory(MetadataForm, extra=0) - class DocumentCreateWizard(BoundFormWizard): def generate_metadata_initial_values(self): initial = [] diff --git a/apps/documents/managers.py b/apps/documents/managers.py new file mode 100644 index 0000000000..4888f7cd08 --- /dev/null +++ b/apps/documents/managers.py @@ -0,0 +1,52 @@ +from django.db import models + +#from documents.models import DocumentGroup +from metadata.classes import MetadataObject + + +class DocumentGroupManager(models.Manager): + def get_groups_for(self, document, group_obj=None): + errors = [] + metadata_groups = {} + metadata_dict = {} + for document_metadata in document.documentmetadata_set.all(): + metadata_dict[document_metadata.metadata_type.name] = document_metadata.value + eval_dict = {} + eval_dict['document'] = document + eval_dict['metadata'] = MetadataObject(metadata_dict) + + #if group_obj: + # groups_qs = DocumentGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True) & Q(pk=group_obj.pk)) + #else: + # groups_qs = DocumentGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True)) + groups_qs=[] + + for group in groups_qs: + total_query = Q() + for item in group.metadatagroupitem_set.filter(enabled=True): + try: + value_query = Q(**{'value__%s' % item.operator: eval(item.expression, eval_dict)}) + if item.negated: + query = (Q(metadata_type__id=item.metadata_type_id) & ~value_query) + else: + query = (Q(metadata_type__id=item.metadata_type_id) & value_query) + + if item.inclusion == INCLUSION_AND: + total_query &= query + elif item.inclusion == INCLUSION_OR: + total_query |= query + except Exception, e: + errors.append(e) + value_query = Q() + query = Q() + + if total_query: + document_id_list = DocumentMetadata.objects.filter(total_query).values_list('document', flat=True) + metadata_groups[group] = Document.objects.filter(Q(id__in=document_id_list)).order_by('file_filename') or [] + else: + metadata_groups[group] = [] + + if group_obj: + return metadata_groups[group_obj], errors + + return metadata_groups, errors diff --git a/apps/documents/models.py b/apps/documents/models.py index 8f023ff5f7..dbb39d752d 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -15,18 +15,16 @@ from taggit.managers import TaggableManager from dynamic_search.api import register from converter.api import get_page_count from converter import TRANFORMATION_CHOICES +from metadata.classes import MetadataObject from documents.conf.settings import AVAILABLE_INDEXING_FUNCTIONS -from documents.conf.settings import AVAILABLE_FUNCTIONS -from documents.conf.settings import AVAILABLE_MODELS from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION from documents.conf.settings import STORAGE_BACKEND from documents.conf.settings import AVAILABLE_TRANSFORMATIONS from documents.conf.settings import DEFAULT_TRANSFORMATIONS from documents.conf.settings import RECENT_COUNT -from documents.classes import MetadataObject - +from documents.managers import DocumentGroupManager def get_filename_from_uuid(instance, filename): filename, extension = os.path.splitext(filename) @@ -190,7 +188,7 @@ class Document(models.Model): return u', '.join([u'%s - %s' % (metadata.metadata_type, metadata.value) for metadata in self.documentmetadata_set.select_related('metadata_type', 'document').defer('document__document_type', 'document__file', 'document__description', 'document__file_filename', 'document__uuid', 'document__date_added', 'document__date_updated', 'document__file_mimetype', 'document__file_mime_encoding')]) def get_metadata_groups(self, group_obj=None): - return MetadataGroup.objects.get_groups_for(self, group_obj) + return DocumentGroup.objects.get_groups_for(self, group_obj) def apply_default_transformations(self): #Only apply default transformations on new documents @@ -207,60 +205,8 @@ class Document(models.Model): page_transformation.save() -available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else u'' -available_models_string = (_(u' Available models: %s') % u','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else u'' - - -class MetadataType(models.Model): - 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'), - help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string) - lookup = models.CharField(max_length=128, blank=True, null=True, - verbose_name=_(u'lookup'), - help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string) - #TODO: datatype? - - def __unicode__(self): - return self.title if self.title else self.name - - class Meta: - verbose_name = _(u'metadata type') - verbose_name_plural = _(u'metadata types') - - -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 set or group of - metadata fields - """ - 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')) - - def __unicode__(self): - return unicode(self.metadata_type) - - class Meta: - 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'' - class MetadataIndex(models.Model): document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) expression = models.CharField(max_length=128, @@ -276,23 +222,6 @@ class MetadataIndex(models.Model): verbose_name_plural = _(u'metadata indexes') -class DocumentMetadata(models.Model): - """ - Link a document to a specific instance of a metadata type with it's - current value - """ - document = models.ForeignKey(Document, verbose_name=_(u'document')) - metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type')) - value = models.TextField(blank=True, null=True, verbose_name=_(u'metadata value'), db_index=True) - - def __unicode__(self): - return unicode(self.metadata_type) - - class Meta: - verbose_name = _(u'document metadata') - verbose_name_plural = _(u'document metadata') - - class DocumentTypeFilename(models.Model): """ List of filenames available to a specific document type for the @@ -347,61 +276,13 @@ class DocumentPage(models.Model): return ' '.join(transformation_list), warnings -class MetadataGroupManager(models.Manager): - def get_groups_for(self, document, group_obj=None): - errors = [] - metadata_groups = {} - metadata_dict = {} - for document_metadata in document.documentmetadata_set.all(): - metadata_dict[document_metadata.metadata_type.name] = document_metadata.value - eval_dict = {} - eval_dict['document'] = document - eval_dict['metadata'] = MetadataObject(metadata_dict) - - if group_obj: - groups_qs = MetadataGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True) & Q(pk=group_obj.pk)) - else: - groups_qs = MetadataGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True)) - - for group in groups_qs: - total_query = Q() - for item in group.metadatagroupitem_set.filter(enabled=True): - try: - value_query = Q(**{'value__%s' % item.operator: eval(item.expression, eval_dict)}) - if item.negated: - query = (Q(metadata_type__id=item.metadata_type_id) & ~value_query) - else: - query = (Q(metadata_type__id=item.metadata_type_id) & value_query) - - if item.inclusion == INCLUSION_AND: - total_query &= query - elif item.inclusion == INCLUSION_OR: - total_query |= query - except Exception, e: - errors.append(e) - value_query = Q() - query = Q() - - if total_query: - document_id_list = DocumentMetadata.objects.filter(total_query).values_list('document', flat=True) - metadata_groups[group] = Document.objects.filter(Q(id__in=document_id_list)).order_by('file_filename') or [] - else: - metadata_groups[group] = [] - - if group_obj: - return metadata_groups[group_obj], errors - - return metadata_groups, errors - - class DocumentGroup(models.Model): document_type = models.ManyToManyField(DocumentType, null=True, blank=True, verbose_name=_(u'document type'), help_text=_(u'If left blank, all document types will be matched.')) - #name = models.CharField(max_length=32, verbose_name=_(u'name')) label = models.CharField(max_length=32, verbose_name=_(u'label')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) - objects = MetadataGroupManager() + objects = DocumentGroupManager() def __unicode__(self): return self.label if self.label else self.name @@ -443,10 +324,10 @@ OPERATOR_CHOICES = ( class DocumentGroupItem(models.Model): metadata_group = models.ForeignKey(DocumentGroup, verbose_name=_(u'metadata group')) inclusion = models.CharField(default=INCLUSION_AND, max_length=16, choices=INCLUSION_CHOICES, help_text=_(u'The inclusion is ignored for the first item.')) - foreign_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_foreign', verbose_name=_(u'foreign metadata'), help_text=_(u'This represents the metadata of all other documents.')) + #foreign_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_foreign', verbose_name=_(u'foreign metadata'), help_text=_(u'This represents the metadata of all other documents.')) operator = models.CharField(max_length=16, choices=OPERATOR_CHOICES) - local_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_local', verbose_name=_(u'local metadata'), help_text=_(u'This represents the metadata of the current document.')) + #local_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_local', verbose_name=_(u'local metadata'), help_text=_(u'This represents the metadata of the current document.')) expression = models.TextField(verbose_name=_(u'expression'), help_text=_(u'This expression will be evaluated against the current selected document. The document metadata is available as variables `metadata` and document properties under the variable `document`.')) negated = models.BooleanField(default=False, verbose_name=_(u'negated'), help_text=_(u'Inverts the logic of the operator.')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) @@ -458,8 +339,7 @@ class DocumentGroupItem(models.Model): verbose_name = _(u'group item') verbose_name_plural = _(u'group items') - -available_transformations = ([(name, data['label']) for name, data in AVAILABLE_TRANSFORMATIONS.items()]) if AVAILABLE_MODELS else [] +available_transformations = ([(name, data['label']) for name, data in AVAILABLE_TRANSFORMATIONS.items()]) class DocumentPageTransformation(models.Model): diff --git a/apps/documents/urls.py b/apps/documents/urls.py index e29ddd1b24..22308c3982 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -21,8 +21,6 @@ urlpatterns = patterns('documents.views', url(r'^document/(?P\d+)/delete/$', 'document_delete', (), 'document_delete'), url(r'^document/multiple/delete/$', 'document_multiple_delete', (), 'document_multiple_delete'), url(r'^document/(?P\d+)/edit/$', 'document_edit', (), 'document_edit'), - url(r'^document/(?P\d+)/edit/metadata/$', 'document_edit_metadata', (), 'document_edit_metadata'), - url(r'^document/multiple/edit/metadata/$', 'document_multiple_edit_metadata', (), 'document_multiple_edit_metadata'), url(r'^document/(?P\d+)/print/$', 'document_print', (), 'document_print'), url(r'^document/(?P\d+)/hard_copy/$', 'document_hard_copy', (), 'document_hard_copy'), diff --git a/apps/documents/views.py b/apps/documents/views.py index da29c7ee65..6c1915d593 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -31,6 +31,8 @@ from converter.api import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, \ from common.literals import PAGE_SIZE_DIMENSIONS, \ PAGE_ORIENTATION_PORTRAIT, PAGE_ORIENTATION_LANDSCAPE from common.conf.settings import DEFAULT_PAPER_SIZE +from metadata.api import save_metadata_list, \ + decode_metadata_from_url, metadata_repr_as_list from documents.conf.settings import DELETE_STAGING_FILE_AFTER_UPLOAD from documents.conf.settings import USE_STAGING_DIRECTORY @@ -48,7 +50,7 @@ from documents.conf.settings import PRINT_SIZE from documents import PERMISSION_DOCUMENT_CREATE, \ PERMISSION_DOCUMENT_PROPERTIES_EDIT, \ - PERMISSION_DOCUMENT_METADATA_EDIT, PERMISSION_DOCUMENT_VIEW, \ + PERMISSION_DOCUMENT_VIEW, \ PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \ PERMISSION_DOCUMENT_TRANSFORM, \ PERMISSION_DOCUMENT_EDIT @@ -56,14 +58,12 @@ from documents import PERMISSION_DOCUMENT_CREATE, \ from documents.forms import DocumentTypeSelectForm, DocumentCreateWizard, \ DocumentForm, DocumentForm_edit, DocumentForm_view, \ StagingDocumentForm, DocumentPreviewForm, \ - MetadataFormSet, DocumentPageForm, DocumentPageTransformationForm, \ + DocumentPageForm, DocumentPageTransformationForm, \ DocumentContentForm, DocumentPageForm_edit, MetaDataGroupForm, \ DocumentPageForm_text, PrintForm, MetadataSelectionForm -from documents.metadata import save_metadata_list, \ - decode_metadata_from_url, metadata_repr_as_list from documents.models import Document, DocumentType, DocumentPage, \ - DocumentPageTransformation, RecentDocument, MetadataGroup + DocumentPageTransformation, RecentDocument, DocumentGroup from documents.staging import StagingFile from documents import metadata_group_link from documents.literals import PICTURE_ERROR_SMALL, PICTURE_ERROR_MEDIUM, \ @@ -455,89 +455,6 @@ def document_edit(request, document_id): }, context_instance=RequestContext(request)) -def document_edit_metadata(request, document_id=None, document_id_list=None): - check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_METADATA_EDIT]) - - if document_id: - documents = [get_object_or_404(Document, pk=document_id)] - elif document_id_list: - documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] - if len(set([document.document_type for document in documents])) > 1: - messages.error(request, _(u'All documents must be from the same type.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - else: - messages.error(request, _(u'Must provide at least one document.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - - metadata = {} - for document in documents: - RecentDocument.objects.add_document_for_user(request.user, document) - - for item in document.documentmetadata_set.all(): - value = item.value - if item.metadata_type in metadata: - if value not in metadata[item.metadata_type]: - metadata[item.metadata_type].append(value) - else: - metadata[item.metadata_type] = [value] - - initial = [] - for key, value in metadata.items(): - initial.append({ - 'metadata_type': key, - 'value': u', '.join(value) - }) - - formset = MetadataFormSet(initial=initial) - if request.method == 'POST': - formset = MetadataFormSet(request.POST) - if formset.is_valid(): - for document in documents: - 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: - warnings = document_create_fs_links(document) - - if request.user.is_staff or request.user.is_superuser: - for warning in warnings: - messages.warning(request, warning) - - messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) - except Exception, e: - messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { - 'document': document, 'error': e}) - - if len(documents) == 1: - return HttpResponseRedirect(document.get_absolute_url()) - elif len(documents) > 1: - return HttpResponseRedirect(reverse('document_list')) - - context = { - 'form_display_mode_table': True, - 'form': formset, - } - if len(documents) == 1: - context['object'] = documents[0] - context['title'] = _(u'Edit metadata for document: %s') % ', '.join([unicode(d) for d in documents]) - elif len(documents) > 1: - context['title'] = _(u'Edit metadata for documents: %s') % ', '.join([unicode(d) for d in documents]) - - return render_to_response('generic_form.html', context, - context_instance=RequestContext(request)) - - -def document_multiple_edit_metadata(request): - return document_edit_metadata(request, document_id_list=request.GET.get('id_list', [])) - - def calculate_converter_arguments(document, *args, **kwargs): size = kwargs.pop('size', PREVIEW_SIZE) quality = kwargs.pop('quality', QUALITY_DEFAULT) diff --git a/apps/metadata/__init__.py b/apps/metadata/__init__.py new file mode 100644 index 0000000000..f3bfe8c528 --- /dev/null +++ b/apps/metadata/__init__.py @@ -0,0 +1,28 @@ +from django.utils.translation import ugettext_lazy as _ + +from navigation.api import register_links, register_menu, \ + register_model_list_columns, register_multi_item_links +from main.api import register_diagnostic, register_tool +from permissions.api import register_permissions + +from documents.models import Document + +PERMISSION_METADATA_DOCUMENT_EDIT = u'metadata_document_edit' +PERMISSION_METADATA_DOCUMENT_ADD = u'metadata_document_add' +PERMISSION_METADATA_DOCUMENT_REMOVE = u'metadata_document_remove' + +register_permissions('metadata', [ + {'name': PERMISSION_METADATA_DOCUMENT_EDIT, 'label': _(u'Edit a document\'s metadata')}, + {'name': PERMISSION_METADATA_DOCUMENT_ADD, 'label': _(u'Add metadata to a document')}, + {'name': PERMISSION_METADATA_DOCUMENT_REMOVE, 'label': _(u'Remove metadata from a document')}, +]) + +metadata_edit = {'text': _(u'edit metadata'), 'view': 'metadata_edit', 'args': 'object.id', 'famfam': 'xhtml_go', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]}} +metadata_multiple_edit = {'text': _(u'edit metadata'), 'view': 'metadata_multiple_edit', 'famfam': 'xhtml_go', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]}} +metadata_add = {'text': _(u'add metadata'), 'view': 'metadata_add', 'args': 'object.id', 'famfam': 'xhtml_add', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]}} +metadata_multiple_add = {'text': _(u'add metadata'), 'view': 'metadata_multiple_add', 'famfam': 'xhtml_add', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]}} +metadata_remove = {'text': _(u'remove metadata'), 'view': 'metadata_remove', 'args': 'object.id', 'famfam': 'xhtml_delete', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]}} +metadata_multiple_remove = {'text': _(u'remove metadata'), 'view': 'metadata_multiple_remove', 'famfam': 'xhtml_delete', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]}} + +register_links(Document, [metadata_add, metadata_edit, metadata_remove]) +register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [metadata_multiple_add, metadata_multiple_edit, metadata_multiple_remove]) diff --git a/apps/metadata/admin.py b/apps/metadata/admin.py new file mode 100644 index 0000000000..d28ea231a4 --- /dev/null +++ b/apps/metadata/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin + +from metadata.models import MetadataType, MetadataSet, MetadataSetItem, \ + DocumentMetadata + + +class MetadataTypeAdmin(admin.ModelAdmin): + list_display = ('name', 'title', 'default', 'lookup') + + +class MetadataSetItemInline(admin.StackedInline): + model = MetadataSetItem + extra = 1 + classes = ('collapse-open',) + allow_add = True + + +class DocumentMetadataInline(admin.StackedInline): + model = DocumentMetadata + extra = 0 + classes = ('collapse-open',) + allow_add = False + + +class MetadataSetAdmin(admin.ModelAdmin): + inlines = [MetadataSetItemInline] + + +admin.site.register(MetadataType, MetadataTypeAdmin) +admin.site.register(MetadataSet, MetadataSetAdmin) diff --git a/apps/metadata/api.py b/apps/metadata/api.py index 9b5aaf5b1a..7bfe0acff0 100644 --- a/apps/metadata/api.py +++ b/apps/metadata/api.py @@ -3,10 +3,9 @@ from urllib import unquote_plus from django.shortcuts import get_object_or_404 -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext_lazy as _ -from documents.models import DocumentMetadata, MetadataType +from metadata.models import DocumentMetadata, MetadataType def decode_metadata_from_url(url_dict): @@ -59,13 +58,22 @@ def save_metadata_list(metadata_list, document): def save_metadata(metadata_dict, document): """save metadata_dict""" # Use matched metadata now to create document metadata - document_metadata, created = DocumentMetadata.objects.get_or_create( + #document_metadata, created = DocumentMetadata.objects.get_or_create( + # document=document, + # metadata_type=get_object_or_404( + # MetadataType, + # pk=metadata_dict['id'] + # ), + #) + + document_metadata = DocumentMetadata.objects.get( document=document, metadata_type=get_object_or_404( MetadataType, pk=metadata_dict['id'] ), ) + # Handle 'plus sign as space' in the url # unquote_plus handles utf-8?!? diff --git a/apps/metadata/conf/__init__.py b/apps/metadata/conf/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/apps/metadata/conf/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/metadata/conf/settings.py b/apps/metadata/conf/settings.py new file mode 100644 index 0000000000..a2955cad2a --- /dev/null +++ b/apps/metadata/conf/settings.py @@ -0,0 +1,27 @@ +"""Configuration options for the metadata app""" + +import datetime + +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User + +from smart_settings.api import register_settings + + +default_available_functions = { + 'current_date': datetime.datetime.now().date, +} + +default_available_models = { + 'User': User +} + +register_settings( + namespace=u'metadata', + module=u'metadata.conf.settings', + settings=[ + # Definition + {'name': u'AVAILABLE_FUNCTIONS', 'global_name': u'METADATA_AVAILABLE_FUNCTIONS', 'default': default_available_functions}, + {'name': u'AVAILABLE_MODELS', 'global_name': u'METADATA_AVAILABLE_MODELS', 'default': default_available_models}, + ] +) diff --git a/apps/metadata/forms.py b/apps/metadata/forms.py new file mode 100644 index 0000000000..7bee8872e7 --- /dev/null +++ b/apps/metadata/forms.py @@ -0,0 +1,67 @@ +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.forms.formsets import formset_factory + +from metadata.conf.settings import AVAILABLE_MODELS +from metadata.conf.settings import AVAILABLE_FUNCTIONS +from metadata.models import MetadataType, DocumentMetadata + + +class MetadataForm(forms.Form): + def __init__(self, *args, **kwargs): + super(MetadataForm, self).__init__(*args, **kwargs) + + #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) + + # 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 + required_string = ' (%s)' % _(u'required') + else: + #TODO: FIXME: not working correctly + self.fields['value'].required = False + + self.fields['name'].initial = '%s%s' % ((self.metadata_type.title if self.metadata_type.title else self.metadata_type.name), required_string) + self.fields['id'].initial = self.metadata_type.pk + if self.metadata_type.default: + try: + self.fields['value'].initial = eval(self.metadata_type.default, AVAILABLE_FUNCTIONS) + except Exception, err: + self.fields['value'].initial = err + + if self.metadata_type.lookup: + try: + choices = eval(self.metadata_type.lookup, AVAILABLE_MODELS) + self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label) + choices = zip(choices, choices) + if not required: + choices.insert(0, ('', '------')) + self.fields['value'].choices = choices + self.fields['value'].required = required + except Exception, err: + self.fields['value'].initial = err + self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'}) + + id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput) + name = forms.CharField(label=_(u'Name'), + required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) + value = forms.CharField(label=_(u'Value'), required=False) + update = forms.BooleanField(initial=True, label=_(u'Update'), required=False) + +MetadataFormSet = formset_factory(MetadataForm, extra=0) + + +class AddMetadataForm(forms.Form): + metadata_type = forms.ModelChoiceField(queryset=MetadataType.objects.all(), label=_(u'Metadata type')) + + +class MetadataRemoveForm(MetadataForm): + update = forms.BooleanField(initial=False, label=_(u'Remove'), required=False) + +MetadataRemoveFormSet = formset_factory(MetadataRemoveForm, extra=0) diff --git a/apps/metadata/models.py b/apps/metadata/models.py new file mode 100644 index 0000000000..c298f5c3a7 --- /dev/null +++ b/apps/metadata/models.py @@ -0,0 +1,74 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from documents.models import Document + +from metadata.conf.settings import AVAILABLE_MODELS +from metadata.conf.settings import AVAILABLE_FUNCTIONS + +available_models_string = (_(u' Available models: %s') % u','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else u'' +available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else u'' + + +class MetadataType(models.Model): + name = models.CharField(unique=True, 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'), + help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string) + lookup = models.CharField(max_length=128, blank=True, null=True, + verbose_name=_(u'lookup'), + help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string) + #TODO: datatype? + + def __unicode__(self): + return self.title if self.title else self.name + + class Meta: + verbose_name = _(u'metadata type') + verbose_name_plural = _(u'metadata types') + + +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 set or group of + metadata fields + """ + 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')) + + def __unicode__(self): + return unicode(self.metadata_type) + + class Meta: + verbose_name = _(u'metadata set item') + verbose_name_plural = _(u'metadata set items') + + +class DocumentMetadata(models.Model): + """ + Link a document to a specific instance of a metadata type with it's + current value + """ + document = models.ForeignKey(Document, verbose_name=_(u'document')) + metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'type')) + value = models.TextField(blank=True, null=True, verbose_name=_(u'value'), db_index=True) + + def __unicode__(self): + return unicode(self.metadata_type) + + class Meta: + verbose_name = _(u'document metadata') + verbose_name_plural = _(u'document metadata') diff --git a/apps/metadata/tests.py b/apps/metadata/tests.py new file mode 100644 index 0000000000..2247054b35 --- /dev/null +++ b/apps/metadata/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/apps/metadata/urls.py b/apps/metadata/urls.py new file mode 100644 index 0000000000..3e5e841a3c --- /dev/null +++ b/apps/metadata/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('metadata.views', + url(r'^(?P\d+)/edit/$', 'metadata_edit', (), 'metadata_edit'), + url(r'^multiple/edit/$', 'metadata_multiple_edit', (), 'metadata_multiple_edit'), + url(r'^(?P\d+)/add/$', 'metadata_add', (), 'metadata_add'), + url(r'^multiple/add/$', 'metadata_multiple_add', (), 'metadata_multiple_add'), + url(r'^(?P\d+)/remove/$', 'metadata_remove', (), 'metadata_remove'), + url(r'^multiple/remove/$', 'metadata_multiple_remove', (), 'metadata_multiple_remove'), +) diff --git a/apps/metadata/views.py b/apps/metadata/views.py new file mode 100644 index 0000000000..cd99f777ee --- /dev/null +++ b/apps/metadata/views.py @@ -0,0 +1,251 @@ +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ +from django.shortcuts import get_object_or_404 +from django.contrib import messages +from django.http import HttpResponseRedirect +from django.core.urlresolvers import reverse +from django.utils.http import urlencode + +from documents.models import Document, RecentDocument +from permissions.api import check_permissions +from filesystem_serving.api import document_create_fs_links, document_delete_fs_links + +from metadata import PERMISSION_METADATA_DOCUMENT_EDIT, \ + PERMISSION_METADATA_DOCUMENT_ADD, PERMISSION_METADATA_DOCUMENT_REMOVE +from metadata.forms import MetadataFormSet, AddMetadataForm, MetadataRemoveFormSet +from metadata.api import save_metadata_list +from metadata.models import DocumentMetadata, MetadataType + + +def metadata_edit(request, document_id=None, document_id_list=None): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_EDIT]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + if documents[0].documentmetadata_set.count() == 0: + messages.warning(request, _(u'The selected document doesn\'t have any metadata.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + for item in document.documentmetadata_set.all(): + value = item.value + if item.metadata_type in metadata: + if value not in metadata[item.metadata_type]: + metadata[item.metadata_type].append(value) + else: + metadata[item.metadata_type] = [value] if value else u'' + + initial = [] + for key, value in metadata.items(): + initial.append({ + 'metadata_type': key, + 'value': u', '.join(value) + }) + + formset = MetadataFormSet(initial=initial) + if request.method == 'POST': + formset = MetadataFormSet(request.POST) + if formset.is_valid(): + for document in documents: + 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}) + + for form in formset.forms: + if form.cleaned_data['update']: + try: + save_metadata_list([form.cleaned_data], document) + messages.success(request, _(u'Metadata for document %s edited successfully.') % document) + except Exception, e: + messages.error(request, _(u'Error editing metadata for document %(document)s; %(error)s.') % { + 'document': document, 'error': e}) + + try: + warnings = document_create_fs_links(document) + + if request.user.is_staff or request.user.is_superuser: + for warning in warnings: + messages.warning(request, warning) + + messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) + except Exception, e: + messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + if len(documents) == 1: + return HttpResponseRedirect(document.get_absolute_url()) + elif len(documents) > 1: + return HttpResponseRedirect(reverse('document_list_recent')) + + context = { + 'form_display_mode_table': True, + 'form': formset, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Edit metadata for document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Edit metadata for documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_edit(request): + return metadata_edit(request, document_id_list=request.GET.get('id_list', [])) + + +def metadata_add(request, document_id=None, document_id_list=None): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_ADD]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + if request.method == 'POST': + form = AddMetadataForm(request.POST) + if form.is_valid(): + metadata_type = form.cleaned_data['metadata_type'] + for document in documents: + document_metadata, created = DocumentMetadata.objects.get_or_create(document=document, metadata_type=metadata_type, defaults={'value': u''}) + if created: + messages.success(request, _(u'Metadata type: %(metadata_type)s successfully added to document %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + else: + messages.warning(request, _(u'Metadata type: %(metadata_type)s already present in document %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + if len(documents) == 1: + return HttpResponseRedirect(reverse(metadata_edit, args=[document.pk])) + elif len(documents) > 1: + return HttpResponseRedirect(u'%s?%s' % (reverse('metadata_multiple_edit'), urlencode({'id_list': document_id_list}))) + + else: + form = AddMetadataForm() + + context = { + #'form_display_mode_table': True, + 'form': form, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Add metadata type to document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Add metadata type to documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_add(request): + return metadata_add(request, document_id_list=request.GET.get('id_list', [])) + + +def metadata_remove(request, document_id): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_REMOVE]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + if documents[0].documentmetadata_set.count() == 0: + messages.warning(request, _(u'The selected document doesn\'t have any metadata.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + for item in document.documentmetadata_set.all(): + value = item.value + if item.metadata_type in metadata: + if value not in metadata[item.metadata_type]: + metadata[item.metadata_type].append(value) + else: + metadata[item.metadata_type] = [value] if value else u'' + + initial = [] + for key, value in metadata.items(): + initial.append({ + 'metadata_type': key, + 'value': u', '.join(value) + }) + + formset = MetadataRemoveFormSet(initial=initial) + if request.method == 'POST': + formset = MetadataRemoveFormSet(request.POST) + if formset.is_valid(): + for document in documents: + 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}) + + for form in formset.forms: + if form.cleaned_data['update']: + metadata_type = get_object_or_404(MetadataType, pk=form.cleaned_data['id']) + try: + document_metadata = DocumentMetadata.objects.get(document=document, metadata_type=metadata_type) + document_metadata.delete() + messages.success(request, _(u'Successfully remove metadata type: %(metadata_type)s from document: %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + except: + messages.error(request, _(u'Error removing metadata type: %(metadata_type)s from document: %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + + try: + warnings = document_create_fs_links(document) + + if request.user.is_staff or request.user.is_superuser: + for warning in warnings: + messages.warning(request, warning) + + messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) + except Exception, e: + messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + if len(documents) == 1: + return HttpResponseRedirect(document.get_absolute_url()) + elif len(documents) > 1: + return HttpResponseRedirect(reverse('document_list_recent')) + + context = { + 'form_display_mode_table': True, + 'form': formset, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Remove metadata types to document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Remove metadata types to documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_remove(request): + return metadata_remove(request, document_id_list=request.GET.get('id_list', [])) diff --git a/settings.py b/settings.py index 407fa95034..1591f996bd 100644 --- a/settings.py +++ b/settings.py @@ -125,7 +125,7 @@ INSTALLED_APPS = ( 'web_theme', 'main', 'common', - 'documents', + 'metadata', 'pagination', 'dynamic_search', 'filetransfers', @@ -145,6 +145,7 @@ INSTALLED_APPS = ( 'tags', 'document_comments', 'user_management', + 'documents', ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -160,7 +161,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( #--------- Pagination ------------------ #PAGINATION_DEFAULT_PAGINATION = 10 #--------- Web theme app --------------- -#WEB_THEME = 'default' +#WEB_THEME_THEME = 'default' #-------------- Main ----------------- #MAIN_SIDE_BAR_SEARCH = False #------------ Common -------------- @@ -176,10 +177,11 @@ TEMPLATE_CONTEXT_PROCESSORS = ( #STORAGE_GRIDFS_DATABASE_NAME = u'document_storage' # Filebased #STORAGE_FILESTORAGE_LOCATION = u'document_storage' +#---------- Metadata ----------------- +# METADATA_AVAILABLE_FUNCTIONS = {} +# METADATA_AVAILABLE_MODELS = {} #---------- Documents ------------------ # Definition -#DOCUMENTS_METADATA_AVAILABLE_FUNCTIONS = {} -#DOCUMENTS_METADATA_AVAILABLE_MODELS = {} #DOCUMENTS_INDEXING_AVAILABLE_FUNCTIONS = {} # Upload diff --git a/urls.py b/urls.py index b3520b487f..afc451f855 100644 --- a/urls.py +++ b/urls.py @@ -21,6 +21,7 @@ urlpatterns = patterns('', (r'^comments/', include('document_comments.urls')), (r'^user_management/', include('user_management.urls')), (r'^settings/', include('smart_settings.urls')), + (r'^metadata/', include('metadata.urls')), ) From f7f7a1dbdcf07726fac0aa81dac72a21d8c8b1a8 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:42:13 -0400 Subject: [PATCH 08/50] Splitted common/forms into forms & widgets --- apps/common/forms.py | 49 ++---------------------------------------- apps/common/widgets.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 47 deletions(-) create mode 100644 apps/common/widgets.py diff --git a/apps/common/forms.py b/apps/common/forms.py index 4615a1ee9f..93bf87db86 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -1,55 +1,10 @@ from django import forms from django.utils.safestring import mark_safe -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ from django.db import models from common.utils import return_attrib - - -class DetailSelectMultiple(forms.widgets.SelectMultiple): - def __init__(self, queryset=None, *args, **kwargs): - self.queryset = queryset - super(DetailSelectMultiple, self).__init__(*args, **kwargs) - - def render(self, name, value, attrs=None, choices=()): - if value is None: - value = '' - final_attrs = self.build_attrs(attrs, name=name) - css_class = final_attrs.get('class', 'list') - output = u'
    ' % css_class - options = None - if value: - if getattr(value, '__iter__', None): - options = [(index, string) for index, string in \ - self.choices if index in value] - else: - options = [(index, string) for index, string in \ - self.choices if index == value] - else: - if self.choices: - if self.choices[0] != (u'', u'---------') and value != []: - options = [(index, string) for index, string in \ - self.choices] - - if options: - for index, string in options: - if self.queryset: - try: - output += u'
  • %s
  • ' % ( - self.queryset.get(pk=index).get_absolute_url(), - string) - except AttributeError: - output += u'
  • %s
  • ' % (string) - else: - output += u'
  • %s
  • ' % string - else: - output += u'
  • %s
  • ' % _(u"None") - return mark_safe(output + u'
\n') - - -class PlainWidget(forms.widgets.Widget): - def render(self, name, value, attrs=None): - return mark_safe(u'%s' % value) +from common.widgets import DetailSelectMultiple, PlainWidget class DetailForm(forms.ModelForm): diff --git a/apps/common/widgets.py b/apps/common/widgets.py new file mode 100644 index 0000000000..2d07facbdf --- /dev/null +++ b/apps/common/widgets.py @@ -0,0 +1,49 @@ +from django.utils.translation import ugettext_lazy as _ +from django.utils.safestring import mark_safe +from django import forms + + +class PlainWidget(forms.widgets.Widget): + def render(self, name, value, attrs=None): + return mark_safe(u'%s' % value) + + +class DetailSelectMultiple(forms.widgets.SelectMultiple): + def __init__(self, queryset=None, *args, **kwargs): + self.queryset = queryset + super(DetailSelectMultiple, self).__init__(*args, **kwargs) + + def render(self, name, value, attrs=None, choices=(), *args, **kwargs): + if value is None: + value = '' + final_attrs = self.build_attrs(attrs, name=name) + css_class = final_attrs.get('class', 'list') + output = u'
    ' % css_class + options = None + if value: + if getattr(value, '__iter__', None): + options = [(index, string) for index, string in \ + self.choices if index in value] + else: + options = [(index, string) for index, string in \ + self.choices if index == value] + else: + if self.choices: + if self.choices[0] != (u'', u'---------') and value != []: + options = [(index, string) for index, string in \ + self.choices] + + if options: + for index, string in options: + if self.queryset: + try: + output += u'
  • %s
  • ' % ( + self.queryset.get(pk=index).get_absolute_url(), + string) + except AttributeError: + output += u'
  • %s
  • ' % (string) + else: + output += u'
  • %s
  • ' % string + else: + output += u'
  • %s
  • ' % _(u"None") + return mark_safe(output + u'
\n') From 69d38c067c14922a654b06ab17379bf136eaefd8 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:42:36 -0400 Subject: [PATCH 09/50] Simplified and improved navigation link registration --- apps/navigation/api.py | 64 ++++++++++++------------------------------ 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/apps/navigation/api.py b/apps/navigation/api.py index 83f5ce1b24..470c04a013 100644 --- a/apps/navigation/api.py +++ b/apps/navigation/api.py @@ -1,5 +1,3 @@ -import copy - object_navigation = {} multi_object_navigation = {} menu_links = [] @@ -8,47 +6,25 @@ sidebar_templates = {} def register_multi_item_links(src, links, menu_name=None): - if menu_name in multi_object_navigation: - if hasattr(src, '__iter__'): - for one_src in src: - if one_src in object_navigation[menu_name]: - multi_object_navigation[menu_name][one_src]['links'].extend(links) - else: - multi_object_navigation[menu_name][one_src] = {'links': copy.copy(links)} - else: - if src in multi_object_navigation[menu_name]: - multi_object_navigation[menu_name][src]['links'].extend(links) - else: - multi_object_navigation[menu_name][src] = {'links': links} + multi_object_navigation.setdefault(menu_name, {}) + if hasattr(src, '__iter__'): + for one_src in src: + multi_object_navigation[menu_name].setdefault(one_src, {'links': []}) + multi_object_navigation[menu_name][one_src]['links'].extend(links) else: - multi_object_navigation[menu_name] = {} - if hasattr(src, '__iter__'): - for one_src in src: - multi_object_navigation[menu_name][one_src] = {'links': links} - else: - multi_object_navigation[menu_name] = {src: {'links': links}} + multi_object_navigation[menu_name].setdefault(src, {'links': []}) + multi_object_navigation[menu_name][src]['links'].extend(links) def register_links(src, links, menu_name=None): - if menu_name in object_navigation: - if hasattr(src, '__iter__'): - for one_src in src: - if one_src in object_navigation[menu_name]: - object_navigation[menu_name][one_src]['links'].extend(links) - else: - object_navigation[menu_name][one_src] = {'links': copy.copy(links)} - else: - if src in object_navigation[menu_name]: - object_navigation[menu_name][src]['links'].extend(links) - else: - object_navigation[menu_name][src] = {'links': links} + object_navigation.setdefault(menu_name, {}) + if hasattr(src, '__iter__'): + for one_src in src: + object_navigation[menu_name].setdefault(one_src, {'links': []}) + object_navigation[menu_name][one_src]['links'].extend(links) else: - object_navigation[menu_name] = {} - if hasattr(src, '__iter__'): - for one_src in src: - object_navigation[menu_name][one_src] = {'links': links} - else: - object_navigation[menu_name] = {src: {'links': links}} + object_navigation[menu_name].setdefault(src, {'links': []}) + object_navigation[menu_name][src]['links'].extend(links) def register_menu(links): @@ -59,15 +35,11 @@ def register_menu(links): def register_model_list_columns(model, columns): - if model in model_list_columns: - model_list_columns[model].extend(columns) - else: - model_list_columns[model] = copy.copy(columns) + model_list_columns.setdefault(model, []) + model_list_columns[model].extend(columns) def register_sidebar_template(source_list, template_name): for source in source_list: - if source in sidebar_templates: - sidebar_templates[source].append(template_name) - else: - sidebar_templates[source] = [template_name] + sidebar_templates.setdefault(source, []) + sidebar_templates[source].append(template_name) From e63cbba1715ee10b21675ca1a953b8ac2a461040 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:43:17 -0400 Subject: [PATCH 10/50] Use lazy translation instead --- apps/folders/forms.py | 2 +- apps/folders/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/folders/forms.py b/apps/folders/forms.py index 4354ba9bbe..a199b14d9e 100644 --- a/apps/folders/forms.py +++ b/apps/folders/forms.py @@ -1,5 +1,5 @@ from django import forms -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ from folders.models import Folder diff --git a/apps/folders/views.py b/apps/folders/views.py index 487205e60f..98a09770ba 100644 --- a/apps/folders/views.py +++ b/apps/folders/views.py @@ -1,4 +1,4 @@ -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext From cd116354432796e6898aad0f12bdd8559099496c Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 03:43:42 -0400 Subject: [PATCH 11/50] Added missing import --- apps/filesystem_serving/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/filesystem_serving/api.py b/apps/filesystem_serving/api.py index c003f3101c..bde9185d91 100644 --- a/apps/filesystem_serving/api.py +++ b/apps/filesystem_serving/api.py @@ -5,7 +5,7 @@ from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ from documents.conf.settings import AVAILABLE_INDEXING_FUNCTIONS -from documents.classes import MetadataObject +from metadata.classes import MetadataObject from filesystem_serving.conf.settings import FILESERVING_ENABLE from filesystem_serving.conf.settings import FILESERVING_PATH From 6dbf89bdcca57f11122a524939669e75c3947c3c Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 04:15:27 -0400 Subject: [PATCH 12/50] Added missing import --- apps/documents/forms.py | 1 + apps/documents/views.py | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 94e3474a2c..e5a1d9795d 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -17,6 +17,7 @@ from common.conf.settings import DEFAULT_PAPER_SIZE from common.conf.settings import DEFAULT_PAGE_ORIENTATION from common.utils import urlquote from metadata.models import MetadataSet, MetadataType +from metadata.forms import MetadataFormSet from documents.staging import StagingFile from documents.models import Document, DocumentType, \ diff --git a/apps/documents/views.py b/apps/documents/views.py index 6c1915d593..1e119b81ca 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -33,6 +33,7 @@ from common.literals import PAGE_SIZE_DIMENSIONS, \ from common.conf.settings import DEFAULT_PAPER_SIZE from metadata.api import save_metadata_list, \ decode_metadata_from_url, metadata_repr_as_list +from metadata.forms import MetadataFormSet from documents.conf.settings import DELETE_STAGING_FILE_AFTER_UPLOAD from documents.conf.settings import USE_STAGING_DIRECTORY From c91d05bff7bac73eb020228ef2cb0ab8c40935a5 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 May 2011 04:20:14 -0400 Subject: [PATCH 13/50] Added document to convert to the new metadata tables --- contrib/metadata_conversion.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 contrib/metadata_conversion.txt diff --git a/contrib/metadata_conversion.txt b/contrib/metadata_conversion.txt new file mode 100644 index 0000000000..f21d250da9 --- /dev/null +++ b/contrib/metadata_conversion.txt @@ -0,0 +1,7 @@ +mysqldump mayan documents_metadatatype -u root -p | replace 'documents_metadatatype' 'metadata_metadatatype' > new_types.mysql +cat new_types.mysql | mysql -u root -p mayan + +mysqldump mayan documents_documentmetadata -u root -p | replace 'documents_documentmetadata' 'metadata_documentmetadata' > new_metadata.mysql +cat new_metadata.mysql | mysql -u root -p mayan + + From 5a0ae2eb32c3361fd71227892c003886685ef4a0 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 11 May 2011 21:01:06 -0400 Subject: [PATCH 14/50] Edited metadata api to allow switchable creation of new document metadata --- apps/documents/views.py | 2 +- apps/metadata/api.py | 39 ++++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/apps/documents/views.py b/apps/documents/views.py index 1e119b81ca..c74381b2fa 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -122,7 +122,7 @@ def _handle_save_document(request, document, form=None): document.file_filename = form.cleaned_data['document_type_available_filenames'].filename document.save() - save_metadata_list(decode_metadata_from_url(request.GET), document) + save_metadata_list(decode_metadata_from_url(request.GET), document, create=True) try: warnings = document_create_fs_links(document) if request.user.is_staff or request.user.is_superuser: diff --git a/apps/metadata/api.py b/apps/metadata/api.py index 7bfe0acff0..f55a262726 100644 --- a/apps/metadata/api.py +++ b/apps/metadata/api.py @@ -32,12 +32,12 @@ def decode_metadata_from_url(url_dict): return metadata_list -def save_metadata_list(metadata_list, document): +def save_metadata_list(metadata_list, document, create=False): """ Take a list of metadata values and associate a document to it """ for item in metadata_list: - save_metadata(item, document) + save_metadata(item, document, create) #if item['value']: # save_metadata(item, document) @@ -55,24 +55,25 @@ def save_metadata_list(metadata_list, document): # pass -def save_metadata(metadata_dict, document): +def save_metadata(metadata_dict, document, create=False): """save metadata_dict""" - # Use matched metadata now to create document metadata - #document_metadata, created = DocumentMetadata.objects.get_or_create( - # document=document, - # metadata_type=get_object_or_404( - # MetadataType, - # pk=metadata_dict['id'] - # ), - #) - - document_metadata = DocumentMetadata.objects.get( - document=document, - metadata_type=get_object_or_404( - MetadataType, - pk=metadata_dict['id'] - ), - ) + if create: + # Use matched metadata now to create document metadata + document_metadata, created = DocumentMetadata.objects.get_or_create( + document=document, + metadata_type=get_object_or_404( + MetadataType, + pk=metadata_dict['id'] + ), + ) + else: + document_metadata = DocumentMetadata.objects.get( + document=document, + metadata_type=get_object_or_404( + MetadataType, + pk=metadata_dict['id'] + ), + ) # Handle 'plus sign as space' in the url From 2162e99543a8f8d8083bae824896b4d3fe5b0e7b Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 11 May 2011 22:08:52 -0400 Subject: [PATCH 15/50] Finished converting project to the new subtemplate rendering --- apps/common/templates/generic_detail.html | 123 +++---- .../templates/generic_detail_subtemplate.html | 44 --- apps/common/templates/generic_form.html | 139 +++----- apps/common/templates/generic_list.html | 68 ---- apps/documents/__init__.py | 4 +- apps/documents/urls.py | 2 +- apps/documents/views.py | 308 ++++++++++-------- apps/main/templates/base.html | 2 +- apps/permissions/views.py | 30 +- apps/user_management/views.py | 28 +- docs/TODO | 15 +- 11 files changed, 287 insertions(+), 476 deletions(-) diff --git a/apps/common/templates/generic_detail.html b/apps/common/templates/generic_detail.html index e201229699..6b2f05faf9 100644 --- a/apps/common/templates/generic_detail.html +++ b/apps/common/templates/generic_detail.html @@ -10,28 +10,26 @@ {% include subtemplate %} {% endfor %} - - {% for subtemplate in sidebar_subtemplates_dict %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% with subtemplate.hide_columns as hide_columns %} - {% with "true" as side_bar %} - {% include subtemplate.name %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} + + {% for subtemplate in sidebar_subtemplates_list %} + {% with "true" as side_bar %} + {% if subtemplate.form %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {% with "true" as read_only %} +
+ {{ rendered_subtemplate }} +
+ {% endwith %} + {% else %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {{ rendered_subtemplate }} + {% endif %} + + {% if subtemplate.grid_clear or not subtemplate.grid %} +
+ {% endif %} + {% endwith %} + {% endfor %} {% endblock %} {% block stylesheets %} @@ -45,83 +43,38 @@ {% endblock %} {% block content %} -
+
{% if form %} {% with "true" as read_only %} -
+
{% include "generic_form_subtemplate.html" %}
{% if grid_clear or not grid %} -
+
{% endif %} {% endwith %} {% endif %} - {% for subtemplate in subtemplates %} - {% include subtemplate %} - {% endfor %} - - {% for form in form_list %} - {% with form.submit_method as submit_method %} - {% with form.striptags as striptags %} - {% with form.title as title %} - {% with form.object as object %} - {% with form.object_name as object_name %} - {% with form.form_action as form_action %} - {% with "true" as read_only %} -
- {% with form.form as form %} -
- {% include "generic_form_subtemplate.html" %} -
- {% endwith %} -
- {% if form.grid_clear or not form.grid %} -
- {% endif %} - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} - {% for subtemplate in subtemplates_list %} - -
- - {% if subtemplate.form %} - {% with subtemplate.submit_method as submit_method %} - {% with subtemplate.striptags as striptags %} - {% with subtemplate.object as object %} - {% with subtemplate.object_name as object_name %} - {% with subtemplate.form_action as form_action %} - {% with "true" as read_only %} - {% with subtemplate.form as form %} -
- {% include subtemplate.name %} -
- {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% else %} - {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} - {{ rendered_subtemplate }} - {% endif %} -
- {% if subtemplate.grid_clear or not subtemplate.grid %} -
+
+ {% if subtemplate.form %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {% with "true" as read_only %} +
+ {{ rendered_subtemplate }} +
+ {% endwith %} + {% else %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {{ rendered_subtemplate }} {% endif %} +
+ {% if subtemplate.grid_clear or not subtemplate.grid %} +
+ {% endif %} {% endfor %}
{% endblock %} diff --git a/apps/common/templates/generic_detail_subtemplate.html b/apps/common/templates/generic_detail_subtemplate.html index aae27f1983..7d8129f0bc 100644 --- a/apps/common/templates/generic_detail_subtemplate.html +++ b/apps/common/templates/generic_detail_subtemplate.html @@ -19,49 +19,5 @@
{% endwith %} {% endif %} - - {% for subtemplate in subtemplates %} - {% include subtemplate %} - {% endfor %} - - {% for form in form_list %} - {% with form.submit_method as submit_method %} - {% with form.striptags as striptags %} - {% with form.title as title %} - {% with form.object as object %} - {% with form.object_name as object_name %} - {% with form.form_action as form_action %} - {% with "true" as read_only %} - {% with form.form as form %} - {% include "generic_form_subtemplate.html" %} - {% endwith %} - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} - - - {% for subtemplate in subtemplates_dict %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% include subtemplate.name %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} {% endblock %} diff --git a/apps/common/templates/generic_form.html b/apps/common/templates/generic_form.html index ba6afd3efb..29eb58ce16 100644 --- a/apps/common/templates/generic_form.html +++ b/apps/common/templates/generic_form.html @@ -1,116 +1,51 @@ {% extends "base.html" %} +{% load subtemplates_tags %} + {% block title %} :: {% with "true" as striptags %}{% include "calculate_form_title.html" %}{% endwith %}{% endblock %} {% block sidebar %} - {% for subtemplate in sidebar_subtemplates %} -
- {% include subtemplate %} -
- {% endfor %} - {% for subtemplate in sidebar_subtemplates_list %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% with subtemplate.hide_columns as hide_columns %} - {% with "true" as side_bar %} - - {% with subtemplate.submit_method as submit_method %} - {% with subtemplate.striptags as striptags %} - {% with subtemplate.title as title %} - {% with subtemplate.object as object %} - {% with subtemplate.object_name as object_name %} - {% with subtemplate.form_action as form_action %} - {% with subtemplate.submit_label as submit_label %} - {% with subtemplate.form as form %} - - {% with subtemplate.content as content %} - {% with subtemplate.paragraphs as paragraphs %} - - {% include subtemplate.name %} - - {% endwith %} - {% endwith %} - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} + {% if subtemplate.form %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} +
+ {{ rendered_subtemplate }} +
+ {% else %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {{ rendered_subtemplate }} + {% endif %} + {% if subtemplate.grid_clear or not subtemplate.grid %} + {% endif %} + {% endfor %} {% endblock %} {% block content %} -
- {% if form %} -
- {% include "generic_form_subtemplate.html" %} -
- {% if form.grid_clear or not form.grid %} -
- {% endif %} - {% endif %} - - {% for form in form_list %} - {% with form.submit_method as submit_method %} - {% with form.striptags as striptags %} - {% with form.title as title %} - {% with form.object as object %} - {% with form.object_name as object_name %} - {% with form.submit_label as submit_label %} - {% with form.form_action as form_action %} -
- {% with form.form as form %} - {% include "generic_form_subtemplate.html" %} - {% endwith %} +
+ {% if form %} +
+ {% include "generic_form_subtemplate.html" %}
{% if form.grid_clear or not form.grid %} -
+
{% endif %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} -
+ {% endif %} - {% for subtemplate in subtemplates_dict %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% include subtemplate.name %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} + {% for subtemplate in subtemplates_list %} +
+ {% if subtemplate.form %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} +
+ {{ rendered_subtemplate }} +
+ {% else %} + {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %} + {{ rendered_subtemplate }} + {% endif %} +
+ {% if subtemplate.grid_clear or not subtemplate.grid %} +
+ {% endif %} + {% endfor %} +
{% endblock %} diff --git a/apps/common/templates/generic_list.html b/apps/common/templates/generic_list.html index 8f0c35f702..f0a72cfb02 100644 --- a/apps/common/templates/generic_list.html +++ b/apps/common/templates/generic_list.html @@ -4,84 +4,16 @@ {% block title %} :: {% blocktrans %}List of {{ title }}{% endblocktrans %}{% endblock %} {#{% block secondary_links %}{{ secondary_links|safe }}{% endblock %}#} - {% block sidebar %} {% for subtemplate in sidebar_subtemplates %}
{% include subtemplate %}
{% endfor %} - - {% for subtemplate in sidebar_subtemplates_list %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% with subtemplate.hide_columns as hide_columns %} - {% with "true" as side_bar %} - - {% with subtemplate.submit_method as submit_method %} - {% with subtemplate.striptags as striptags %} - {% with subtemplate.title as title %} - {% with subtemplate.object as object %} - {% with subtemplate.object_name as object_name %} - {% with subtemplate.form_action as form_action %} - {% with subtemplate.form as form %} - - {% with subtemplate.content as content %} - {% with subtemplate.paragraphs as paragraphs %} - - {% include subtemplate.name %} - - {% endwith %} - {% endwith %} - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - - - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} {% endblock %} {% block content %} {% include "generic_list_subtemplate.html" %} - {% for subtemplate in subtemplates_dict %} - {% with subtemplate.title as title %} - {% with subtemplate.object_list as object_list %} - {% with subtemplate.extra_columns as extra_columns %} - {% with subtemplate.hide_object as hide_object %} - {% with subtemplate.main_object as main_object %} - {% with subtemplate.hide_link as hide_link %} - {% with subtemplate.hide_header as hide_header %} - {% with subtemplate.multi_select as multi_select %} - {% include subtemplate.name %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endwith %} - {% endfor %} - {% endblock %} diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index 3515837b0b..67f0cfe77b 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -37,8 +37,8 @@ document_list_recent = {'text': _(u'recent documents list'), 'view': 'document_l document_create = {'text': _(u'upload a new document'), 'view': 'document_create', 'famfam': 'page_add', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}} document_create_multiple = {'text': _(u'upload multiple new documents'), 'view': 'document_create_multiple', 'famfam': 'page_add', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}} document_create_sibling = {'text': _(u'upload new document using same metadata'), 'view': 'document_create_sibling', 'args': 'object.id', 'famfam': 'page_copy', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}} -document_view = {'text': _(u'details (advanced)'), 'view': 'document_view', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_view_simple = {'text': _(u'details (simple)'), 'view': 'document_view_simple', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} +document_view_advanced = {'text': _(u'details (advanced)'), 'view': 'document_view_advanced', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_delete = {'text': _(u'delete'), 'view': 'document_delete', 'args': 'object.id', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_delete', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]}} @@ -79,7 +79,7 @@ metadata_group_create_sibling = {'text': _(u'upload new document using same meta staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': 'object.id', 'famfam': 'drive_magnify'} staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': 'object.id', 'famfam': 'drive_delete'} -register_links(Document, [document_view_simple, document_view, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) +register_links(Document, [document_view_simple, document_view_advanced, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) register_links(Document, [document_create_sibling], menu_name='sidebar') register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete]) diff --git a/apps/documents/urls.py b/apps/documents/urls.py index 22308c3982..e843201ee6 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -16,7 +16,7 @@ urlpatterns = patterns('documents.views', url(r'^document/create/from/local/multiple/$', 'document_create', {'multiple': True}, 'document_create_multiple'), 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+)/$', 'document_view_advanced', (), 'document_view_advanced'), url(r'^document/(?P\d+)/simple/$', 'document_view_simple', (), 'document_view_simple'), url(r'^document/(?P\d+)/delete/$', 'document_delete', (), 'document_delete'), url(r'^document/multiple/delete/$', 'document_multiple_delete', (), 'document_multiple_delete'), diff --git a/apps/documents/views.py b/apps/documents/views.py index c74381b2fa..9fc2c31cef 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -205,16 +205,15 @@ def upload_document_with_type(request, multiple=True): else: return HttpResponseRedirect(reverse('document_list')) - context = { - 'document_type_id': document_type_id, - 'form_list': [ - { - 'form': local_form, - 'title': _(u'upload a local document'), - 'grid': 6, - 'grid_clear': False if USE_STAGING_DIRECTORY else True, - }, - ], + subtemplates_list = [] + local_upload_form = { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'form': local_form, + 'title': _(u'upload a local document'), + 'grid': 6, + 'grid_clear': False if USE_STAGING_DIRECTORY else True, + } } if USE_STAGING_DIRECTORY: @@ -224,45 +223,146 @@ def upload_document_with_type(request, multiple=True): messages.error(request, e) filelist = [] finally: - context.update({ - 'subtemplates_dict': [ - { - 'name': 'generic_list_subtemplate.html', + subtemplates_list.append(local_upload_form) + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'form': staging_form, + 'title': _(u'upload a document from staging'), + 'grid': 6, + 'grid_clear': True, + } + }, + ) + subtemplates_list.append( + { + 'name': 'generic_list_subtemplate.html', + 'context': { 'title': _(u'files in staging'), 'object_list': filelist, 'hide_link': True, - }, - ], - }) - context['form_list'].append( - { - 'form': staging_form, - 'title': _(u'upload a document from staging'), - 'grid': 6, - 'grid_clear': True, + } }, - ) + ) + else: + subtemplates_list.append(local_upload_form) + + + context = { + 'document_type_id': document_type_id, + 'subtemplates_list': subtemplates_list, + } context.update({ 'sidebar_subtemplates_list': [ { - 'title': _(u'Current metadata'), 'name': 'generic_subtemplate.html', - #'content': metadata_repr(decode_metadata_from_url(request.GET)), - 'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)) + 'context': { + 'title': _(u'Current metadata'), + #'content': metadata_repr(decode_metadata_from_url(request.GET)), + 'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)) + } }] - }) + }) return render_to_response('generic_form.html', context, context_instance=RequestContext(request)) -def document_view(request, document_id): +def document_view_simple(request, document_id): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) document = get_object_or_404(Document.objects.select_related(), pk=document_id) RecentDocument.objects.add_document_for_user(request.user, document) + subtemplates_list = [] + + content_form = DocumentContentForm(document=document) + + preview_form = DocumentPreviewForm(document=document) + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'form': preview_form, + 'object': document, + } + }, + ) + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'title':_(u'document properties'), + 'form': content_form, + 'object': document, + }, + } + ) + + if document.tags.count(): + subtemplates_list.append(get_tags_subtemplate(document)) + + if Comment.objects.for_model(document).count(): + subtemplates_list.append(get_comments_subtemplate(document)) + + subtemplates_list.append( + { + 'name': 'generic_list_subtemplate.html', + 'context': { + 'title': _(u'metadata'), + 'object_list': document.documentmetadata_set.all(), + 'extra_columns': [{'name': _(u'value'), 'attribute': 'value'}], + 'hide_link': True, + } + }, + ) + + metadata_groups, errors = document.get_metadata_groups() + if (request.user.is_staff or request.user.is_superuser) and errors: + for error in errors: + messages.warning(request, _(u'Document group query error: %s' % error)) + + if not GROUP_SHOW_EMPTY: + #If GROUP_SHOW_EMPTY is False, remove empty groups from + #dictionary + metadata_groups = dict([(group, data) for group, data in metadata_groups.items() if data]) + + if metadata_groups: + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'title': _(u'document groups (%s)') % len(metadata_groups.keys()), + 'form': MetaDataGroupForm( + groups=metadata_groups, current_document=document, + links=[ + metadata_group_link + ] + ), + 'form_action': reverse('metadatagroup_action'), + 'submit_method': 'GET', + } + } + ) + + return render_to_response('generic_detail.html', { + 'object': document, + 'document': document, + 'subtemplates_list': subtemplates_list, + }, context_instance=RequestContext(request)) + + +def document_view_advanced(request, document_id): + check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) + + document = get_object_or_404(Document.objects.select_related(), pk=document_id) + + RecentDocument.objects.add_document_for_user(request.user, document) + + subtemplates_list = [] + form = DocumentForm_view(instance=document, extra_fields=[ {'label': _(u'Filename'), 'field': 'file_filename'}, {'label': _(u'File extension'), 'field': 'file_extension'}, @@ -279,19 +379,27 @@ def document_view(request, document_id): ]) preview_form = DocumentPreviewForm(document=document) - form_list = [ - { - 'form': preview_form, - 'object': document, - }, - { - 'title': _(u'document properties'), - 'form': form, - 'object': document, - }, - ] - subtemplates_list = [] + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'form': preview_form, + 'object': document, + } + }, + ) + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'form': form, + 'title': _(u'document properties'), + 'object': document, + } + }, + ) + if document.tags.count(): subtemplates_list.append(get_tags_subtemplate(document)) @@ -346,7 +454,6 @@ def document_view(request, document_id): }) return render_to_response('generic_detail.html', { - 'form_list': form_list, 'object': document, 'document': document, 'subtemplates_list': subtemplates_list, @@ -723,13 +830,13 @@ def document_clear_transformations(request, document_id=None, document_id_list=N if document_id: documents = [get_object_or_404(Document.objects, pk=document_id)] - post_redirect = reverse('document_view', args=[documents[0].pk]) + post_redirect = reverse('document_view_simple', args=[documents[0].pk]) elif document_id_list: documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] post_redirect = None else: messages.error(request, _(u'Must provide at least one document.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', post_redirect or reverse('document_list')))) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_redirect or reverse('document_list')))) @@ -768,83 +875,6 @@ def document_multiple_clear_transformations(request): return document_clear_transformations(request, document_id_list=request.GET.get('id_list', [])) -def document_view_simple(request, document_id): - check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - - document = get_object_or_404(Document.objects.select_related(), pk=document_id) - - RecentDocument.objects.add_document_for_user(request.user, document) - - content_form = DocumentContentForm(document=document) - - preview_form = DocumentPreviewForm(document=document) - form_list = [ - { - 'form': preview_form, - 'object': document, - }, - { - 'title':_(u'document properties'), - 'form': content_form, - 'object': document, - }, - ] - - subtemplates_list = [] - if document.tags.count(): - subtemplates_list.append(get_tags_subtemplate(document)) - - if Comment.objects.for_model(document).count(): - subtemplates_list.append(get_comments_subtemplate(document)) - - subtemplates_list.append( - { - 'name': 'generic_list_subtemplate.html', - 'context': { - 'title': _(u'metadata'), - 'object_list': document.documentmetadata_set.all(), - 'extra_columns': [{'name': _(u'value'), 'attribute': 'value'}], - 'hide_link': True, - } - }, - ) - - metadata_groups, errors = document.get_metadata_groups() - if (request.user.is_staff or request.user.is_superuser) and errors: - for error in errors: - messages.warning(request, _(u'Document group query error: %s' % error)) - - if not GROUP_SHOW_EMPTY: - #If GROUP_SHOW_EMPTY is False, remove empty groups from - #dictionary - metadata_groups = dict([(group, data) for group, data in metadata_groups.items() if data]) - - if metadata_groups: - subtemplates_list.append( - { - 'name': 'generic_form_subtemplate.html', - 'context': { - 'title': _(u'document groups (%s)') % len(metadata_groups.keys()), - 'form': MetaDataGroupForm( - groups=metadata_groups, current_document=document, - links=[ - metadata_group_link - ] - ), - 'form_action': reverse('metadatagroup_action'), - 'submit_method': 'GET', - } - } - ) - - return render_to_response('generic_detail.html', { - 'form_list': form_list, - 'object': document, - 'document': document, - 'subtemplates_list': subtemplates_list, - }, context_instance=RequestContext(request)) - - def document_missing_list(request): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) @@ -876,16 +906,11 @@ def document_page_view(request, document_page_id): rotation = int(request.GET.get('rotation', 0)) document_page_form = DocumentPageForm(instance=document_page, zoom=zoom, rotation=rotation) - form_list = [ - { - 'form': document_page_form, - 'title': _(u'details for: %s') % document_page, - }, - ] return render_to_response('generic_detail.html', { - 'form_list': form_list, 'object': document_page, 'web_theme_hide_menus': True, + 'form': document_page_form, + 'title': _(u'details for: %s') % document_page, }, context_instance=RequestContext(request)) @@ -895,16 +920,11 @@ def document_page_text(request, document_page_id): document_page = get_object_or_404(DocumentPage, pk=document_page_id) document_page_form = DocumentPageForm_text(instance=document_page) - form_list = [ - { - 'form': document_page_form, - 'title': _(u'details for: %s') % document_page, - }, - ] return render_to_response('generic_detail.html', { - 'form_list': form_list, 'object': document_page, 'web_theme_hide_menus': True, + 'form': document_page_form, + 'title': _(u'details for: %s') % document_page, }, context_instance=RequestContext(request)) @@ -934,12 +954,12 @@ def document_page_edit(request, document_page_id): def document_page_navigation_next(request, document_page_id): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) if document_page.page_number >= document_page.document.documentpage_set.count(): messages.warning(request, _(u'There are no more pages in this document')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) else: document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.page_number + 1) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -947,12 +967,12 @@ def document_page_navigation_next(request, document_page_id): def document_page_navigation_previous(request, document_page_id): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) if document_page.page_number <= 1: messages.warning(request, _(u'You are already at the first page of this document')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) else: document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.page_number - 1) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -960,7 +980,7 @@ def document_page_navigation_previous(request, document_page_id): def document_page_navigation_first(request, document_page_id): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=1) @@ -969,7 +989,7 @@ def document_page_navigation_first(request, document_page_id): def document_page_navigation_last(request, document_page_id): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.document.documentpage_set.count()) @@ -986,11 +1006,11 @@ def document_list_recent(request): def transform_page(request, document_page_id, zoom_function=None, rotation_function=None): check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) - view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) # Get the query string from the referer url - query = urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).query + query = urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).query # Parse the query string and get the zoom value # parse_qs return a dictionary whose values are lists zoom = int(urlparse.parse_qs(query).get('zoom', ['100'])[0]) @@ -1047,7 +1067,7 @@ def metadatagroup_action(request): if not action: messages.error(request, _(u'No action selected.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) return HttpResponseRedirect(action) diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html index 28c13164ca..16bbd160ba 100644 --- a/apps/main/templates/base.html +++ b/apps/main/templates/base.html @@ -30,7 +30,7 @@ {##} - + {##}