Created new metadata app and move all code relating metadata to it
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
52
apps/documents/managers.py
Normal file
52
apps/documents/managers.py
Normal file
@@ -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
|
||||
@@ -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):
|
||||
|
||||
@@ -21,8 +21,6 @@ urlpatterns = patterns('documents.views',
|
||||
url(r'^document/(?P<document_id>\d+)/delete/$', 'document_delete', (), 'document_delete'),
|
||||
url(r'^document/multiple/delete/$', 'document_multiple_delete', (), 'document_multiple_delete'),
|
||||
url(r'^document/(?P<document_id>\d+)/edit/$', 'document_edit', (), 'document_edit'),
|
||||
url(r'^document/(?P<document_id>\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<document_id>\d+)/print/$', 'document_print', (), 'document_print'),
|
||||
url(r'^document/(?P<document_id>\d+)/hard_copy/$', 'document_hard_copy', (), 'document_hard_copy'),
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
28
apps/metadata/__init__.py
Normal file
28
apps/metadata/__init__.py
Normal file
@@ -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])
|
||||
30
apps/metadata/admin.py
Normal file
30
apps/metadata/admin.py
Normal file
@@ -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)
|
||||
@@ -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?!?
|
||||
|
||||
1
apps/metadata/conf/__init__.py
Normal file
1
apps/metadata/conf/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
27
apps/metadata/conf/settings.py
Normal file
27
apps/metadata/conf/settings.py
Normal file
@@ -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},
|
||||
]
|
||||
)
|
||||
67
apps/metadata/forms.py
Normal file
67
apps/metadata/forms.py
Normal file
@@ -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)
|
||||
74
apps/metadata/models.py
Normal file
74
apps/metadata/models.py
Normal file
@@ -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')
|
||||
23
apps/metadata/tests.py
Normal file
23
apps/metadata/tests.py
Normal file
@@ -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
|
||||
"""}
|
||||
|
||||
10
apps/metadata/urls.py
Normal file
10
apps/metadata/urls.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('metadata.views',
|
||||
url(r'^(?P<document_id>\d+)/edit/$', 'metadata_edit', (), 'metadata_edit'),
|
||||
url(r'^multiple/edit/$', 'metadata_multiple_edit', (), 'metadata_multiple_edit'),
|
||||
url(r'^(?P<document_id>\d+)/add/$', 'metadata_add', (), 'metadata_add'),
|
||||
url(r'^multiple/add/$', 'metadata_multiple_add', (), 'metadata_multiple_add'),
|
||||
url(r'^(?P<document_id>\d+)/remove/$', 'metadata_remove', (), 'metadata_remove'),
|
||||
url(r'^multiple/remove/$', 'metadata_multiple_remove', (), 'metadata_multiple_remove'),
|
||||
)
|
||||
251
apps/metadata/views.py
Normal file
251
apps/metadata/views.py
Normal file
@@ -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', []))
|
||||
10
settings.py
10
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
|
||||
|
||||
Reference in New Issue
Block a user