Add MultiFormView class based view, convert InteractiveUpload view to MultiForm view

This commit is contained in:
Roberto Rosario
2014-10-24 18:18:40 -04:00
parent 4acf88c9a6
commit c23205f464
8 changed files with 271 additions and 182 deletions

View File

@@ -14,6 +14,7 @@ from django.shortcuts import redirect, render_to_response
from django.template import RequestContext
from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic.list import ListView
@@ -394,3 +395,73 @@ class SingleObjectListView(MayanPermissionCheckMixin, ExtraContextMixin, MayanVi
return queryset
else:
return queryset
class MultiFormView(FormView):
prefixes = {}
prefix = None
def get_form_kwargs(self, form_name):
kwargs = {}
kwargs.update({'initial': self.get_initial(form_name)})
kwargs.update({'prefix': self.get_prefix(form_name)})
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def _create_form(self, form_name, klass):
form_kwargs = self.get_form_kwargs(form_name)
form_create_method = 'create_%s_form' % form_name
if hasattr(self, form_create_method):
form = getattr(self, form_create_method)(**form_kwargs)
else:
form = klass(**form_kwargs)
return form
def get_forms(self, form_classes):
return dict([(key, self._create_form(key, klass)) for key, klass in form_classes.items()])
def get_initial(self, form_name):
initial_method = 'get_%s_initial' % form_name
if hasattr(self, initial_method):
return getattr(self, initial_method)()
else:
return self.initial.copy()
def get_prefix(self, form_name):
return self.prefixes.get(form_name, self.prefix)
def get(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
return self.render_to_response(self.get_context_data(forms=forms))
def forms_valid(self, forms):
for form_name, form in forms.items():
form_valid_method = '%s_form_valid' % form_name
if hasattr(self, form_valid_method):
return getattr(self, form_valid_method)(form)
self.all_forms_valid(forms)
return HttpResponseRedirect(self.get_success_url())
def forms_invalid(self, forms):
return self.render_to_response(self.get_context_data(forms=forms))
def post(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
form_name = request.POST.get('action')
forms = self.get_forms(form_classes)
if all([form.is_valid() for form in forms.values()]):
return self.forms_valid(forms)
else:
return self.forms_invalid(forms)

View File

@@ -11,7 +11,7 @@
<form method="{{ submit_method|default:'post' }}" action="{{ form_action }}" class="form">
{% endif %}
{% for form in forms %}
{% for form_name, form in forms.items %}
{% if submit_method != 'GET' and submit_method != 'get' %}
{% csrf_token %}
{% endif %}

View File

@@ -24,6 +24,13 @@ class PseudoFile(File):
self.file.seek(0)
class SourceUploadedFile(File):
def __init__(self, source, file, extra_data=None):
self.file = file
self.source = source
self.extra_data = extra_data
class Attachment(File):
def __init__(self, part, name):
self.name = name

View File

@@ -23,6 +23,7 @@ class UploadBaseForm(forms.Form):
def __init__(self, *args, **kwargs):
show_expand = kwargs.pop('show_expand', False)
self.source = kwargs.pop('source')
super(UploadBaseForm, self).__init__(*args, **kwargs)
if show_expand:

View File

@@ -24,7 +24,7 @@ from djcelery.models import PeriodicTask, IntervalSchedule
from documents.models import Document, DocumentType
from metadata.api import save_metadata_list
from .classes import Attachment, StagingFile
from .classes import Attachment, SourceUploadedFile, StagingFile
from .literals import (DEFAULT_INTERVAL, DEFAULT_POP3_TIMEOUT,
DEFAULT_IMAP_MAILBOX, SOURCE_CHOICES,
SOURCE_CHOICE_STAGING, SOURCE_CHOICE_WATCH,
@@ -53,9 +53,6 @@ class Source(models.Model):
def fullname(self):
return u' '.join([self.class_fullname(), '"%s"' % self.title])
def internal_name(self):
return u'%s_%d' % (self.source_type, self.pk)
def get_transformation_list(self):
return SourceTransformation.transformations.get_for_object_as_list(self)
@@ -93,7 +90,7 @@ class Source(models.Model):
new_version.apply_default_transformations(transformations)
if metadata_dict_list:
save_metadata_list(metadata_dict_list, document, create=True)
save_metadata_list(metadata_dict_list, new_version.document, create=True)
def upload_new_version(self, file_object, document, user, new_version_data=None, comment=None):
if not new_version_data:
@@ -102,6 +99,12 @@ class Source(models.Model):
# TODO: new HISTORY for version updates
new_version = document.new_version(file=file_object, user=user, **new_version_data)
def get_upload_file_object(self, request, form):
pass
def clean_up_upload_file(self, upload_file_object):
pass
class Meta:
ordering = ('title',)
verbose_name = _(u'Source')
@@ -144,6 +147,17 @@ class StagingFolderSource(InteractiveSource):
except OSError as exception:
raise Exception(_(u'Unable get list of staging files: %s') % exception)
def get_upload_file_object(self, request, form):
staging_file = self.get_file(encoded_filename=form.cleaned_data['staging_file_id'])
return SourceUploadedFile(source=self, file=staging_file.as_file(), extra_data=staging_file)
def clean_up_upload_file(self, upload_file_object):
if self.delete_after_upload:
try:
upload_file_object.extra_data.delete()
except Exception as exception:
raise Exception(_(u'Error deleting staging file; %s') % exception)
class Meta:
verbose_name = _(u'Staging folder')
verbose_name_plural = _(u'Staging folders')
@@ -153,9 +167,13 @@ class WebFormSource(InteractiveSource):
is_interactive = True
source_type = SOURCE_CHOICE_WEB_FORM
# TODO: unify uncompress as an InteractiveSource field
uncompress = models.CharField(max_length=1, choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, verbose_name=_(u'Uncompress'), help_text=_(u'Whether to expand or not compressed archives.'))
# Default path
def get_upload_file_object(self, request, form):
return SourceUploadedFile(source=self, file=form.cleaned_data['file'])
class Meta:
verbose_name = _(u'Web form')
verbose_name_plural = _(u'Web forms')

View File

@@ -21,7 +21,7 @@ def task_check_interval_source(source_id):
@app.task(ignore_result=True)
def task_upload_document(source_id, file_path, label, document_type_id=None, expand=False, metadata_dict_list=None, user_id=None, command_line=False, description=None, language=None):
def task_upload_document(source_id, file_path, label, document_type_id, expand=False, metadata_dict_list=None, user_id=None, command_line=False, description=None, language=None):
source = Source.objects.get_subclass(pk=source_id)
document_type = DocumentType.objects.get(pk=document_type_id)

View File

@@ -5,13 +5,14 @@ from django.conf.urls import patterns, url
from .api_views import (APIDocumentCreateView, APIStagingSourceFileView,
APIStagingSourceFileImageView, APIStagingSourceListView,
APIStagingSourceView)
from .views import UploadInteractiveView
from .wizards import DocumentCreateWizard
urlpatterns = patterns('sources.views',
url(r'^staging_file/(?P<staging_folder_pk>\d+)/(?P<encoded_filename>.+)/delete/$', 'staging_file_delete', name='staging_file_delete'),
url(r'^upload/document/new/interactive/(?P<source_id>\d+)/$', 'upload_interactive', (), 'upload_interactive'),
url(r'^upload/document/new/interactive/$', 'upload_interactive', (), 'upload_interactive'),
url(r'^upload/document/new/interactive/(?P<source_id>\d+)/$', UploadInteractiveView.as_view(), name='upload_interactive'),
url(r'^upload/document/new/interactive/$', UploadInteractiveView.as_view(), name='upload_interactive'),
url(r'^upload/document/(?P<document_pk>\d+)/version/interactive/(?P<source_id>\d+)/$', 'upload_new_version', (), 'upload_version'),
url(r'^upload/document/(?P<document_pk>\d+)/version/interactive/$', 'upload_new_version', (), 'upload_version'),

View File

@@ -5,7 +5,7 @@ import tempfile
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
@@ -16,6 +16,7 @@ from django.utils.translation import ugettext_lazy as _
from acls.models import AccessEntry
from common.utils import encapsulate
from common.views import MultiFormView
from documents.exceptions import NewDocumentVersionNotAllowed
from documents.models import DocumentType, Document
from documents.permissions import (PERMISSION_DOCUMENT_CREATE,
@@ -52,7 +53,7 @@ def document_create_siblings(request, document_id):
return HttpResponseRedirect('%s?%s' % (url, urlencode(query_dict)))
def return_function(obj):
def conditional_highlight_factory(obj):
return lambda context: context['source'].source_type == obj.source_type and context['source'].pk == obj.pk
@@ -69,7 +70,7 @@ def get_tab_link_for_source(source, document=None):
'view': view,
'args': args,
'keep_query': True,
'conditional_highlight': return_function(source),
'conditional_highlight': conditional_highlight_factory(source),
}
@@ -91,31 +92,98 @@ def get_active_tab_links(document=None):
}
def upload_new_version(request, document_id):
document = get_object_or_404(Document, pk=document_pk)
# try:
# Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_NEW_VERSION])
# except PermissionDenied:
# AccessEntry.objects.check_access(PERMISSION_DOCUMENT_NEW_VERSION, request.user, document)
# results = get_active_tab_links(document)
class UploadInteractiveView(MultiFormView):
template_name = 'main/generic_form.html'
prefixes = {'source_form': 'source', 'document_form': 'document'}
# messages.success(request, _(u'New document version queued for uploaded and will be available shortly.'))
# return HttpResponseRedirect(reverse('documents:document_version_list', args=[document.pk]))
# title = _(u'Upload a new version from source: %s') % source.title
# TODO: move to version upload
#if document:
#context['object'] = document
pass
def upload_interactive(request, source_id=None):
subtemplates_list = []
def dispatch(self, request, *args, **kwargs):
self.subtemplates_list = []
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
results = get_active_tab_links()
context = {}
self.document_type = get_object_or_404(DocumentType, pk=self.request.GET['document_type_id'])
if 'source_id' in kwargs:
self.source = get_object_or_404(Source.objects.filter(enabled=True).select_subclasses(), pk=kwargs['source_id'])
else:
self.source = InteractiveSource.objects.filter(enabled=True).select_subclasses().first()
return super(UploadInteractiveView, self).dispatch(request, *args, **kwargs)
def forms_valid(self, forms):
if self.source.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK:
expand = forms['source_form'].cleaned_data.get('expand')
else:
if self.source.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y:
expand = True
else:
expand = False
uploaded_file = self.source.get_upload_file_object(self.request, forms['source_form'])
file_object = uploaded_file.file
temporary_file = tempfile.NamedTemporaryFile(delete=False)
for chunk in file_object.chunks():
temporary_file.write(chunk)
temporary_file.close()
file_object.close()
try:
self.source.clean_up_upload_file(uploaded_file)
except Exception as exception:
messages.error(self.request, exception)
if not self.request.user.is_anonymous():
user_id = self.request.user.pk
else:
user_id = None
if 'document_type_available_filenames' in forms['document_form'].cleaned_data:
if forms['document_form'].cleaned_data['document_type_available_filenames']:
label = forms['document_form'].cleaned_data['document_type_available_filenames'].filename
task_upload_document.apply_async(kwargs=dict(
source_id=self.source.pk,
file_path=temporary_file.name,
label=file_object.name,
document_type_id=self.document_type.pk,
expand=expand,
metadata_dict_list=decode_metadata_from_url(self.request.GET),
user_id=user_id,
description=forms['document_form'].cleaned_data.get('description'),
language=forms['document_form'].cleaned_data.get('language')
), queue='uploads')
messages.success(self.request, _(u'New document queued for uploaded and will be available shortly.'))
return HttpResponseRedirect(self.request.get_full_path())
def create_source_form_form(self, **kwargs):
return self.get_form_classes()['source_form'](
prefix=kwargs['prefix'],
source=self.source,
show_expand=(self.source.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK),
data=kwargs.get('data', None),
files=kwargs.get('files', None),
)
def create_document_form_form(self, **kwargs):
return self.get_form_classes()['document_form'](
prefix=kwargs['prefix'],
document_type=self.document_type,
data=kwargs.get('data', None),
files=kwargs.get('files', None),
)
def get_form_classes(self):
return {'document_form': NewDocumentForm, 'source_form': get_upload_form_class(self.source.source_type)}
def get_context_data(self, **kwargs):
context = super(UploadInteractiveView, self).get_context_data(**kwargs)
subtemplates_list = []
results = get_active_tab_links()
context['source'] = self.source
if InteractiveSource.objects.filter(enabled=True).count() == 0:
subtemplates_list.append(
@@ -129,108 +197,11 @@ def upload_interactive(request, source_id=None):
}
})
document_type = get_object_or_404(DocumentType, pk=request.GET['document_type_id'])
title = _(u'Upload a local document from source: %s') % self.source.title
if source_id:
source = get_object_or_404(Source.objects.filter(enabled=True).select_subclasses(), pk=source_id)
else:
source = InteractiveSource.objects.filter(enabled=True).select_subclasses().first()
if source:
upload_form_class = get_upload_form_class(source.source_type)
context['source'] = source
if request.method == 'POST':
upload_form = upload_form_class(
request.POST, request.FILES,
show_expand=(source.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK),
source=source,
prefix='source'
)
document_form = NewDocumentForm(
data=request.POST,
document_type=document_type,
prefix='document')
if upload_form.is_valid() and document_form.is_valid():
if isinstance(self.source, StagingFolderSource):
try:
if source.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK:
expand = upload_form.cleaned_data.get('expand')
else:
if source.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y:
expand = True
else:
expand = False
# TODO: move this to model: "get_file_object(request, upload_form)"
if isinstance(source, WebFormSource):
staging_file = None
file_object = request.FILES['source-file']
else:
staging_file = source.get_file(encoded_filename=upload_form.cleaned_data['staging_file_id'])
file_object = staging_file.as_file()
temporary_file = tempfile.NamedTemporaryFile(delete=False)
for chunk in file_object.chunks():
temporary_file.write(chunk)
temporary_file.close()
file_object.close()
# TODO: move this to model: "post_upload(request)"
if isinstance(source, StagingFolderSource):
if source.delete_after_upload:
try:
staging_file.delete()
except Exception as exception:
messages.error(request, _(u'Error deleting staging file; %s') % exception)
if not request.user.is_anonymous():
user_id = request.user.pk
else:
user_id = None
# Determine how to name the new document
label = file_object.name
if 'document_type_available_filenames' in document_form.cleaned_data:
if document_form.cleaned_data['document_type_available_filenames']:
label = document_form.cleaned_data['document_type_available_filenames'].filename
task_upload_document.apply_async(kwargs=dict(
source_id=source.pk,
file_path=temporary_file.name,
label=label,
document_type_id=document_type.pk,
expand=expand,
metadata_dict_list=decode_metadata_from_url(request.GET),
user_id=user_id,
description=document_form.cleaned_data.get('description'),
language=document_form.cleaned_data.get('language')
), queue='uploads')
messages.success(request, _(u'New document queued for uploaded and will be available shortly.'))
return HttpResponseRedirect(request.get_full_path())
except Exception as exception:
if settings.DEBUG:
raise
messages.error(request, _(u'Unhandled exception: %s') % exception)
else:
upload_form = upload_form_class(
show_expand=(source.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK),
source=source,
prefix='source',
)
document_form = NewDocumentForm(
document_type=document_type,
prefix='document')
title = _(u'Upload a local document from source: %s') % source.title
if isinstance(source, StagingFolderSource):
try:
staging_filelist = list(source.get_files())
staging_filelist = list(self.source.get_files())
except Exception as exception:
messages.error(request, exception)
staging_filelist = []
@@ -239,7 +210,7 @@ def upload_interactive(request, source_id=None):
{
'name': 'main/generic_multiform_subtemplate.html',
'context': {
'forms': [upload_form, document_form],
'forms': context['forms'],
'title': title,
}
},
@@ -256,14 +227,14 @@ def upload_interactive(request, source_id=None):
subtemplates_list.append({
'name': 'main/generic_multiform_subtemplate.html',
'context': {
'forms': [upload_form, document_form],
'forms': context['forms'],
'title': title,
'is_multipart': True
},
})
context.update({
'document_type_id': document_type.pk,
'document_type_id': self.document_type.pk,
'subtemplates_list': subtemplates_list,
'temporary_navigation_links': {
'form_header': {
@@ -284,7 +255,7 @@ def upload_interactive(request, source_id=None):
'name': 'main/generic_subtemplate.html',
'context': {
'title': _(u'Current document type'),
'paragraphs': [document_type if document_type else _(u'None')],
'paragraphs': [self.document_type if self.document_type else _(u'None')],
'side_bar': True,
}
},
@@ -292,7 +263,7 @@ def upload_interactive(request, source_id=None):
'name': 'main/generic_subtemplate.html',
'context': {
'title': _(u'Current metadata'),
'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)),
'paragraphs': metadata_repr_as_list(decode_metadata_from_url(self.request.GET)),
'side_bar': True,
}
}
@@ -300,8 +271,28 @@ def upload_interactive(request, source_id=None):
}
)
return render_to_response('main/generic_form.html', context,
context_instance=RequestContext(request))
return context
def upload_new_version(request, document_id):
subtemplates_list = []
document = get_object_or_404(Document, pk=document_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_NEW_VERSION])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_NEW_VERSION, request.user, document)
results = get_active_tab_links(document)
# messages.success(request, _(u'New document version queued for uploaded and will be available shortly.'))
# return HttpResponseRedirect(reverse('documents:document_version_list', args=[document.pk]))
# title = _(u'Upload a new version from source: %s') % source.title
# TODO: move to version upload
#if document:
#context['object'] = document
pass
def staging_file_delete(request, staging_folder_pk, encoded_filename):