Initial commit to remove use of eval. gh-issue #151.

This commit is contained in:
Roberto Rosario
2015-07-22 23:19:43 -04:00
parent 4527563d89
commit 58d919d173
10 changed files with 606 additions and 176 deletions

View File

@@ -38,11 +38,15 @@ class APIMetadataTypeListView(generics.ListCreateAPIView):
mayan_view_permissions = {'POST': [permission_metadata_type_create]}
def get(self, *args, **kwargs):
"""Returns a list of all the metadata types."""
"""
Returns a list of all the metadata types.
"""
return super(APIMetadataTypeListView, self).get(*args, **kwargs)
def post(self, *args, **kwargs):
"""Create a new metadata type."""
"""
Create a new metadata type.
"""
return super(APIMetadataTypeListView, self).post(*args, **kwargs)
@@ -59,19 +63,27 @@ class APIMetadataTypeView(generics.RetrieveUpdateDestroyAPIView):
}
def delete(self, *args, **kwargs):
"""Delete the selected metadata type."""
"""
Delete the selected metadata type.
"""
return super(APIMetadataTypeView, self).delete(*args, **kwargs)
def get(self, *args, **kwargs):
"""Return the details of the selected metadata type."""
"""
Return the details of the selected metadata type.
"""
return super(APIMetadataTypeView, self).get(*args, **kwargs)
def patch(self, *args, **kwargs):
"""Edit the selected metadata type."""
"""
Edit the selected metadata type.
"""
return super(APIMetadataTypeView, self).patch(*args, **kwargs)
def put(self, *args, **kwargs):
"""Edit the selected metadata type."""
"""
Edit the selected metadata type.
"""
return super(APIMetadataTypeView, self).put(*args, **kwargs)
@@ -86,19 +98,31 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
document = self.get_document()
if self.request == 'GET':
# Make sure the use has the permission to see the metadata for this document
# Make sure the use has the permission to see the metadata for
# this document
try:
Permission.check_permissions(self.request.user, [permission_metadata_document_view])
Permission.check_permissions(
self.request.user, [permission_metadata_document_view]
)
except PermissionDenied:
AccessControlList.objects.check_access(permission_metadata_document_view, self.request.user, document)
AccessControlList.objects.check_access(
permission_metadata_document_view, self.request.user,
document
)
else:
return document.metadata.all()
elif self.request == 'POST':
# Make sure the use has the permission to add metadata to this document
# Make sure the use has the permission to add metadata to this
# document
try:
Permission.check_permissions(self.request.user, [permission_metadata_document_add])
Permission.check_permissions(
self.request.user, [permission_metadata_document_add]
)
except PermissionDenied:
AccessControlList.objects.check_access(permission_metadata_document_add, self.request.user, document)
AccessControlList.objects.check_access(
permission_metadata_document_add, self.request.user,
document
)
else:
return document.metadata.all()
@@ -106,11 +130,15 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
serializer.document = self.get_document()
def get(self, *args, **kwargs):
"""Returns a list of selected document's metadata types and values."""
"""
Returns a list of selected document's metadata types and values.
"""
return super(APIDocumentMetadataListView, self).get(*args, **kwargs)
def post(self, *args, **kwargs):
"""Add an existing metadata type and value to the selected document."""
"""
Add an existing metadata type and value to the selected document.
"""
return super(APIDocumentMetadataListView, self).post(*args, **kwargs)
@@ -127,29 +155,53 @@ class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView):
}
def delete(self, *args, **kwargs):
"""Delete the selected document metadata type and value."""
"""
Delete the selected document metadata type and value.
"""
try:
return super(APIDocumentMetadataView, self).delete(*args, **kwargs)
return super(
APIDocumentMetadataView, self
).delete(*args, **kwargs)
except Exception as exception:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'non_fields_errors': unicode(exception)})
return Response(
status=status.HTTP_400_BAD_REQUEST, data={
'non_fields_errors': unicode(exception)
}
)
def get(self, *args, **kwargs):
"""Return the details of the selected document metadata type and value."""
"""
Return the details of the selected document metadata type and value.
"""
return super(APIDocumentMetadataView, self).get(*args, **kwargs)
def patch(self, *args, **kwargs):
"""Edit the selected document metadata type and value."""
"""
Edit the selected document metadata type and value.
"""
try:
return super(APIDocumentMetadataView, self).patch(*args, **kwargs)
return super(
APIDocumentMetadataView, self
).patch(*args, **kwargs)
except Exception as exception:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'non_fields_errors': unicode(exception)})
return Response(
status=status.HTTP_400_BAD_REQUEST, data={
'non_fields_errors': unicode(exception)
}
)
def put(self, *args, **kwargs):
"""Edit the selected document metadata type and value."""
"""
Edit the selected document metadata type and value.
"""
try:
return super(APIDocumentMetadataView, self).put(*args, **kwargs)
except Exception as exception:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'non_fields_errors': unicode(exception)})
return Response(
status=status.HTTP_400_BAD_REQUEST, data={
'non_fields_errors': unicode(exception)
}
)
class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
@@ -160,17 +212,28 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
required_metadata = False
def get_queryset(self):
document_type = get_object_or_404(DocumentType, pk=self.kwargs['document_type_pk'])
document_type = get_object_or_404(
DocumentType, pk=self.kwargs['document_type_pk']
)
try:
Permission.check_permissions(self.request.user, [permission_document_type_view])
Permission.check_permissions(
self.request.user, [permission_document_type_view]
)
except PermissionDenied:
AccessControlList.objects.check_access(permission_document_type_view, self.request.user, document_type)
AccessControlList.objects.check_access(
permission_document_type_view, self.request.user,
document_type
)
return document_type.metadata.filter(required=self.required_metadata)
def get(self, *args, **kwargs):
"""Returns a list of selected document type's optional metadata types."""
return super(APIDocumentTypeMetadataTypeOptionalListView, self).get(*args, **kwargs)
"""
Returns a list of selected document type's optional metadata types.
"""
return super(
APIDocumentTypeMetadataTypeOptionalListView, self
).get(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
@@ -182,18 +245,29 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
"""
Add an optional metadata type to a document type.
"""
document_type = get_object_or_404(DocumentType, pk=self.kwargs['document_type_pk'])
document_type = get_object_or_404(
DocumentType, pk=self.kwargs['document_type_pk']
)
try:
Permission.check_permissions(self.request.user, [permission_document_type_edit])
Permission.check_permissions(
self.request.user, [permission_document_type_edit]
)
except PermissionDenied:
AccessControlList.objects.check_access(permission_document_type_edit, self.request.user, document_type)
AccessControlList.objects.check_access(
permission_document_type_edit, self.request.user,
document_type
)
serializer = self.get_serializer(data=self.request.POST)
if serializer.is_valid():
metadata_type = get_object_or_404(MetadataType, pk=serializer.data['metadata_type_pk'])
document_type.metadata_type.add(metadata_type, required=self.required_metadata)
metadata_type = get_object_or_404(
MetadataType, pk=serializer.data['metadata_type_pk']
)
document_type.metadata_type.add(
metadata_type, required=self.required_metadata
)
return Response(status=status.HTTP_201_CREATED)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
@@ -203,14 +277,21 @@ class APIDocumentTypeMetadataTypeRequiredListView(APIDocumentTypeMetadataTypeOpt
required_metadata = True
def get(self, *args, **kwargs):
"""Returns a list of the selected document type's required metadata types."""
return super(APIDocumentTypeMetadataTypeRequiredListView, self).get(*args, **kwargs)
"""
Returns a list of the selected document type's required metadata
types.
"""
return super(
APIDocumentTypeMetadataTypeRequiredListView, self
).get(*args, **kwargs)
def post(self, request, *args, **kwargs):
"""
Add a required metadata type to a document type.
"""
return super(APIDocumentTypeMetadataTypeRequiredListView, self).get(*args, **kwargs)
return super(
APIDocumentTypeMetadataTypeRequiredListView, self
).get(*args, **kwargs)
class APIDocumentTypeMetadataTypeRequiredView(views.APIView):
@@ -219,12 +300,21 @@ class APIDocumentTypeMetadataTypeRequiredView(views.APIView):
Remove a metadata type from a document type.
"""
document_type = get_object_or_404(DocumentType, pk=self.kwargs['document_type_pk'])
document_type = get_object_or_404(
DocumentType, pk=self.kwargs['document_type_pk']
)
try:
Permission.check_permissions(self.request.user, [permission_document_type_edit])
Permission.check_permissions(
self.request.user, [permission_document_type_edit]
)
except PermissionDenied:
AccessControlList.objects.check_access(permission_document_type_edit, self.request.user, document_type)
AccessControlList.objects.check_access(
permission_document_type_edit, self.request.user,
document_type
)
metadata_type = get_object_or_404(MetadataType, pk=self.kwargs['metadata_type_pk'])
metadata_type = get_object_or_404(
MetadataType, pk=self.kwargs['metadata_type_pk']
)
document_type.metadata_type.remove(metadata_type)
return Response(status=status.HTTP_204_NO_CONTENT)

View File

@@ -31,10 +31,12 @@ from .handlers import (
from .links import (
link_metadata_add, link_metadata_edit, link_metadata_multiple_add,
link_metadata_multiple_edit, link_metadata_multiple_remove,
link_metadata_remove, link_metadata_view, link_setup_document_type_metadata,
link_setup_document_type_metadata_required, link_setup_metadata_type_create,
link_setup_metadata_type_delete, link_setup_metadata_type_edit,
link_setup_metadata_type_list, link_documents_missing_required_metadata
link_metadata_remove, link_metadata_view,
link_setup_document_type_metadata,
link_setup_document_type_metadata_required,
link_setup_metadata_type_create, link_setup_metadata_type_delete,
link_setup_metadata_type_edit, link_setup_metadata_type_list,
link_documents_missing_required_metadata
)
from .models import DocumentTypeMetadataType, MetadataType
from .permissions import (
@@ -54,21 +56,47 @@ class MetadataApp(MayanAppConfig):
APIEndPoint('metadata')
Document.add_to_class('metadata_value_of', DocumentMetadataHelper.constructor)
Document.add_to_class(
'metadata_value_of', DocumentMetadataHelper.constructor
)
ModelAttribute(Document, 'metadata', type_name='related', description=_('Queryset containing a MetadataType instance reference and a value for that metadata type'))
ModelAttribute(Document, 'metadata__metadata_type__name', label=_('Metadata type name'), type_name='query')
ModelAttribute(Document, 'metadata__value', label=_('Metadata type value'), type_name='query')
ModelAttribute(Document, 'metadata_value_of', label=_('Value of a metadata'), description=_('Return the value of a specific document metadata'), type_name=['property', 'indexing'])
ModelAttribute(
Document, 'metadata', type_name='related',
description=_(
'Queryset containing a MetadataType instance reference and a value for that metadata type'
)
)
ModelAttribute(
Document, 'metadata__metadata_type__name',
label=_('Metadata type name'), type_name='query'
)
ModelAttribute(
Document, 'metadata__value', label=_('Metadata type value'),
type_name='query'
)
ModelAttribute(
Document, 'metadata_value_of', label=_('Value of a metadata'),
description=_(
'Return the value of a specific document metadata'
),
type_name=['property', 'indexing']
)
ModelPermission.register(
model=Document, permissions=(
permission_metadata_document_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view,
permission_metadata_document_add,
permission_metadata_document_edit,
permission_metadata_document_remove,
permission_metadata_document_view,
)
)
SourceColumn(source=Document, label=_('Metadata'), attribute=encapsulate(lambda document: get_metadata_string(document)))
SourceColumn(
source=Document, label=_('Metadata'),
attribute=encapsulate(
lambda document: get_metadata_string(document)
)
)
app.conf.CELERY_QUEUES.append(
Queue('metadata', Exchange('metadata'), routing_key='metadata'),
@@ -85,18 +113,66 @@ class MetadataApp(MayanAppConfig):
}
)
document_search.add_model_field(field='metadata__metadata_type__name', label=_('Metadata type'))
document_search.add_model_field(field='metadata__value', label=_('Metadata value'))
document_search.add_model_field(
field='metadata__metadata_type__name', label=_('Metadata type')
)
document_search.add_model_field(
field='metadata__value', label=_('Metadata value')
)
menu_facet.bind_links(links=[link_metadata_view], sources=[Document])
menu_multi_item.bind_links(links=[link_metadata_multiple_add, link_metadata_multiple_edit, link_metadata_multiple_remove], sources=[Document])
menu_object.bind_links(links=[link_setup_document_type_metadata, link_setup_document_type_metadata_required], sources=[DocumentType])
menu_object.bind_links(links=[link_setup_metadata_type_edit, link_setup_metadata_type_delete], sources=[MetadataType])
menu_secondary.bind_links(links=[link_setup_metadata_type_list, link_setup_metadata_type_create], sources=[MetadataType, 'metadata:setup_metadata_type_list', 'metadata:setup_metadata_type_create'])
menu_multi_item.bind_links(
links=[
link_metadata_multiple_add, link_metadata_multiple_edit,
link_metadata_multiple_remove
], sources=[Document]
)
menu_object.bind_links(
links=[
link_setup_document_type_metadata,
link_setup_document_type_metadata_required
], sources=[DocumentType]
)
menu_object.bind_links(
links=[
link_setup_metadata_type_edit,
link_setup_metadata_type_delete
], sources=[MetadataType]
)
menu_secondary.bind_links(
links=[
link_setup_metadata_type_list,
link_setup_metadata_type_create
], sources=[
MetadataType, 'metadata:setup_metadata_type_list',
'metadata:setup_metadata_type_create'
]
)
menu_setup.bind_links(links=[link_setup_metadata_type_list])
menu_sidebar.bind_links(links=[link_metadata_add, link_metadata_edit, link_metadata_remove], sources=['metadata:metadata_add', 'metadata:metadata_edit', 'metadata:metadata_remove', 'metadata:metadata_view'])
menu_tools.bind_links(links=[link_documents_missing_required_metadata])
menu_sidebar.bind_links(
links=[
link_metadata_add, link_metadata_edit, link_metadata_remove
], sources=[
'metadata:metadata_add', 'metadata:metadata_edit',
'metadata:metadata_remove', 'metadata:metadata_view'
]
)
menu_tools.bind_links(
links=[link_documents_missing_required_metadata]
)
post_delete.connect(post_document_type_metadata_type_delete, dispatch_uid='post_document_type_metadata_type_delete', sender=DocumentTypeMetadataType)
post_document_type_change.connect(post_post_document_type_change_metadata, dispatch_uid='post_post_document_type_change_metadata', sender=Document)
post_save.connect(post_document_type_metadata_type_add, dispatch_uid='post_document_type_metadata_type_add', sender=DocumentTypeMetadataType)
post_delete.connect(
post_document_type_metadata_type_delete,
dispatch_uid='post_document_type_metadata_type_delete',
sender=DocumentTypeMetadataType
)
post_document_type_change.connect(
post_post_document_type_change_metadata,
dispatch_uid='post_post_document_type_change_metadata',
sender=Document
)
post_save.connect(
post_document_type_metadata_type_add,
dispatch_uid='post_document_type_metadata_type_add',
sender=DocumentTypeMetadataType
)

View File

@@ -3,11 +3,11 @@ from __future__ import unicode_literals
from django import forms
from django.core.exceptions import ValidationError
from django.forms.formsets import formset_factory
from django.template import Context, Template
from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
from .models import MetadataType
from .settings import setting_available_functions, setting_available_models
class MetadataForm(forms.Form):
@@ -35,14 +35,19 @@ class MetadataForm(forms.Form):
except AttributeError:
message = unicode(exception)
raise ValidationError(_('Invalid value: %(message)s'), params={'message': message}, code='invalid')
raise ValidationError(
_('Invalid value: %(message)s'), params={
'message': message
}, code='invalid'
)
else:
# Return the result if it was a parsing function
# If it was a validation function and passed correctly we return
# the original input value
# If it was a validation function and passed correctly
# we return the original input value
return result or self.cleaned_data['value']
else:
# If a validator was never specified we return the original value
# If a validator was never specified we return the original
# value
return self.cleaned_data['value']
def __init__(self, *args, **kwargs):
@@ -60,12 +65,16 @@ class MetadataForm(forms.Form):
else:
self.fields['value'].required = False
self.fields['name'].initial = '%s%s' % ((self.metadata_type.label if self.metadata_type.label else self.metadata_type.name), required_string)
self.fields['name'].initial = '%s%s' % (
(self.metadata_type.label if self.metadata_type.label else self.metadata_type.name),
required_string
)
self.fields['id'].initial = self.metadata_type.pk
if self.metadata_type.lookup:
try:
choices = eval(self.metadata_type.lookup, setting_available_models.value)
#choices = eval(self.metadata_type.lookup, setting_available_models.value) #####
choices = []
self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label)
choices = zip(choices, choices)
if not required:
@@ -74,25 +83,39 @@ class MetadataForm(forms.Form):
self.fields['value'].required = required
except Exception as exception:
self.fields['value'].initial = exception
self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'})
self.fields['value'].widget = forms.TextInput(
attrs={'readonly': 'readonly'}
)
if self.metadata_type.default:
try:
self.fields['value'].initial = eval(self.metadata_type.default, setting_available_functions.value)
template = Template(self.metadata_type.default)
context = Context()
result = template.render(context=context)
self.fields['value'].initial = result
except Exception as exception:
self.fields['value'].initial = exception
self.fields['value'].initial = _(
'Error: %s'
) % exception
id = forms.CharField(label=_('ID'), widget=forms.HiddenInput)
name = forms.CharField(label=_('Name'), required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'}))
name = forms.CharField(
label=_('Name'), required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'})
)
value = forms.CharField(label=_('Value'), required=False)
update = forms.BooleanField(initial=True, label=_('Update'), required=False)
update = forms.BooleanField(
initial=True, label=_('Update'), required=False
)
MetadataFormSet = formset_factory(MetadataForm, extra=0)
class AddMetadataForm(forms.Form):
metadata_type = forms.ModelChoiceField(queryset=MetadataType.objects.all(), label=_('Metadata type'))
metadata_type = forms.ModelChoiceField(
queryset=MetadataType.objects.all(), label=_('Metadata type')
)
def __init__(self, *args, **kwargs):
document_type = kwargs.pop('document_type')
@@ -101,7 +124,9 @@ class AddMetadataForm(forms.Form):
class MetadataRemoveForm(MetadataForm):
update = forms.BooleanField(initial=False, label=_('Remove'), required=False)
update = forms.BooleanField(
initial=False, label=_('Remove'), required=False
)
def __init__(self, *args, **kwargs):
super(MetadataRemoveForm, self).__init__(*args, **kwargs)

View File

@@ -12,12 +12,22 @@ def post_document_type_metadata_type_add(sender, instance, created, **kwargs):
logger.debug('instance: %s', instance)
if created and instance.required:
task_add_required_metadata_type.apply_async(kwargs={'document_type_id': instance.document_type.pk, 'metadata_type_id': instance.metadata_type.pk})
task_add_required_metadata_type.apply_async(
kwargs={
'document_type_id': instance.document_type.pk,
'metadata_type_id': instance.metadata_type.pk
}
)
def post_document_type_metadata_type_delete(sender, instance, **kwargs):
logger.debug('instance: %s', instance)
task_remove_metadata_type.apply_async(kwargs={'document_type_id': instance.document_type.pk, 'metadata_type_id': instance.metadata_type.pk})
task_remove_metadata_type.apply_async(
kwargs={
'document_type_id': instance.document_type.pk,
'metadata_type_id': instance.metadata_type.pk
}
)
def post_post_document_type_change_metadata(sender, instance, **kwargs):
@@ -29,4 +39,8 @@ def post_post_document_type_change_metadata(sender, instance, **kwargs):
# Add new document type metadata types to document
for document_type_metadata_type in instance.document_type.metadata.filter(required=True):
DocumentMetadata.objects.create(document=instance, metadata_type=document_type_metadata_type.metadata_type, value=None)
DocumentMetadata.objects.create(
document=instance,
metadata_type=document_type_metadata_type.metadata_type,
value=None
)

View File

@@ -12,7 +12,10 @@ from .settings import setting_available_validators
def validation_choices():
return zip(setting_available_validators.value, setting_available_validators.value)
return zip(
setting_available_validators.value,
setting_available_validators.value
)
@python_2_unicode_compatible
@@ -20,13 +23,28 @@ class MetadataType(models.Model):
"""
Define a type of metadata
"""
name = models.CharField(max_length=48, help_text=_('Do not use python reserved words, or spaces.'), unique=True, verbose_name=_('Name'))
name = models.CharField(
max_length=48,
help_text=_('Name used by other apps to reference this value. Do not use python reserved words, or spaces.'),
unique=True, verbose_name=_('Name')
)
label = models.CharField(max_length=48, verbose_name=_('Label'))
default = models.CharField(blank=True, max_length=128, null=True, help_text=_('Enter a string to be evaluated.'), verbose_name=_('Default'))
default = models.CharField(
blank=True, max_length=128, null=True,
help_text=_('Enter a template to render. Use Django\'s default templating language (https://docs.djangoproject.com/en/1.7/ref/templates/builtins/)'),
verbose_name=_('Default')
)
# TODO: Add enable_lookup boolean to allow users to switch the lookup on and
# off without losing the lookup expression
lookup = models.TextField(blank=True, null=True, help_text=_('Enter a string to be evaluated that returns an iterable.'), verbose_name=_('Lookup'))
validation = models.CharField(blank=True, choices=validation_choices(), max_length=64, verbose_name=_('Validation function name'))
lookup = models.TextField(
blank=True, null=True,
help_text=_('Enter a string to be evaluated that returns an iterable.'),
verbose_name=_('Lookup')
)
validation = models.CharField(
blank=True, choices=validation_choices(), max_length=64,
verbose_name=_('Validation function name')
)
# TODO: Find a different way to let users know what models and functions are
# available now that we removed these from the help_text
objects = MetadataTypeManager()
@@ -49,22 +67,31 @@ class DocumentMetadata(models.Model):
Link a document to a specific instance of a metadata type with it's
current value
"""
document = models.ForeignKey(Document, related_name='metadata', verbose_name=_('Document'))
document = models.ForeignKey(
Document, related_name='metadata', verbose_name=_('Document')
)
metadata_type = models.ForeignKey(MetadataType, verbose_name=_('Type'))
value = models.CharField(blank=True, db_index=True, max_length=255, null=True, verbose_name=_('Value'))
value = models.CharField(
blank=True, db_index=True, max_length=255, null=True,
verbose_name=_('Value')
)
def __str__(self):
return unicode(self.metadata_type)
def save(self, *args, **kwargs):
if self.metadata_type.pk not in self.document.document_type.metadata.values_list('metadata_type', flat=True):
raise ValidationError(_('Metadata type is not valid for this document type.'))
raise ValidationError(
_('Metadata type is not valid for this document type.')
)
return super(DocumentMetadata, self).save(*args, **kwargs)
def delete(self, enforce_required=True, *args, **kwargs):
if enforce_required and self.metadata_type.pk in self.document.document_type.metadata.filter(required=True).values_list('metadata_type', flat=True):
raise ValidationError(_('Metadata type is required for this document type.'))
raise ValidationError(
_('Metadata type is required for this document type.')
)
return super(DocumentMetadata, self).delete(*args, **kwargs)
@@ -76,8 +103,13 @@ class DocumentMetadata(models.Model):
@python_2_unicode_compatible
class DocumentTypeMetadataType(models.Model):
document_type = models.ForeignKey(DocumentType, related_name='metadata', verbose_name=_('Document type'))
metadata_type = models.ForeignKey(MetadataType, verbose_name=_('Metadata type'))
document_type = models.ForeignKey(
DocumentType, related_name='metadata',
verbose_name=_('Document type')
)
metadata_type = models.ForeignKey(
MetadataType, verbose_name=_('Metadata type')
)
required = models.BooleanField(default=False, verbose_name=_('Required'))
def __str__(self):

View File

@@ -28,9 +28,16 @@ class DocumentTypeMetadataTypeSerializer(serializers.ModelSerializer):
class DocumentNewMetadataSerializer(serializers.Serializer):
metadata_type = serializers.IntegerField(help_text=_('Primary key of the metadata type to be added.'))
value = serializers.CharField(max_length=255, help_text=_('Value of the corresponding metadata type instance.'))
metadata_type = serializers.IntegerField(
help_text=_('Primary key of the metadata type to be added.')
)
value = serializers.CharField(
max_length=255,
help_text=_('Value of the corresponding metadata type instance.')
)
class DocumentTypeNewMetadataTypeSerializer(serializers.Serializer):
metadata_type = serializers.IntegerField(help_text=_('Primary key of the metadata type to be added.'))
metadata_type = serializers.IntegerField(
help_text=_('Primary key of the metadata type to be added.')
)

View File

@@ -1,22 +1,13 @@
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from smart_settings import Namespace
from .validators import MetadataValidator
default_available_functions = {
'current_date': now().date,
}
default_available_models = {
'User': User
}
namespace = Namespace(name='metadata', label=_('Metadata'))
setting_available_validators = namespace.add_setting(global_name='METADATA_AVAILABLE_VALIDATORS', default=MetadataValidator.get_import_paths())
setting_available_functions = namespace.add_setting(global_name='METADATA_AVAILABLE_FUNCTIONS', default=default_available_functions)
setting_available_models = namespace.add_setting(global_name='METADATA_AVAILABLE_MODELS', default=default_available_models)
setting_available_validators = namespace.add_setting(
global_name='METADATA_AVAILABLE_VALIDATORS',
default=MetadataValidator.get_import_paths()
)

View File

@@ -11,7 +11,10 @@ logger = logging.getLogger(__name__)
@app.task(ignore_result=True)
def task_remove_metadata_type(document_type_id, metadata_type_id):
DocumentMetadata.objects.filter(document__document_type__id=document_type_id, metadata_type__id=metadata_type_id).delete()
DocumentMetadata.objects.filter(
document__document_type__id=document_type_id,
metadata_type__id=metadata_type_id
).delete()
@app.task(ignore_result=True)

View File

@@ -12,37 +12,101 @@ from .api_views import (
from .views import (
MetadataTypeCreateView, MetadataTypeDeleteView, MetadataTypeEditView,
MetadataTypeListView, MissingRequiredMetadataDocumentListView,
SetupDocumentTypeMetadataOptionalView, SetupDocumentTypeMetadataRequiredView
SetupDocumentTypeMetadataOptionalView,
SetupDocumentTypeMetadataRequiredView
)
urlpatterns = patterns(
'metadata.views',
url(r'^(?P<pk>\d+)/edit/$', 'metadata_edit', name='metadata_edit'),
url(r'^(?P<document_id>\d+)/view/$', 'metadata_view', name='metadata_view'),
url(r'^multiple/edit/$', 'metadata_multiple_edit', name='metadata_multiple_edit'),
url(
r'^(?P<document_id>\d+)/view/$', 'metadata_view',
name='metadata_view'
),
url(
r'^multiple/edit/$', 'metadata_multiple_edit',
name='metadata_multiple_edit'
),
url(r'^(?P<document_id>\d+)/add/$', 'metadata_add', name='metadata_add'),
url(r'^multiple/add/$', 'metadata_multiple_add', name='metadata_multiple_add'),
url(r'^(?P<document_id>\d+)/remove/$', 'metadata_remove', name='metadata_remove'),
url(r'^multiple/remove/$', 'metadata_multiple_remove', name='metadata_multiple_remove'),
url(
r'^multiple/add/$', 'metadata_multiple_add',
name='metadata_multiple_add'
),
url(
r'^(?P<document_id>\d+)/remove/$', 'metadata_remove',
name='metadata_remove'
),
url(
r'^multiple/remove/$', 'metadata_multiple_remove',
name='metadata_multiple_remove'
),
url(r'^setup/type/list/$', MetadataTypeListView.as_view(), name='setup_metadata_type_list'),
url(r'^setup/type/create/$', MetadataTypeCreateView.as_view(), name='setup_metadata_type_create'),
url(r'^setup/type/(?P<pk>\d+)/edit/$', MetadataTypeEditView.as_view(), name='setup_metadata_type_edit'),
url(r'^setup/type/(?P<pk>\d+)/delete/$', MetadataTypeDeleteView.as_view(), name='setup_metadata_type_delete'),
url(
r'^setup/type/list/$', MetadataTypeListView.as_view(),
name='setup_metadata_type_list'
),
url(
r'^setup/type/create/$', MetadataTypeCreateView.as_view(),
name='setup_metadata_type_create'
),
url(
r'^setup/type/(?P<pk>\d+)/edit/$', MetadataTypeEditView.as_view(),
name='setup_metadata_type_edit'
),
url(
r'^setup/type/(?P<pk>\d+)/delete/$',
MetadataTypeDeleteView.as_view(), name='setup_metadata_type_delete'
),
url(r'^setup/document/type/(?P<pk>\d+)/metadata/edit/$', SetupDocumentTypeMetadataOptionalView.as_view(), name='setup_document_type_metadata'),
url(r'^setup/document/type/(?P<pk>\d+)/metadata/edit/required/$', SetupDocumentTypeMetadataRequiredView.as_view(), name='setup_document_type_metadata_required'),
url(
r'^setup/document/type/(?P<pk>\d+)/metadata/edit/$',
SetupDocumentTypeMetadataOptionalView.as_view(),
name='setup_document_type_metadata'
),
url(
r'^setup/document/type/(?P<pk>\d+)/metadata/edit/required/$',
SetupDocumentTypeMetadataRequiredView.as_view(),
name='setup_document_type_metadata_required'
),
url(r'^tools/missing_required_metadata/$', MissingRequiredMetadataDocumentListView.as_view(), name='documents_missing_required_metadata'),
url(
r'^tools/missing_required_metadata/$',
MissingRequiredMetadataDocumentListView.as_view(),
name='documents_missing_required_metadata'
),
)
api_urls = patterns(
'',
url(r'^metadatatypes/$', APIMetadataTypeListView.as_view(), name='metadatatype-list'),
url(r'^metadatatypes/(?P<pk>[0-9]+)/$', APIMetadataTypeView.as_view(), name='metadatatype-detail'),
url(r'^document/metadata/(?P<pk>[0-9]+)/$', APIDocumentMetadataView.as_view(), name='documentmetadata-detail'),
url(r'^document/(?P<document_pk>[0-9]+)/metadata/$', APIDocumentMetadataListView.as_view(), name='documentmetadata-list'),
url(r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/optional/$', APIDocumentTypeMetadataTypeOptionalListView.as_view(), name='documenttypemetadatatype-list'),
url(r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/required/$', APIDocumentTypeMetadataTypeRequiredListView.as_view(), name='documenttypemetadatatype-list'),
url(r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/(?P<metadata_type_pk>[0-9]+)/$', APIDocumentTypeMetadataTypeRequiredView.as_view(), name='documenttypemetadatatype-detail'),
url(
r'^metadatatypes/$', APIMetadataTypeListView.as_view(),
name='metadatatype-list'
),
url(
r'^metadatatypes/(?P<pk>[0-9]+)/$', APIMetadataTypeView.as_view(),
name='metadatatype-detail'
),
url(
r'^document/metadata/(?P<pk>[0-9]+)/$',
APIDocumentMetadataView.as_view(), name='documentmetadata-detail'
),
url(
r'^document/(?P<document_pk>[0-9]+)/metadata/$',
APIDocumentMetadataListView.as_view(), name='documentmetadata-list'
),
url(
r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/optional/$',
APIDocumentTypeMetadataTypeOptionalListView.as_view(),
name='documenttypemetadatatype-list'
),
url(
r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/required/$',
APIDocumentTypeMetadataTypeRequiredListView.as_view(),
name='documenttypemetadatatype-list'
),
url(
r'^document_type/(?P<document_type_pk>[0-9]+)/metadatatypes/(?P<metadata_type_pk>[0-9]+)/$',
APIDocumentTypeMetadataTypeRequiredView.as_view(),
name='documenttypemetadatatype-detail'
),
)

View File

@@ -41,30 +41,47 @@ class MissingRequiredMetadataDocumentListView(DocumentListView):
}
def get_document_queryset(self):
return Document.objects.filter(document_type__metadata__required=True, metadata__value__isnull=True)
return Document.objects.filter(
document_type__metadata__required=True,
metadata__value__isnull=True
)
def metadata_edit(request, document_id=None, document_id_list=None):
if document_id:
document_id_list = unicode(document_id)
documents = Document.objects.select_related('metadata').filter(pk__in=document_id_list.split(','))
documents = Document.objects.select_related('metadata').filter(
pk__in=document_id_list.split(',')
)
try:
Permission.check_permissions(request.user, [permission_metadata_document_edit])
Permission.check_permissions(
request.user, [permission_metadata_document_edit]
)
except PermissionDenied:
documents = AccessControlList.objects.filter_by_access(permission_metadata_document_edit, request.user, documents)
documents = AccessControlList.objects.filter_by_access(
permission_metadata_document_edit, request.user, documents
)
if not documents:
if document_id:
raise Http404
else:
messages.error(request, _('Must provide at least one document.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
if len(set([document.document_type.pk for document in documents])) > 1:
messages.error(request, _('Only select documents of the same type.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
if set(documents.values_list('metadata__value', flat=True)) == set([None]):
message = ungettext(
@@ -73,11 +90,19 @@ def metadata_edit(request, document_id=None, document_id_list=None):
len(documents)
)
messages.warning(request, message)
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
post_action_redirect = reverse('documents:document_list_recent')
next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect)))
next = request.POST.get(
'next', request.GET.get(
'next', request.META.get('HTTP_REFERER', post_action_redirect)
)
)
metadata = {}
initial = []
@@ -97,7 +122,9 @@ def metadata_edit(request, document_id=None, document_id_list=None):
initial.append({
'metadata_type': key,
'value': ', '.join(value) if value else '',
'required': key in document.document_type.metadata.filter(required=True),
'required': key in document.document_type.metadata.filter(
required=True
),
})
formset = MetadataFormSet(initial=initial)
@@ -119,10 +146,19 @@ def metadata_edit(request, document_id=None, document_id_list=None):
if settings.DEBUG:
raise
else:
messages.error(request, _('Error editing metadata for document %(document)s; %(exception)s.') % {
'document': document, 'exception': ', '.join(exception.messages)})
messages.error(
request, _(
'Error editing metadata for document %(document)s; %(exception)s.'
) % {
'document': document,
'exception': ', '.join(exception.messages)
}
)
else:
messages.success(request, _('Metadata for document %s edited successfully.') % document)
messages.success(
request,
_('Metadata for document %s edited successfully.') % document
)
return HttpResponseRedirect(next)
@@ -146,54 +182,102 @@ def metadata_edit(request, document_id=None, document_id_list=None):
def metadata_multiple_edit(request):
return metadata_edit(request, document_id_list=request.GET.get('id_list', ''))
return metadata_edit(
request, document_id_list=request.GET.get('id_list', '')
)
def metadata_add(request, document_id=None, document_id_list=None):
if document_id:
documents = [get_object_or_404(Document, pk=document_id)]
elif document_id_list:
documents = [get_object_or_404(Document.objects.select_related('document_type'), pk=document_id) for document_id in document_id_list.split(',')]
documents = [
get_object_or_404(Document.objects.select_related('document_type'), pk=document_id) for document_id in document_id_list.split(',')
]
if len(set([document.document_type.pk for document in documents])) > 1:
messages.error(request, _('Only select documents of the same type.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
messages.error(
request, _('Only select documents of the same type.')
)
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
try:
Permission.check_permissions(request.user, [permission_metadata_document_add])
Permission.check_permissions(
request.user, [permission_metadata_document_add]
)
except PermissionDenied:
documents = AccessControlList.objects.filter_by_access(permission_metadata_document_add, request.user, documents)
documents = AccessControlList.objects.filter_by_access(
permission_metadata_document_add, request.user, documents
)
if not documents:
messages.error(request, _('Must provide at least one document.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
for document in documents:
document.add_as_recent_document_for_user(request.user)
post_action_redirect = reverse('documents:document_list_recent')
next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect)))
next = request.POST.get(
'next',
request.GET.get(
'next', request.META.get('HTTP_REFERER', post_action_redirect)
)
)
if request.method == 'POST':
form = AddMetadataForm(data=request.POST, document_type=document.document_type)
form = AddMetadataForm(
data=request.POST, document_type=document.document_type
)
if form.is_valid():
metadata_type = form.cleaned_data['metadata_type']
for document in documents:
try:
document_metadata, created = DocumentMetadata.objects.get_or_create(document=document, metadata_type=metadata_type.metadata_type, defaults={'value': ''})
document_metadata, created = DocumentMetadata.objects.get_or_create(
document=document,
metadata_type=metadata_type.metadata_type,
defaults={'value': ''}
)
except Exception as exception:
if getattr(settings, 'DEBUG', False):
raise
else:
messages.error(request, _('Error adding metadata type "%(metadata_type)s" to document: %(document)s; %(exception)s') % {
'metadata_type': metadata_type, 'document': document, 'exception': ', '.join(getattr(exception, 'messages', exception))})
messages.error(
request,
_(
'Error adding metadata type "%(metadata_type)s" to document: %(document)s; %(exception)s'
) % {
'metadata_type': metadata_type,
'document': document,
'exception': ', '.join(getattr(exception, 'messages', exception))
}
)
else:
if created:
messages.success(request, _('Metadata type: %(metadata_type)s successfully added to document %(document)s.') % {
'metadata_type': metadata_type, 'document': document})
messages.success(
request,
_(
'Metadata type: %(metadata_type)s successfully added to document %(document)s.'
) % {
'metadata_type': metadata_type, 'document': document
}
)
else:
messages.warning(request, _('Metadata type: %(metadata_type)s already present in document %(document)s.') % {
'metadata_type': metadata_type, 'document': document})
messages.warning(
request, _(
'Metadata type: %(metadata_type)s already present in document %(document)s.'
) % {
'metadata_type': metadata_type, 'document': document
}
)
if len(documents) == 1:
return HttpResponseRedirect('%s?%s' % (
@@ -228,30 +312,46 @@ def metadata_add(request, document_id=None, document_id_list=None):
def metadata_multiple_add(request):
return metadata_add(request, document_id_list=request.GET.get('id_list', []))
return metadata_add(
request, document_id_list=request.GET.get('id_list', [])
)
def metadata_remove(request, document_id=None, document_id_list=None):
if document_id:
document_id_list = unicode(document_id)
documents = Document.objects.select_related('metadata').filter(pk__in=document_id_list.split(','))
documents = Document.objects.select_related('metadata').filter(
pk__in=document_id_list.split(',')
)
try:
Permission.check_permissions(request.user, [permission_metadata_document_remove])
Permission.check_permissions(
request.user, [permission_metadata_document_remove]
)
except PermissionDenied:
documents = AccessControlList.objects.filter_by_access(permission_metadata_document_remove, request.user, documents)
documents = AccessControlList.objects.filter_by_access(
permission_metadata_document_remove, request.user, documents
)
if not documents:
if document_id:
raise Http404
else:
messages.error(request, _('Must provide at least one document.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
if len(set([document.document_type.pk for document in documents])) > 1:
messages.error(request, _('Only select documents of the same type.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
if set(documents.values_list('metadata__value', flat=True)) == set([None]):
message = ungettext(
@@ -260,7 +360,11 @@ def metadata_remove(request, document_id=None, document_id_list=None):
len(documents)
)
messages.warning(request, message)
return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))
return HttpResponseRedirect(
request.META.get(
'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)
)
)
post_action_redirect = reverse('documents:document_list_recent')
@@ -413,20 +517,36 @@ class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
return get_object_or_404(DocumentType, pk=self.kwargs['pk'])
def left_list(self):
return AssignRemoveView.generate_choices(set(MetadataType.objects.all()) - set(MetadataType.objects.filter(id__in=self.get_object().metadata.values_list('metadata_type', flat=True))))
return AssignRemoveView.generate_choices(
set(MetadataType.objects.all()) - set(
MetadataType.objects.filter(
id__in=self.get_object().metadata.values_list(
'metadata_type', flat=True
)
)
)
)
def right_list(self):
return AssignRemoveView.generate_choices(self.get_object().metadata.filter(required=False))
return AssignRemoveView.generate_choices(
self.get_object().metadata.filter(required=False)
)
def remove(self, item):
item.delete()
def get_context_data(self, **kwargs):
data = super(SetupDocumentTypeMetadataOptionalView, self).get_context_data(**kwargs)
data.update({
'object': self.get_object(),
'title': _('Optional metadata types for document type: %s') % self.get_object(),
})
data = super(
SetupDocumentTypeMetadataOptionalView, self
).get_context_data(**kwargs)
data.update(
{
'object': self.get_object(),
'title': _(
'Optional metadata types for document type: %s'
) % self.get_object(),
}
)
return data
@@ -436,13 +556,21 @@ class SetupDocumentTypeMetadataRequiredView(SetupDocumentTypeMetadataOptionalVie
self.get_object().metadata.create(metadata_type=item, required=True)
def right_list(self):
return AssignRemoveView.generate_choices(self.get_object().metadata.filter(required=True))
return AssignRemoveView.generate_choices(
self.get_object().metadata.filter(required=True)
)
def get_context_data(self, **kwargs):
data = super(SetupDocumentTypeMetadataRequiredView, self).get_context_data(**kwargs)
data.update({
'object': self.get_object(),
'title': _('Required metadata types for document type: %s') % self.get_object(),
})
data = super(
SetupDocumentTypeMetadataRequiredView, self
).get_context_data(**kwargs)
data.update(
{
'object': self.get_object(),
'title': _(
'Required metadata types for document type: %s'
) % self.get_object(),
}
)
return data