From 0a41cb62f634e1c7f4c66cc4975bb6a9295bbfda Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 29 Sep 2015 12:36:45 +0200 Subject: [PATCH] add validation of documents User can validate some document as of being of a certain type, and attach metadata to this validation event. --- fargo/fargo/admin.py | 47 ++++- fargo/fargo/forms.py | 67 +++++-- fargo/fargo/managers.py | 24 +++ fargo/fargo/migrations/0003_auto_20150924_1056.py | 97 ++++++++++ fargo/fargo/models.py | 70 ++++++-- fargo/fargo/tables.py | 13 +- fargo/fargo/utils.py | 20 +++ fargo/fargo/views.py | 209 +++++++++++++++++----- fargo/settings.py | 16 ++ fargo/templates/fargo/base_iframe.html | 13 ++ fargo/templates/fargo/pick.html | 27 ++- fargo/templates/fargo/table.html | 1 + fargo/templates/fargo/validation.html | 22 +++ fargo/urls.py | 27 +-- setup.py | 1 + 15 files changed, 562 insertions(+), 92 deletions(-) create mode 100644 fargo/fargo/managers.py create mode 100644 fargo/fargo/migrations/0003_auto_20150924_1056.py create mode 100644 fargo/fargo/utils.py create mode 100644 fargo/templates/fargo/base_iframe.html create mode 100644 fargo/templates/fargo/validation.html diff --git a/fargo/fargo/admin.py b/fargo/fargo/admin.py index 21bc4dc..b23a000 100644 --- a/fargo/fargo/admin.py +++ b/fargo/fargo/admin.py @@ -1,9 +1,52 @@ from django.contrib import admin +from django.contrib.auth import get_user_model from . import models + +class ValidationMixin(object): + fields = [ 'user', + 'filename', + 'size', + 'document_type', + 'start', + 'end', + 'data' ] + readonly_fields = ['user', 'filename', 'size'] + + def user(self, validation): + user = validation.user_document.user + return user.get_full_name().strip() or user.email or user.username + + def filename(self, validation): + return validation.user_document.filename + + def size(self, validation): + return validation.user_document.document.content.size + + +class ValidationInline(ValidationMixin, admin.TabularInline): + model = models.Validation + + +class UserDocumentAdmin(admin.ModelAdmin): + list_display = ['user', 'filename', 'created'] + readonly_fields = ['created'] + inlines = [ValidationInline] + + class DocumentAdmin(admin.ModelAdmin): - list_display = ['user', 'document_filename', 'document_file', 'creation'] - readonly_fields = ['creation'] + list_display = ['content_hash', 'users'] + + def users(self, instance): + User = get_user_model() + qs = User.objects.filter(user_document_set__document=instance) + return u', '.join(unicode(u) for u in qs) + + +class ValidationAdmin(ValidationMixin, admin.ModelAdmin): + list_display = ['user', 'filename', 'size', 'document_type', 'start', 'end'] +admin.site.register(models.UserDocument, UserDocumentAdmin) admin.site.register(models.Document, DocumentAdmin) +admin.site.register(models.Validation, ValidationAdmin) diff --git a/fargo/fargo/forms.py b/fargo/fargo/forms.py index 0ed5d0d..0011b9c 100644 --- a/fargo/fargo/forms.py +++ b/fargo/fargo/forms.py @@ -1,17 +1,64 @@ -from django.forms import ModelForm +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings -from . import models -class UploadForm(ModelForm): - def __init__(self, *args, **kwargs): - self.user = kwargs.pop('user') - super(UploadForm, self).__init__(*args, **kwargs) +from . import models, utils + + +class UploadForm(forms.ModelForm): + content = forms.FileField( + label=_('file'), max_length=512) def save(self, *args, **kwargs): - self.instance.user = self.user - self.instance.document_filename = self.files['document_file'].name + self.instance.filename = self.files['content'].name[:512] + self.instance.document = models.Document.objects.get_by_file( + self.files['content']) return super(UploadForm, self).save(*args, **kwargs) class Meta: - model = models.Document - fields = ['document_file'] + model = models.UserDocument + fields = [] + + +class ValidationForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + self.creator = kwargs.pop('user').get_full_name() + self.user_document = kwargs.pop('user_document') + self.document_type = kwargs.pop('document_type') + self.schema = utils.get_document_type_schema(settings, self.document_type) + super(ValidationForm, self).__init__(*args, **kwargs) + for fields in self.schema['metadata']: + name = fields['name'] + label = fields['label'] + required = fields.get('required', True) + self.fields[name] = forms.CharField(required=required, label=label) + + def save(self, commit=True): + assert commit, 'not committing is unsupported' + data = {} + for fields in self.schema['metadata']: + name = fields['name'] + value = self.cleaned_data.get(name) + if value: + data[fields['name']] = value + validation, created = models.Validation.objects.get_or_create( + user_document=self.user_document, + document_type=self.document_type, + defaults={ + 'data': data, + 'creator': self.creator, + 'start': self.cleaned_data['start'], + 'end': self.cleaned_data['end'], + }) + if not created: + validation.data = data + validation.creator = self.creator + validation.start = self.cleaned_data['start'] + validation.end = self.cleaned_data['end'] + validation.save() + return validation + + class Meta: + model = models.Validation + fields = ['start', 'end'] diff --git a/fargo/fargo/managers.py b/fargo/fargo/managers.py new file mode 100644 index 0000000..e25bc94 --- /dev/null +++ b/fargo/fargo/managers.py @@ -0,0 +1,24 @@ +from django.db import models + +from . import utils + + +class DocumentManager(models.Manager): + def clean(self): + '''Remove all documents not linked to an user''' + qs = self.filter(user_documents__isnull=True) + for document in qs: + qs.content.delete(False) + qs.delete() + + def get_by_file(self, f): + '''Get document with the same SHA-256 hash as the passde Django file + like object. + ''' + file_hash = utils.sha256_of_file(f) + try: + o = self.get(content_hash=file_hash) + except self.model.DoesNotExist: + o = self.model() + o.content.save(file_hash, f) + return o diff --git a/fargo/fargo/migrations/0003_auto_20150924_1056.py b/fargo/fargo/migrations/0003_auto_20150924_1056.py new file mode 100644 index 0000000..e566aac --- /dev/null +++ b/fargo/fargo/migrations/0003_auto_20150924_1056.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import jsonfield.fields +from django.utils.timezone import utc +import datetime +from django.conf import settings + + +def clear_document(apps, schema_editor): + Document = apps.get_model('fargo', 'Document') + Document.objects.all().delete() + + +def noop(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('fargo', '0002_auto_20150818_2117'), + ] + + operations = [ + migrations.RunPython(clear_document, noop), + migrations.CreateModel( + name='UserDocument', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('filename', models.CharField(max_length=512, verbose_name='filename')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), + ], + options={ + 'ordering': ('-created', 'user'), + 'verbose_name': 'user document', + 'verbose_name_plural': 'user documents', + }, + ), + migrations.CreateModel( + name='Validation', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('document_type', models.CharField(max_length=256, verbose_name='document type')), + ('data', jsonfield.fields.JSONField(null=True, verbose_name='data')), + ('start', models.DateField(verbose_name='start date')), + ('end', models.DateField(verbose_name='end date')), + ('creator', models.CharField(max_length=256, verbose_name='creator')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), + ('user_document', models.ForeignKey(verbose_name='user document', to='fargo.UserDocument')), + ], + ), + migrations.AlterModelOptions( + name='document', + options={'ordering': ('content_hash',), 'verbose_name': 'document', 'verbose_name_plural': 'documents'}, + ), + migrations.RenameField( + model_name='document', + old_name='document_file', + new_name='content', + ), + migrations.RemoveField( + model_name='document', + name='creation', + ), + migrations.RemoveField( + model_name='document', + name='document_filename', + ), + migrations.RemoveField( + model_name='document', + name='id', + ), + migrations.RemoveField( + model_name='document', + name='user', + ), + migrations.AddField( + model_name='document', + name='content_hash', + field=models.CharField(default=datetime.datetime(2015, 9, 24, 10, 56, 54, 873399, tzinfo=utc), max_length=128, serialize=False, verbose_name='content hash', primary_key=True), + preserve_default=False, + ), + migrations.AddField( + model_name='userdocument', + name='document', + field=models.ForeignKey(related_name='user_documents', verbose_name='document', to='fargo.Document'), + ), + migrations.AddField( + model_name='userdocument', + name='user', + field=models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL), + ), + migrations.RunPython(noop, clear_document), + ] diff --git a/fargo/fargo/models.py b/fargo/fargo/models.py index a8302ad..6122738 100644 --- a/fargo/fargo/models.py +++ b/fargo/fargo/models.py @@ -2,18 +2,64 @@ from django.conf import settings from django.db import models from django.utils.translation import ugettext_lazy as _ -class Document(models.Model): +from jsonfield import JSONField + +from . import utils, managers + + +class UserDocument(models.Model): + '''Document uploaded by an user or an agent''' user = models.ForeignKey( - settings.AUTH_USER_MODEL, - verbose_name=_('user')) - document_filename = models.CharField( - verbose_name=_('document filename'), - max_length=512) - document_file = models.FileField( - verbose_name=_('file')) - creation = models.DateTimeField( - verbose_name=_('creation date'), - auto_now_add=True) + settings.AUTH_USER_MODEL, + verbose_name=_('user')) + document = models.ForeignKey( + 'Document', + related_name='user_documents', + verbose_name=_('document')) + filename = models.CharField( + verbose_name=_('filename'), + max_length=512) + created = models.DateTimeField( + verbose_name=_('creation date'), + auto_now_add=True) + + class Meta: + verbose_name = _('user document') + verbose_name_plural = _('user documents') + ordering = ('-created', 'user') + + +class Validation(models.Model): + '''Validation of a document as special kind for an user, + the data field contains metadata extracted from the document. + ''' + user_document = models.ForeignKey('UserDocument', + verbose_name=_('user document')) + document_type = models.CharField(max_length=256, verbose_name=_('document type')) + data = JSONField(null=True, verbose_name=_('data')) + start = models.DateField(verbose_name=_('start date')) + end = models.DateField(verbose_name=_('end date')) + creator = models.CharField(max_length=256, verbose_name=_('creator')) + created = models.DateTimeField(verbose_name=_('creation date'), + auto_now_add=True) + + +class Document(models.Model): + '''Content indexed documents''' + content_hash = models.CharField( + primary_key=True, + max_length=128, + verbose_name=_('content hash')) + content = models.FileField( + verbose_name=_('file')) + + objects = managers.DocumentManager() + + def save(self, *args, **kwargs): + '''Create content_hash if new''' + if not self.content_hash: + self.content_hash = utils.sha256_of_file(self.content) + super(Document, self).save(*args, **kwargs) def delete(self): '''Delete file on model delete''' @@ -23,4 +69,4 @@ class Document(models.Model): class Meta: verbose_name = _('document') verbose_name_plural = _('documents') - ordering = ('-creation',) + ordering = ('content_hash',) diff --git a/fargo/fargo/tables.py b/fargo/fargo/tables.py index 0643039..9873660 100644 --- a/fargo/fargo/tables.py +++ b/fargo/fargo/tables.py @@ -1,7 +1,18 @@ +from django.utils.translation import ugettext_lazy as _ + import django_tables2 as tables + from . import models + class DocumentTable(tables.Table): + '''Display the list of documents of the user''' + filename = tables.Column(verbose_name=_('filename').title()) + size = tables.Column(accessor='document.content.size', + verbose_name=_('size').title()) + created = tables.DateTimeColumn(verbose_name=_('creation date').title()) + class Meta: model = models.Document - fields = ('document_filename', 'creation') + fields = ('filename', 'size', 'created') + empty_text = _('You currently have no documents') diff --git a/fargo/fargo/utils.py b/fargo/fargo/utils.py new file mode 100644 index 0000000..643f931 --- /dev/null +++ b/fargo/fargo/utils.py @@ -0,0 +1,20 @@ +import hashlib +from django.utils.timezone import utc + + +def to_isodate(dt): + return dt.astimezone(utc).isoformat('T').split('.')[0] + 'Z' + + +def get_document_type_schema(settings, document_type): + for schema in settings.FARGO_DOCUMENT_TYPES: + if schema.get('name') == document_type: + return schema + + +def sha256_of_file(f): + '''Compute SHA-256 hash of Django File object''' + hasher = hashlib.sha256() + for chunk in f.chunks(): + hasher.update(chunk) + return hasher.hexdigest() diff --git a/fargo/fargo/views.py b/fargo/fargo/views.py index 9b6ba41..1c0a1a4 100644 --- a/fargo/fargo/views.py +++ b/fargo/fargo/views.py @@ -7,7 +7,8 @@ from django.views.generic import CreateView, DeleteView, View, TemplateView from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404, resolve_url -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden +from django.http import (HttpResponse, HttpResponseRedirect, + HttpResponseForbidden, Http404) from django.core import signing from django.contrib import messages from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME @@ -15,10 +16,11 @@ from django.contrib.auth import logout as auth_logout from django.contrib.auth import views as auth_views from django.utils.translation import ugettext as _ from django.utils.decorators import method_decorator +from django.conf import settings from django_tables2 import SingleTableMixin -from . import models, forms, tables +from . import models, forms, tables, utils try: from mellon.utils import get_idps @@ -30,22 +32,26 @@ class Logger(object): def __init__(self, *args, **kwargs): self.logger = logging.getLogger(__name__) + class CommonUpload(Logger, CreateView): form_class = forms.UploadForm - model = models.Document + model = models.UserDocument template_name = 'fargo/upload.html' def get_form_kwargs(self, **kwargs): kwargs = super(CommonUpload, self).get_form_kwargs(**kwargs) - kwargs['user'] = self.request.user + kwargs['instance'] = models.UserDocument( + user=self.request.user) return kwargs def form_valid(self, form): result = super(CommonUpload, self).form_valid(form) - self.logger.info('user uploaded file %r(%s)', - self.object.document_filename, self.object.pk) + self.logger.info(u'user uploaded file %s (sha256=%s)', + self.object.filename, + self.object.document.content_hash) return result + class Upload(CommonUpload): def get_success_url(self): homepage = reverse('home') @@ -56,9 +62,13 @@ class Upload(CommonUpload): return HttpResponseRedirect(self.get_success_url()) return super(Upload, self).post(request, *args, **kwargs) + class Documents(object): def get_queryset(self): - return models.Document.objects.filter(user=self.request.user) + return models.UserDocument.objects \ + .filter(user=self.request.user) \ + .select_related('document', 'user') + class Homepage(Documents, SingleTableMixin, CommonUpload): '''Show documents of users, eventually paginate and sort them.''' @@ -66,7 +76,7 @@ class Homepage(Documents, SingleTableMixin, CommonUpload): form_class = forms.UploadForm table_class = tables.DocumentTable table_pagination = { - 'per_page': 5, + 'per_page': 5, } def get_success_url(self): @@ -91,51 +101,57 @@ class PickList(Homepage): class Document(TemplateView): template_name = 'fargo/document.html' + class Delete(Logger, DeleteView): - model = models.Document + model = models.UserDocument def delete(self, request, *args, **kwargs): result = super(Delete, self).delete(request, *args, **kwargs) - messages.info(request, _('File %s deleted') % self.object.document_filename) - self.logger.info('user deleted file %r(%s)', - self.object.document_filename, self.object.pk) + messages.info(request, _('File %s deleted') % self.object.filename) + self.logger.info('user deleted file %r(%s)', self.object.filename, + self.object.pk) return result def get_success_url(self): return '../..?%s' % self.request.META['QUERY_STRING'] -class Pick(Logger, View): + +class Pick(Documents, Logger, View): http_method_allowed = ['post'] def post(self, request, pk): - document = get_object_or_404(models.Document, pk=pk, user=self.request.user) + user_document = get_object_or_404(self.get_queryset(), pk=pk, + user=request.user) pick = self.request.GET['pick'] - token = signing.dumps(document.pk) - url = reverse('remote_download', kwargs={'filename': document.document_filename}) + token = signing.dumps(user_document.pk) + url = reverse('remote_download', + kwargs={'filename': user_document.filename}) url += '?%s' % urllib.urlencode({'token': token}) url = request.build_absolute_uri(url) scheme, netloc, path, qs, fragment = urlparse.urlsplit(pick) qs = urlparse.parse_qs(qs) - print url qs['url'] = url qs = urllib.urlencode(qs, True) redirect = urlparse.urlunsplit((scheme, netloc, path, qs, fragment)) - print 'redirect', redirect - self.logger.info('user picked file %r(%s) returned to %s', - document.document_filename, document.pk, pick) + self.logger.info(u'user picked file %s sha256 %s returned to %s', + user_document.filename, + user_document.document.content_hash, pick) return HttpResponseRedirect(redirect) -class Download(View): + +class Download(Documents, View): def get(self, request, pk, filename): - document = get_object_or_404(models.Document, pk=pk, user=self.request.user) - return self.return_document(document) + user_document = get_object_or_404(self.get_queryset(), pk=pk, + user=self.request.user) + return self.return_user_document(user_document) - def return_document(self, document): - response = HttpResponse(document.document_file.chunks(), - content_type='application/octet-stream') + def return_user_document(self, user_document): + response = HttpResponse(user_document.document.content.chunks(), + content_type='application/octet-stream') response['Content-disposition'] = 'attachment' return response + class RemoteDownload(Download): '''Allow downloading any file given the URL contains a signed token''' def get(self, request, filename): @@ -151,18 +167,20 @@ class RemoteDownload(Download): return HttpResponseForbidden('token has expired') except signing.BadSignature: return HttpResponseForbidden('token signature is invalid') - document = get_object_or_404(models.Document, pk=pk) - return self.return_document(document) + user_document = get_object_or_404(models.UserDocument, pk=pk) + return self.return_user_document(user_document) + class JSONP(Documents, View): def get_data(self, request): d = [] - for document in self.get_queryset(): - url = reverse('download', kwargs={'pk': document.pk, - 'filename': document.document_filename}) + for user_document in self.get_queryset(): + url = reverse('download', + kwargs={'pk': user_document.pk, + 'filename': user_document.filename}) url = request.build_absolute_uri(url) d.append({ - 'filename': document.document_filename, + 'filename': user_document.filename, 'url': url, }) return d @@ -170,9 +188,10 @@ class JSONP(Documents, View): def get(self, request): callback = request.GET.get('callback', 'callback') s = '%s(%s)' % (callback.encode('ascii'), - dumps(self.get_data(request))) + dumps(self.get_data(request))) return HttpResponse(s, content_type='application/javascript') + class JSON(JSONP): def get(self, request): username = request.GET.get('username') @@ -182,11 +201,20 @@ class JSON(JSONP): elif not request.user.is_authenticated(): return method_decorator(login_required)(JSON.get)(self, request) response = HttpResponse(dumps(self.get_data(request)), - content_type='application/json') + content_type='application/json') response['Access-Control-Allow-Origin'] = '*' return response +class ChooseDocumentKind(TemplateView): + template_name = 'fargo/choose_document_kind.html' + + def get_context_data(self, **kwargs): + ctx = super(ChooseDocumentKind, self).get_context_data(**kwargs) + ctx['document_kinds'] = settings.FARGO_DOCUMENT_KINDS + return ctx + + def login(request, *args, **kwargs): if any(get_idps()): if not 'next' in request.GET: @@ -207,13 +235,110 @@ def logout(request, next_page=None): return HttpResponseRedirect(next_page) -home = login_required(Homepage.as_view()) -document = login_required(Document.as_view()) -download = login_required(Download.as_view()) -upload = login_required(Upload.as_view()) +class Metadata(View): + def get(self, request, username, content_hash, document_type): + schema = utils.get_document_type_schema(settings, document_type) + if not schema: + raise Http404 + try: + user_document = models.UserDocument.objects.get( + user__username=username[:30], + document__content_hash=content_hash) + except models.UserDocument.DoesNotExist: + raise Http404 + try: + validation = models.Validation.objects.get( + user_document=user_document, + document_type=document_type) + except models.Validation.DoesNotExist: + data = {'err': 1, 'data': 'validation-not-found'} + else: + metadata = [] + data = { + 'err': 0, + 'data': { + 'type': schema['name'], + 'label': schema['label'], + 'creator': validation.creator, + 'created': utils.to_isodate(validation.created), + 'start': validation.start.isoformat(), + 'end': validation.end.isoformat(), + 'metadata': metadata, + }, + } + for field in schema.get('metadata', []): + value = validation.data.get(field['name']) + if value: + metadata.append({ + 'value': value, + 'name': field['name'], + 'label': field['label'], + }) + return HttpResponse(dumps(data), content_type='application/json') + + +class Validation(CreateView): + form_class = forms.ValidationForm + template_name = 'fargo/validation.html' + + def dispatch(self, request, username, content_hash, document_type): + self.user_document = get_object_or_404( + models.UserDocument, user__username=username[:30], + document__content_hash=content_hash) + if not utils.get_document_type_schema(settings, document_type): + raise Http404 + self.document_type = document_type + return super(Validation, self).dispatch(request, + username=username, + content_hash=content_hash, + document_type=document_type) + + def post(self, request, *args, **kwargs): + if 'cancel' in request.POST: + return HttpResponseRedirect(self.get_success_url()) + return super(Validation, self).post(request, *args, **kwargs) + + def get_form_kwargs(self, **kwargs): + kwargs = super(Validation, self).get_form_kwargs(**kwargs) + try: + validation = models.Validation.objects.get( + user_document=self.user_document, + document_type=self.document_type) + kwargs['instance'] = validation + initials = kwargs['initial'] = kwargs.get('initial', {}) + for key, value in validation.data.iteritems(): + initials[key] = value + except models.Validation.DoesNotExist: + pass + kwargs['user'] = self.request.user + kwargs['user_document'] = self.user_document + kwargs['document_type'] = self.document_type + return kwargs + + def get_success_url(self): + return self.request.GET.get(REDIRECT_FIELD_NAME) \ + or reverse(settings.LOGIN_REDIRECT_URL) + + +class DocumentTypes(View): + def get(self, request): + data = { + 'err': 0, + 'data': settings.FARGO_DOCUMENT_TYPES, + } + return HttpResponse(dumps(data), content_type='application/json') + + +home = login_required(Homepage.as_view()) +document = login_required(Document.as_view()) +download = login_required(Download.as_view()) +upload = login_required(Upload.as_view()) remote_download = RemoteDownload.as_view() -delete = login_required(Delete.as_view()) -pick = login_required(Pick.as_view()) -jsonp = login_required(JSONP.as_view()) -json = login_required(JSON.as_view()) +delete = login_required(Delete.as_view()) +pick = login_required(Pick.as_view()) +jsonp = login_required(JSONP.as_view()) +json = login_required(JSON.as_view()) pick_list = login_required(PickList.as_view()) +metadata = Metadata.as_view() +validation = login_required(Validation.as_view()) +document_types = DocumentTypes.as_view() diff --git a/fargo/settings.py b/fargo/settings.py index b3b5d5b..b8523bb 100644 --- a/fargo/settings.py +++ b/fargo/settings.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Django settings for fargo project. @@ -132,6 +133,21 @@ MELLON_USERNAME_TEMPLATE = '{attributes[name_id_content]}' MELLON_IDENTITY_PROVIDERS = [] +FARGO_DOCUMENT_TYPES = [ + { + 'name': 'justificatif-de-domicile', + 'label': 'Justificatif de domicile', + 'metadata': [ + {'label': u'Numéro', 'name': 'numero'}, + {'label': u'Rue', 'name': 'rue'}, + {'label': u'Code postal', 'name': 'code-postal'}, + {'label': u'Ville', 'name': 'ville'}, + ] + } +] + +LOGIN_REDIRECT_URL = 'home' + local_settings_file = os.environ.get('FARGO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')) diff --git a/fargo/templates/fargo/base_iframe.html b/fargo/templates/fargo/base_iframe.html new file mode 100644 index 0000000..1486be6 --- /dev/null +++ b/fargo/templates/fargo/base_iframe.html @@ -0,0 +1,13 @@ +{% load gadjo staticfiles %} + + + + + {% block extra_head %} + {% endblock %} + + + {% block content %} + {% endblock %} + + diff --git a/fargo/templates/fargo/pick.html b/fargo/templates/fargo/pick.html index fc4c6d2..16c9a5c 100644 --- a/fargo/templates/fargo/pick.html +++ b/fargo/templates/fargo/pick.html @@ -1,10 +1,10 @@ -{% load render_table from django_tables2 %}{% load gadjo i18n staticfiles %} - - - - - - +{% extends "fargo/base_iframe.html" %} +{% load render_table from django_tables2 %} +{% load i18n %} + +{% block bodyargs %}class="fargo-popup fargo-pick-popup"{% endblock %} + +{% block content %} {% render_table table "fargo/pick_table.html" %}
{% csrf_token %} @@ -13,11 +13,10 @@
- - +{% endblock %} diff --git a/fargo/templates/fargo/table.html b/fargo/templates/fargo/table.html index 015aaec..d65176b 100644 --- a/fargo/templates/fargo/table.html +++ b/fargo/templates/fargo/table.html @@ -33,4 +33,5 @@ {% endblock %} + Valider {% endblock table.tbody.row %} diff --git a/fargo/templates/fargo/validation.html b/fargo/templates/fargo/validation.html new file mode 100644 index 0000000..0130a8b --- /dev/null +++ b/fargo/templates/fargo/validation.html @@ -0,0 +1,22 @@ +{% extends "fargo/base_iframe.html" %} +{% load i18n gadjo %} + +{% block extra_head %} + + +{% endblock %} + +{% block bodyargs %}class="fargo-popup fargo-pick-popup"{% endblock %} + +{% block content %} +
+ {% csrf_token %} + {{ form.as_p }} + + +
+ +{% endblock %} diff --git a/fargo/urls.py b/fargo/urls.py index a18dcf2..86d5b15 100644 --- a/fargo/urls.py +++ b/fargo/urls.py @@ -2,10 +2,12 @@ from django.conf import settings from django.conf.urls import patterns, include, url from django.contrib import admin -from .fargo.views import (home, jsonp, json, document, download, pick, delete, upload, - remote_download, login, logout, pick_list) +from .fargo.views import (home, jsonp, json, document, download, pick, delete, + upload, remote_download, login, logout, pick_list, + metadata, validation, document_types) -urlpatterns = patterns('', +urlpatterns = patterns( + '', url(r'^$', home, name='home'), url(r'^pick$', pick_list, name='list_to_pick'), url(r'^jsonp/$', jsonp, name='jsonp'), @@ -13,19 +15,22 @@ urlpatterns = patterns('', url(r'^(?P\d+)/$', document, name='document'), url(r'^(?P\d+)/delete/$', delete, name='delete'), url(r'^(?P\d+)/pick/$', pick, name='pick'), - url(r'^(?P\d+)/download/(?P[^/]*)$', download, name='download'), + url(r'^(?P\d+)/download/(?P[^/]*)$', download, + name='download'), url(r'^upload/$', upload, name='upload'), - url(r'^remote-download/(?P[^/]*)$', remote_download, name='remote_download'), + url(r'^remote-download/(?P[^/]*)$', remote_download, + name='remote_download'), + url(r'^metadata/(?P[^/]*)/(?P[^/]*)/(?P[^/]*)/$', + metadata, name='metadata'), + url(r'^validation/(?P[^/]*)/(?P[^/]*)/(?P[^/]*)/$', + validation, name='validation'), url(r'^admin/', include(admin.site.urls)), url(r'^login/$', login, name='auth_login'), url(r'^logout/$', logout, name='auth_logout'), + url(r'^document-types/$', document_types, name='document_types'), ) if 'mellon' in settings.INSTALLED_APPS: - urlpatterns += patterns('', url(r'^accounts/mellon/', include('mellon.urls'))) - -if settings.DEBUG: - urlpatterns += patterns('django.contrib.staticfiles.views', - url(r'^static/(?P.*)$', 'serve'), - ) + urlpatterns += patterns('', + url(r'^accounts/mellon/', include('mellon.urls'))) diff --git a/setup.py b/setup.py index 4d97d3e..6d0edbe 100755 --- a/setup.py +++ b/setup.py @@ -97,6 +97,7 @@ setup( install_requires=['django>=1.7', 'gadjo', 'django-tables2', + 'django-jsonfield >= 0.9.3', ], zip_safe=False, cmdclass={ -- 2.1.4