Complete refactoring to allow out of order document, document file or metadata creation

This commit is contained in:
Roberto Rosario
2011-02-05 21:00:23 -04:00
parent 52b0da385e
commit d8d8c75a28
9 changed files with 193 additions and 57 deletions

View File

@@ -195,7 +195,9 @@ def resolve_template_variable(context, name):
try:
return unescape_string_literal(name)
except ValueError:
return Variable(name).resolve(context)
#return Variable(name).resolve(context)
#Research if should return always as a str
return str(Variable(name).resolve(context))
except TypeError:
return name

View File

@@ -3,6 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from common.api import register_links, register_menu
from models import Document
from staging import StagingFile
document_list = {'text':_(u'documents list'), 'view':'document_list', 'famfam':'page'}
document_create = {'text':_('upload a document'), 'view':'document_create', 'famfam':'page_add'}
@@ -11,10 +12,15 @@ document_view = {'text':_('details'), 'view':'document_view', 'args':'object.id'
document_delete = {'text':_('delete'), 'view':'document_delete', 'args':'object.id', 'famfam':'page_delete'}
document_edit = {'text':_('edit'), 'view':'document_edit', 'args':'object.id', 'famfam':'page_edit'}
document_create_from_staging = {'text':_('select staging'), 'view':'document_create_from_staging', 'args':{'file_id':'object.id', 'document_type_id': 'document_type_id'}, 'famfam':'page_add'}
register_links(Document, [document_view, document_edit, document_delete])
register_links(Document, [document_list, document_create, document_create_multiple], menu_name='sidebar')
register_links(['document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list, document_create, document_create_multiple], menu_name='sidebar')
register_links(StagingFile, [document_create_from_staging])
register_menu([
{'text':_('documents'), 'view':'document_list', 'links':[

View File

@@ -1,7 +1,8 @@
from django.contrib import admin
from models import MetadataType, DocumentType, Document, \
DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename
DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename, \
DocumentFile
class MetadataTypeAdmin(admin.ModelAdmin):
@@ -33,9 +34,19 @@ class DocumentMetadataInline(admin.StackedInline):
allow_add = True
class DocumentFileInline(admin.StackedInline):
model = DocumentFile
extra = 1
classes = ('collapse-open',)
allow_add = True
class DocumentAdmin(admin.ModelAdmin):
inlines = [DocumentMetadataInline,]
list_display = ('uuid', 'file_filename', 'file_extension', 'file_mimetype')
inlines = [DocumentFileInline, DocumentMetadataInline,]
list_display = ('uuid',)
admin.site.register(MetadataType, MetadataTypeAdmin)

View File

@@ -1,5 +1,6 @@
import datetime
import hashlib
import uuid
from django.conf import settings
@@ -14,3 +15,5 @@ DELETE_LOCAL_ORIGINAL = getattr(settings, 'DOCUMENTS_DELETE_LOCAL_ORIGINAL', Fal
SLUGIFY_PATH = getattr(settings, 'DOCUMENTS_SLUGIFY_PATH', False)
CHECKSUM_FUNCTION = getattr(settings, 'DOCUMENTS_CHECKSUM_FUNCTION', lambda x: hashlib.sha256(x).hexdigest())
DELETE_STAGING_FILE_AFTER_UPLOAD = getattr(settings, 'DOCUMENTS_DELETE_STAGING_FILE_AFTER_UPLOAD', False)
UUID_FUNCTION = getattr(settings, 'DOCUMENTS_UUID_FUNTION', lambda:unicode(uuid.uuid4()))
STORAGE_DIRECTORY_NAME = getattr(settings, 'DOCUMENTS_STORAGE_DIRECTORY_NAME', 'documents')

View File

@@ -9,12 +9,16 @@ from common.wizard import BoundFormWizard
from common.utils import urlquote
from common.forms import DetailForm
from models import Document, DocumentType, DocumentTypeMetadataType
from models import Document, DocumentType, DocumentTypeMetadataType, DocumentFile
from documents.conf.settings import AVAILABLE_FUNCTIONS
class DocumentFileForm(forms.ModelForm):
class Meta:
model = DocumentFile
class DocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
@@ -104,8 +108,9 @@ class DocumentCreateWizard(BoundFormWizard):
self.initial = {1:initial}
if step == 1:
self.urldata = []
for metadata in form.cleaned_data:
self.urldata.append((metadata['id'],metadata['value']))
for id, metadata in enumerate(form.cleaned_data):
self.urldata.append(('metadata%s_id' % id,metadata['id']))
self.urldata.append(('metadata%s_value' % id,metadata['value']))
def get_template(self, step):

View File

@@ -1,6 +1,5 @@
import errno
import os
import uuid
import mimetypes
from datetime import datetime
@@ -16,18 +15,21 @@ from documents.conf.settings import AVAILABLE_FUNCTIONS
from documents.conf.settings import FILESERVING_PATH
from documents.conf.settings import SLUGIFY_PATH
from documents.conf.settings import CHECKSUM_FUNCTION
from documents.conf.settings import UUID_FUNCTION
from documents.conf.settings import STORAGE_DIRECTORY_NAME
if SLUGIFY_PATH == False:
#Do not slugify path or filenames and extensions
slugify = lambda x:x
def get_filename_from_uuid(instance, filename, directory='documents'):
populate_file_extension_and_mimetype(instance, filename)
def get_filename_from_uuid(instance, filename, directory=STORAGE_DIRECTORY_NAME):
#populate_file_extension_and_mimetype(instance, filename)
stem, extension = os.path.splitext(filename)
return '%s/%s%s' % (directory, instance.uuid, extension)
def populate_file_extension_and_mimetype(instance, filename):
def populate_file_extension_and_mimetype(instance):#, filename):
filename = instance.file.name
# First populate the file extension and mimetype
instance.file_mimetype, encoding = mimetypes.guess_type(filename)
if not instance.file_mimetype:
@@ -44,32 +46,19 @@ class DocumentType(models.Model):
def __unicode__(self):
return self.name
class Document(models.Model):
""" Minimum fields for a document entry.
Inherit this model to customise document metadata, see BasicDocument for an example.
"""
document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'))
class DocumentFile(models.Model):
file = models.FileField(upload_to=get_filename_from_uuid)
uuid = models.CharField(max_length=36, default=lambda:unicode(uuid.uuid4()), blank=True, editable=False)
file_mimetype = models.CharField(max_length=50, default='', editable=False)
file_filename = models.CharField(max_length=64, default='', editable=False)
file_extension = models.CharField(max_length=10, default='', editable=False)
date_added = models.DateTimeField(verbose_name=_(u'added'), auto_now_add=True)
date_updated = models.DateTimeField(verbose_name=_(u'updated'), auto_now=True)
checksum = models.TextField(blank=True, null=True, verbose_name=_(u'checksum'), editable=False)
class Meta:
verbose_name = _(u'document')
verbose_name_plural = _(u'documents')
ordering = ['-date_updated', '-date_added']
verbose_name = _(u'document file')
verbose_name_plural = _(u'documents files')
def __unicode__(self):
return self.uuid
@models.permalink
def get_absolute_url(self):
return ('document_view', [self.id])
return self.id
def update_checksum(self, save=True):
self.checksum = unicode(CHECKSUM_FUNCTION(self.file.read()))
@@ -78,11 +67,12 @@ class Document(models.Model):
def save(self, *args, **kwargs):
self.update_checksum(save=False)
super(Document, self).save(*args, **kwargs)
populate_file_extension_and_mimetype(self)
super(DocumentFile, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
self.delete_fs_links()
super(Document, self).delete(*args, **kwargs)
super(DocumentFile, self).delete(*args, **kwargs)
def calculate_fs_links(self):
targets = []
@@ -120,6 +110,33 @@ class Document(models.Model):
raise OSError(ugettext(u'Unable to delete metadata indexing symbolic link: %s') % exc)
class Document(models.Model):
""" Minimum fields for a document entry.
Inherit this model to customise document metadata, see BasicDocument for an example.
"""
document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'))
#file = models.FileField(upload_to=get_filename_from_uuid, blank=True, null=True)
uuid = models.CharField(max_length=36, default=UUID_FUNCTION(), blank=True, editable=False)
#file_mimetype = models.CharField(max_length=50, default='', editable=False)
#file_filename = models.CharField(max_length=64, default='', editable=False)
#file_extension = models.CharField(max_length=10, default='', editable=False)
date_added = models.DateTimeField(verbose_name=_(u'added'), auto_now_add=True)
date_updated = models.DateTimeField(verbose_name=_(u'updated'), auto_now=True)
#checksum = models.TextField(blank=True, null=True, verbose_name=_(u'checksum'), editable=False)
document_file = models.ForeignKey(DocumentFile, blank=True, null=True, verbose_name=_('document file'))
class Meta:
verbose_name = _(u'document')
verbose_name_plural = _(u'documents')
ordering = ['-date_updated', '-date_added']
def __unicode__(self):
return self.uuid
@models.permalink
def get_absolute_url(self):
return ('document_view', [self.id])
available_functions_string = (_(u' Available functions: %s') % ','.join(['%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else ''
class MetadataType(models.Model):

67
apps/documents/staging.py Normal file
View File

@@ -0,0 +1,67 @@
import os
import shutil
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.core.files.storage import default_storage
from documents.conf.settings import STAGING_DIRECTORY
from documents.conf.settings import UUID_FUNCTION
from models import Document, get_filename_from_uuid
def get_all_files():
return sorted([os.path.normcase(f) for f in os.listdir(STAGING_DIRECTORY)])
class StagingFile(object):
@classmethod
def get_all(cls):
staging_files = []
for id, filename in enumerate(get_all_files()):
staging_files.append(StagingFile(
filepath=os.path.join(STAGING_DIRECTORY, filename),
id=id))
return staging_files
@classmethod
def get(cls, id):
files = get_all_files()
if id <= len(files):
return StagingFile(
filepath=os.path.join(STAGING_DIRECTORY, files[id]),
id=id)
raise ObjectDoesNotExist
def __init__(self, filepath, id):
self.filepath = filepath
self.filename = os.path.basename(filepath)
self._id = id
def __unicode__(self):
return self.filename
def __repr__(self):
return self.__unicode__()
def __getattr__(self, name):
if name == 'id':
return self._id
else:
raise AttributeError, name
def upload(self, document_type):
document = Document(document_type=document_type)
document.save(save=False)
print 'UUID', document.uuid
tmp_filepath = os.path.join(settings.MEDIA_ROOT, UUID_FUNCTION())
#shutil.copy(self.filepath, tmp_filepath)
#document = Document(document_type=document_type,
# file=tmp_filepath)
#document.save()
#final_filepath = get_filename_from_uuid(document, filename=self.filename)
#document.save()
#print final_filepath

View File

@@ -5,11 +5,14 @@ from django.views.generic.create_update import create_object, update_object
urlpatterns = patterns('documents.views',
url(r'^document/list/$', 'document_list', (), 'document_list'),
url(r'^document/create/single/$', 'document_create', {'multiple':False}, 'document_create'),
url(r'^document/create/multiple/$', 'document_create', {'multiple':True}, 'document_create_multiple'),
url(r'^document/create/from/local/single/$', 'document_create', {'multiple':False}, 'document_create'),
url(r'^document/create/from/local/multiple/$', 'document_create', {'multiple':True}, 'document_create_multiple'),
url(r'^document/type/(?P<document_type_id>\d+)/upload/single/$', 'upload_document_with_type', {'multiple':False}, 'upload_document_with_type'),
url(r'^document/type/(?P<document_type_id>\d+)/upload/multiple/$', 'upload_document_with_type', {'multiple':True}, 'upload_multiple_documents_with_type'),
url(r'^document/(?P<document_id>\d+)/$', 'document_view', (), 'document_view'),
url(r'^document/(?P<document_id>\d+)/delete/$', 'document_delete', (), 'document_delete'),
url(r'^document/(?P<document_id>\d+)/edit/$', 'document_edit', (), 'document_edit'),
url(r'^document/type/(?P<document_type_id>\d+)/upload/from/staging/(?P<file_id>\d+)/single/$', 'document_create_from_staging', {'multiple':False}, 'document_create_from_staging'),
#url(r'^document/create/from/staging/(?P<file_id>\d+)/$', '', (), 'document_create_from_staging'),
)

View File

@@ -1,6 +1,3 @@
import datetime
import os
from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404, redirect
@@ -12,13 +9,12 @@ from django.views.generic.create_update import create_object, delete_object, upd
from django.forms.formsets import formset_factory
from forms import DocumentForm_view
from models import Document, DocumentMetadata, DocumentType, MetadataType
from forms import DocumentTypeSelectForm, DocumentCreateWizard, \
MetadataForm, DocumentForm, DocumentForm_edit
MetadataForm, DocumentForm, DocumentForm_edit, DocumentForm_view, \
DocumentFileForm
from documents.conf.settings import STAGING_DIRECTORY
from staging import StagingFile
def document_list(request):
return object_list(
@@ -43,10 +39,31 @@ def document_create(request, multiple=True):
return wizard(request)
def _save_metadata_from_request(request, document):
metadata_dict = {
'id':{},
'value':{}
}
#Match out of order metadata_type ids with metadata values from request
for key, value in request.GET.items():
if 'metadata' in key:
index, element = key[8:].split('_')
metadata_dict[element][index] = value
#Use matched metadata now to create document metadata
for key, value in zip(metadata_dict['id'].values(), metadata_dict['value'].values()):
document_metadata = DocumentMetadata(
document=document,
metadata_type=get_object_or_404(MetadataType, pk=key),
value=value
)
document_metadata.save()
def upload_document_with_type(request, document_type_id, multiple=True):
document_type = get_object_or_404(DocumentType, pk=document_type_id)
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES, initial={'document_type':document_type})
form = DocumentFileForm(request.POST, request.FILES)#, initial={'document_type':document_type})
if form.is_valid():
instance = form.save()
if 'new_filename' in form.cleaned_data:
@@ -54,14 +71,7 @@ def upload_document_with_type(request, document_type_id, multiple=True):
instance.file_filename = form.cleaned_data['new_filename'].filename
instance.save()
for key, value in request.GET.items():
document_metadata = DocumentMetadata(
document=instance,
metadata_type=get_object_or_404(MetadataType, pk=key),
value=value
)
document_metadata.save()
_save_metadata_from_request(request, instance)
messages.success(request, _(u'Document uploaded successfully.'))
try:
instance.create_fs_links()
@@ -73,13 +83,14 @@ def upload_document_with_type(request, document_type_id, multiple=True):
else:
return HttpResponseRedirect(reverse('document_list'))
else:
form = DocumentForm(initial={'document_type':document_type})
form = DocumentFileForm()#initial={'document_type':document_type})
filelist = sorted([os.path.normcase(f) for f in os.listdir(STAGING_DIRECTORY)])
filelist = StagingFile.get_all()
return render_to_response('generic_form.html', {
'form':form,
'title':_(u'upload a local document'),
'document_type_id':document_type_id,
'subtemplates_dict':[
{
'name':'generic_list_subtemplate.html',
@@ -162,3 +173,14 @@ def document_edit(request, document_id):
}, context_instance=RequestContext(request))
def document_create_from_staging(request, file_id, document_type_id, multiple=True):
document_type = get_object_or_404(DocumentType, pk=document_type_id)
staging_file = StagingFile.get(id=int(file_id))
staging_file.upload(document_type=document_type)
if multiple:
return HttpResponseRedirect(request.get_full_path())
else:
return HttpResponseRedirect(reverse('document_list'))