793 lines
34 KiB
Python
793 lines
34 KiB
Python
# -*- coding: utf-8 -*-
|
|
import datetime
|
|
import os.path
|
|
import json
|
|
import csv
|
|
|
|
from django.core.urlresolvers import reverse
|
|
from django.conf import settings
|
|
from django.shortcuts import redirect, get_object_or_404, render
|
|
from django.contrib.formtools.wizard.views import NamedUrlSessionWizardView
|
|
from django.core.files.storage import FileSystemStorage
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.views.generic.detail import SingleObjectMixin, DetailView
|
|
from django.views.generic.list import ListView
|
|
from django.db import transaction
|
|
from django.db.models import Q
|
|
from django.contrib.formtools.wizard.storage.session import SessionStorage
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
|
from django.utils.http import urlencode, urlquote
|
|
from django.utils.timezone import now
|
|
from django.contrib import messages
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.datastructures import SortedDict
|
|
from django.contrib.formtools.wizard.forms import ManagementForm
|
|
from django.contrib.auth.models import User
|
|
|
|
from ..base.models import (Request, Transition, Entity, Status,
|
|
DocumentLicence)
|
|
from ..base.models.request import get_default_status
|
|
from ..base.rbac import get_workable_requests, get_workflows, get_entity_filter
|
|
from ..utils import sort_by_frequency
|
|
|
|
from forms import DocumentUploadForm, DocumentDetailsForm, ReproDetailsForm, \
|
|
DeliveryForm, CopyrigtsForm, ValidationForm, ReproOriginForm, \
|
|
FinancialForm, CostForm
|
|
import app_settings
|
|
from utils import field_completion
|
|
|
|
import models
|
|
import utils
|
|
|
|
|
|
named_new_request_forms = (
|
|
('document_upload', DocumentUploadForm),
|
|
('document_details', DocumentDetailsForm),
|
|
('repro_origin', ReproOriginForm),
|
|
('reprography', ReproDetailsForm),
|
|
('delivery', DeliveryForm),
|
|
('document_copyrights', CopyrigtsForm),
|
|
('financial_information', FinancialForm),
|
|
('real_cost', CostForm),
|
|
('validation', ValidationForm),
|
|
)
|
|
|
|
class DummyStorage(SessionStorage):
|
|
def set_step_files(self, step, files):
|
|
super(DummyStorage, self).set_step_files(step, {})
|
|
|
|
def get_step_files(self, step):
|
|
return None
|
|
|
|
class RequestWizardView(NamedUrlSessionWizardView, SingleObjectMixin):
|
|
model = Request
|
|
template_name = 'new_request.html'
|
|
file_storage = FileSystemStorage(location = os.path.join(settings.MEDIA_ROOT, 'temporary-uploads'))
|
|
storage_name = 'polynum.request.views.DummyStorage'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.kwargs = kwargs
|
|
self.object = self.get_object() # cache the current object
|
|
return super(RequestWizardView, self).dispatch(request, *args, **kwargs)
|
|
|
|
def get_template_names(self):
|
|
return ['%s.%s' % (self.steps.current, self.template_name), self.template_name]
|
|
|
|
def done(self, form_list, **kwargs):
|
|
ctx = self.get_context_data(form_list[0], **kwargs)
|
|
if ctx.get('validated'):
|
|
instance = self.object
|
|
instance.status = get_default_status()
|
|
instance.save()
|
|
return redirect('list_request')
|
|
for step, form, missings in ctx['all_forms']:
|
|
if missings:
|
|
return redirect(self.get_step_url(step=step))
|
|
raise NotImplemented
|
|
|
|
def get_frequent_financial_codes(self, user):
|
|
frequent_codes = list(Request.objects.filter(
|
|
Q(user=user)|Q(history__user=user)) \
|
|
.order_by('-creation_date') \
|
|
.distinct()[:15] \
|
|
.values_list('financial_code', flat=True))
|
|
instance = self.object
|
|
codes = instance.entity.accounting_codes().values_list('code', flat=True)
|
|
frequent_codes.extend(codes)
|
|
return map(lambda x: (x,x), sort_by_frequency(filter(None, frequent_codes)))
|
|
|
|
def get_frequent_entities(self, user):
|
|
return sort_by_frequency(filter(lambda x: x[1] is not None, list(Request.objects.filter(
|
|
Q(user=user)|Q(history__user=user))
|
|
.order_by('-creation_date')
|
|
.distinct()[:15]
|
|
.select_related('entity')
|
|
.values_list('entity__name', 'entity__id'))
|
|
+ list(Entity.objects.filter(
|
|
code__in=user.get_preferred_entities())
|
|
.values_list('name', 'id'))))
|
|
|
|
def get_form_kwargs(self, step):
|
|
kwargs = super(RequestWizardView, self).get_form_kwargs(self)
|
|
if step == 'financial_information':
|
|
kwargs['frequent_financial_codes'] = self.get_frequent_financial_codes(
|
|
self.request.user)
|
|
if step == 'repro_origin':
|
|
kwargs['frequent_entities'] = self.get_frequent_entities(
|
|
self.request.user)
|
|
return kwargs
|
|
|
|
def get_context_data(self, form, **kwargs):
|
|
'''
|
|
Add all_forms = list of (step, form, pprint_data), where pprint_data is
|
|
a list of (name, value) created by each form, representing a "nice"
|
|
view of the stored datas
|
|
'''
|
|
context = super(RequestWizardView, self).get_context_data(form=form, **kwargs)
|
|
# cleaned_data = self.get_all_cleaned_data() or {}
|
|
all_forms = []
|
|
validated = True
|
|
for step, form in self.get_form_list().items():
|
|
form_instance = self.get_form(step=step)
|
|
pprint_data = form_instance.pprint_data({})
|
|
if pprint_data:
|
|
missings = [ l[0] for l in pprint_data if len(l) > 2 and l[2]]
|
|
else:
|
|
missings = []
|
|
validated = validated and not bool(missings)
|
|
all_forms += [ (step, form, pprint_data, missings) ]
|
|
context['all_forms'] = all_forms
|
|
context['validated'] = validated
|
|
context['object'] = self.object
|
|
context['workflows'] = get_workflows(self.request.user, context['object']) \
|
|
.exclude(action__special_type='edit').filter(action__validate_request__lte=validated).order_by('-warn', 'default')
|
|
context['roots'] = [(e.pk, e.get_description().strip(), e.code) for e in
|
|
Entity.objects.filter(depth=app_settings.ENTITY_ROOTS_DEPTH,
|
|
is_active=True)]
|
|
return context
|
|
|
|
def get_prefix(self, *args, **kwargs):
|
|
prefix = super(RequestWizardView, self).get_prefix(*args, **kwargs)
|
|
prefix += '_%s' % kwargs.get('pk', '')
|
|
return prefix
|
|
|
|
def get_form_instance(self, step):
|
|
return self.object
|
|
|
|
def get_step_url(self, step):
|
|
return reverse(self.url_name, kwargs={self.pk_url_kwarg: self.object.pk, 'step': step})
|
|
|
|
def render_next_step(self, form, **kwargs):
|
|
if hasattr(form, 'cleaned_data') and hasattr(form, 'save') and form.is_valid():
|
|
form.save()
|
|
if hasattr(form, 'save_m2m'):
|
|
form.save_m2m()
|
|
# special case when coming from the validation page
|
|
if 'correction' in self.request.GET:
|
|
return redirect(self.get_step_url('validation'))
|
|
pprint_data = form.pprint_data({})
|
|
# do not apply when going to the validation page, that's useless
|
|
if any([x[2] for x in pprint_data]) and self.steps.next != 'validation':
|
|
field_names = [x[0] for x in pprint_data if x[2]]
|
|
messages.warning(self.request,
|
|
_(u"Vous n'avez pas remplis les champs obligatoires suivants: "
|
|
u"%s. Vous pouvez tout de même continuer à remplir votre demande, "
|
|
u"et aussi la sauver comme brouillon. Mais vous ne pourrez pas "
|
|
u"la soumettre tant que ces champs ne seront pas remplis")
|
|
% ', '.join(map(unicode, field_names)))
|
|
return super(RequestWizardView, self).render_next_step(form, **kwargs)
|
|
|
|
def get_form_list(self):
|
|
transitions = get_workflows(self.request.user, self.object) \
|
|
.select_related().filter(action__special_type='edit')
|
|
pages = reduce(set.__or__, [set(transition.action.edit_pages()) for transition in transitions], set())
|
|
return SortedDict([
|
|
(key,value) for key, value in super(RequestWizardView, self).get_form_list().iteritems()
|
|
if key in pages or key == 'validation'])
|
|
|
|
@transaction.commit_on_success
|
|
def post(self, *args, **kwargs):
|
|
"""
|
|
Surcharge de NamedUrlSessionWizardView.post pour enregistrer le
|
|
"""
|
|
wizard_goto_step = self.request.POST.get('wizard_goto_step', None)
|
|
delete = 'delete-uploadfile' in self.request.POST
|
|
if delete or (wizard_goto_step and wizard_goto_step in self.get_form_list()):
|
|
# Check if form was refreshed
|
|
management_form = ManagementForm(self.request.POST, prefix=self.prefix)
|
|
if not management_form.is_valid():
|
|
messages.error(self.request, _(u'Une erreur a eu lieu, veuillez réessayer.'))
|
|
return redirect(self.get_step_url(self.steps.current))
|
|
|
|
form_current_step = management_form.cleaned_data['current_step']
|
|
if (form_current_step != self.steps.current and
|
|
self.storage.current_step is not None):
|
|
# form refreshed, change current step
|
|
self.storage.current_step = form_current_step
|
|
|
|
if delete:
|
|
request = self.object
|
|
if not request.is_paper():
|
|
request.uploadfile.delete()
|
|
request.uploadfile = None
|
|
request.save()
|
|
return redirect(self.get_step_url(form_current_step))
|
|
|
|
# get the form for the current step
|
|
form = self.get_form(data=self.request.POST, files=self.request.FILES)
|
|
|
|
# and try to validate
|
|
if form.is_valid():
|
|
# if the form is valid, store the cleaned data and files.
|
|
self.storage.set_step_data(self.steps.current, self.process_step(form))
|
|
self.storage.set_step_files(self.steps.current, self.process_step_files(form))
|
|
if hasattr(form, 'save'):
|
|
form.save()
|
|
if hasattr(form, 'save_m2m'):
|
|
form.save_m2m()
|
|
self.storage.current_step = wizard_goto_step
|
|
return redirect(self.get_step_url(wizard_goto_step))
|
|
return super(RequestWizardView, self).post(*args, **kwargs)
|
|
|
|
def render(self, form=None, **kwargs):
|
|
from django.forms.models import model_to_dict
|
|
if self.steps.current == 'document_copyrights' and self.object:
|
|
message = None
|
|
if self.object.usage and self.object.usage.no_diffusion:
|
|
message = _(u"L'usage « {0} » n'autorisant pas la diffusion, "
|
|
u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
|
|
if self.object.is_from_remote_request:
|
|
message = _(u"La requête venant d'une plate-forme distante, "
|
|
u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
|
|
if self.object.is_paper():
|
|
message = _(u"La requête ne comportant pas de fichier, "
|
|
u"l'étape « Options de diffusion » a donc été automatiquement sautée.").format(self.object.usage)
|
|
if message:
|
|
messages.info(self.request, message)
|
|
return self.render_next_step(form, **kwargs)
|
|
if hasattr(form, 'instance') and form.instance and not form.is_bound and kwargs.get('step') != 'document_copyrights':
|
|
data = dict(((form.prefix+'-'+k,v) for k,v in model_to_dict(form.instance).iteritems()))
|
|
for choice in form.instance.choices.all().select_related():
|
|
data[form.add_prefix('profile_option_%s' % choice.option.id)] =\
|
|
unicode(choice.id)
|
|
form = self.get_form(data=data)
|
|
form.full_clean()
|
|
return super(RequestWizardView, self).render(form=form, **kwargs)
|
|
|
|
class RequestDetails(DetailView):
|
|
model = Request
|
|
template_name = 'request_detail.html'
|
|
context_object_name = 'poly_request'
|
|
queryset = Request.objects.select_related().prefetch_related('choices', 'base_profile__choices')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super(RequestDetails, self).get_context_data(**kwargs)
|
|
request = self.get_object()
|
|
ctx['docname'] = request.name
|
|
if not request.is_paper():
|
|
ctx['filename'] = os.path.basename(request.uploadfile.name)
|
|
if not ctx['docname']:
|
|
ctx['docname'] = ctx['filename']
|
|
ctx['creation'] = request.history_set.start()
|
|
ctx['end'] = request.history_set.end()
|
|
# Let only one edit transition be displayed
|
|
transitions = []
|
|
already_one_edit_transition = False
|
|
t1 = get_workflows(self.request.user, request) \
|
|
.filter(action__validate_request__lte=request.validate(),
|
|
show_on_detail=True).order_by('action__id')
|
|
# FIXME in Django 1.6
|
|
if t1:
|
|
t1 = t1.distinct('action', 'destination')
|
|
t2 = Transition.objects.filter(id__in=t1) \
|
|
.order_by('-warn', 'default')
|
|
for transition in t2:
|
|
if transition.action.special_type == 'edit':
|
|
if already_one_edit_transition:
|
|
continue
|
|
already_one_edit_transition = True
|
|
transitions.append(transition)
|
|
ctx['workflows'] = transitions
|
|
ctx['use_pdf_viewer'] = app_settings.USE_PDF_VIEWER
|
|
ctx['entities'] = []
|
|
if request.entity is not None:
|
|
def entity_parent(x, kind):
|
|
return getattr(x.entity.parent_of_type(kind), 'description', None)
|
|
for caption, ref in app_settings.ENTITY_TYPE_TO_SHOW:
|
|
what = entity_parent(request, ref)
|
|
if what is None or what == request.entity:
|
|
continue
|
|
ctx['entities'].append((caption, what))
|
|
return ctx
|
|
|
|
class RequestHistory(DetailView):
|
|
model = Request
|
|
template_name = 'request_history.html'
|
|
context_object_name = 'poly_request'
|
|
|
|
@login_required
|
|
@transaction.commit_on_success
|
|
def new_request(request):
|
|
try:
|
|
default_licence = DocumentLicence.objects.get(default=True)
|
|
except (DocumentLicence.DoesNotExist, DocumentLicence.MultipleObjectsReturned):
|
|
default_licence = None
|
|
request = Request.objects.create(
|
|
user=request.user,
|
|
status=get_default_status(),
|
|
contact_email=getattr(request.user, 'email', ''),
|
|
contact_telephone1=getattr(request.user, 'telephoneNumber', ''),
|
|
contact_telephone2=getattr(request.user, 'supannAutreTelephone', ''),
|
|
contact_bureau=getattr(request.user, 'roomNumber', ''),
|
|
sponsor=u'%(first_name)s %(last_name)s (%(username)s)' % request.user._wrapped.__dict__,
|
|
licence=default_licence,
|
|
)
|
|
return redirect('request_wizard', pk=request.pk)
|
|
|
|
@transaction.commit_on_success
|
|
def request_delete(request, pk):
|
|
instance = get_object_or_404(Request, pk=pk)
|
|
instance.delete()
|
|
return redirect('list_request')
|
|
|
|
request_wizard_step = login_required(RequestWizardView.as_view(named_new_request_forms,
|
|
done_step_name='finished', url_name='request_wizard_step'))
|
|
|
|
def upload(request, attached_file):
|
|
response = HttpResponse(attached_file.chunks(), mimetype='application/octet-stream')
|
|
filename = urlquote(
|
|
os.path.basename(attached_file.name).encode('utf-8'))
|
|
response['Content-disposition'] = "attachment; filename*=UTF-8''%s" % filename
|
|
return response
|
|
|
|
def request_download(request, pk):
|
|
instance = get_object_or_404(Request, pk=pk)
|
|
if not instance.uploadfile.name:
|
|
raise Http404
|
|
return upload(request, instance.uploadfile)
|
|
|
|
@transaction.commit_on_success
|
|
def request_action(request, pk, workflow_pk):
|
|
poly_request = get_object_or_404(Request, pk=pk)
|
|
try:
|
|
workflow = get_workflows(request.user, poly_request).get(pk=workflow_pk)
|
|
except Transition.DoesNotExist:
|
|
transition = get_object_or_404(Transition, pk=workflow_pk)
|
|
message = _(u"L'action « {0} » n'est pas autorisée sur la demande {1}.") \
|
|
.format(transition.action.name,
|
|
poly_request.request_number())
|
|
messages.error(request, message)
|
|
if get_workable_requests(request.user).filter(pk=pk).exists():
|
|
return redirect('request_detail', pk=pk)
|
|
else:
|
|
return redirect('list_request')
|
|
if workflow.comment and not request.POST.get('comment'):
|
|
return render(request, 'request_action.html', locals())
|
|
poly_request.act(request.user, workflow, description=request.POST.get('comment'))
|
|
if workflow.action.ui_message:
|
|
messages.info(request, mark_safe(workflow.action.ui_message))
|
|
if workflow.is_edit:
|
|
return redirect('request_wizard', pk=pk)
|
|
elif workflow.is_delete:
|
|
return redirect('request_delete', pk=pk)
|
|
elif workflow.is_duplicate:
|
|
return redirect('request_duplicate', pk=pk)
|
|
return redirect('request_detail', pk=pk)
|
|
|
|
@transaction.commit_on_success
|
|
def request_duplicate(request, pk):
|
|
from django.core.files.base import ContentFile
|
|
poly_request = get_object_or_404(Request, pk=pk)
|
|
new_poly_request = get_object_or_404(Request, pk=pk)
|
|
new_poly_request.pk = None
|
|
new_poly_request.creation_date = now()
|
|
new_poly_request.month_order = -1
|
|
new_poly_request.status = get_default_status()
|
|
if poly_request.uploadfile:
|
|
content = ContentFile(poly_request.uploadfile.read())
|
|
filename = os.path.split(poly_request.uploadfile.name)[-1]
|
|
new_poly_request.uploadfile.save(filename, content)
|
|
poly_request.uploadfile.close()
|
|
new_poly_request.save()
|
|
new_poly_request.choices = poly_request.choices.all()
|
|
return redirect('request_wizard', pk=new_poly_request.pk)
|
|
|
|
class ListRequest(ListView):
|
|
template_name = 'list_request.html'
|
|
model = Request
|
|
paginate_by = app_settings.PAGINATE_BY
|
|
context_object_name = 'requests'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
self.filters = []
|
|
self.filter_choices = []
|
|
self.sort = request.GET.get('sort')
|
|
self.handle_filter_status()
|
|
self.handle_search_filter()
|
|
self.handle_entity_filter()
|
|
self.handle_date_filter()
|
|
self.handle_licence_filter()
|
|
self.handle_free_text_filter()
|
|
return super(ListRequest, self).dispatch(request, *args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
params = {}
|
|
if 'from_date' in request.POST:
|
|
params['from_date'] = request.POST['from_date']
|
|
if 'to_date' in request.POST:
|
|
params['to_date'] = request.POST['to_date']
|
|
return HttpResponseRedirect(self.qs(**params))
|
|
|
|
def get_queryset(self, without_filter=()):
|
|
qs = super(ListRequest, self).get_queryset()
|
|
qs = get_workable_requests(self.request.user)
|
|
# apply filters
|
|
qs = qs.filter(status__visible=True)
|
|
for filter_name, value, display_list in self.filters:
|
|
if filter_name in without_filter:
|
|
continue
|
|
apply_filter = 'apply_filter_%s' % filter_name
|
|
if hasattr(self, apply_filter):
|
|
qs = getattr(self, apply_filter)(qs, value)
|
|
else:
|
|
qs = qs.filter(**{filter_name: value})
|
|
# apply ordering
|
|
if self.sort:
|
|
qs = qs.order_by(self.sort)
|
|
qs = qs.prefetch_related('choices').select_related('base_profile', 'user', 'entity', 'status')
|
|
return qs
|
|
|
|
def handle_filter_status(self):
|
|
current = self.request.GET.get('filter_status')
|
|
choices = []
|
|
if current:
|
|
display = Status.objects.get(id=current).name
|
|
self.filters.append(('status', current, [(display, self.qs(**{'status': None}))]))
|
|
choices = Status.objects \
|
|
.filter(request__in=self.get_queryset(without_filter=('status',))) \
|
|
.distinct().values_list('id', 'name')
|
|
choices = [
|
|
(self.qs(**{'status': status}), display)
|
|
for status, display in choices
|
|
]
|
|
if choices:
|
|
self.filter_choices.append({
|
|
'caption': _(u'Sur le statut'),
|
|
'choices': choices })
|
|
|
|
def handle_search_filter(self):
|
|
current = self.request.GET.get('filter_search')
|
|
if current:
|
|
self.filters.append(('search', current, [_(u'Contient le texte %s') % current,
|
|
self.qs(**{'search': None})]))
|
|
|
|
def apply_filter_search(sefl, qs, value):
|
|
q = Q(uploadfile__contains=value) | Q(name__contains=value) \
|
|
| Q(user__username__contains=value)
|
|
return qs.filter(q)
|
|
|
|
def handle_entity_filter(self,
|
|
min_depth=app_settings.MIN_ENTITY_FILTER_DEPTH,
|
|
max_depth=app_settings.MAX_ENTITY_FILTER_DEPTH):
|
|
value = self.request.GET.get('filter_entity')
|
|
qs = Entity.objects \
|
|
.filter(depth__lte=max_depth, depth__gte=min_depth) \
|
|
.filter(get_entity_filter(self.request.user))
|
|
if False:
|
|
request_table = qs.query.join((None, Request._meta.db_table, None, None),
|
|
promote=True, always_create=True)
|
|
entity_table2 = qs.query.join((request_table, Entity._meta.db_table,
|
|
"entity_id", "id"))
|
|
qs = qs.extra(where=[
|
|
'{0}.left_bound <= {1}.left_bound'.format(
|
|
Entity._meta.db_table, entity_table2),
|
|
'{0}.right_bound >= {1}.right_bound'.format(
|
|
Entity._meta.db_table, entity_table2)])
|
|
qs = qs.order_by('name')
|
|
by_code = dict([(entity.pk, entity) for entity in qs])
|
|
current = None
|
|
path = []
|
|
if value:
|
|
path = value.split(',')
|
|
path_entities = []
|
|
for entity_code in path:
|
|
try:
|
|
entity_code = int(entity_code)
|
|
except ValueError:
|
|
return
|
|
entity = by_code.get(entity_code)
|
|
if not entity:
|
|
return
|
|
path_entities.append(entity)
|
|
current = path_entities[-1]
|
|
display_list = [ (_(u'Composante %s') % path_entities[0].name,
|
|
self.qs(**{'entity': None}))]
|
|
delete_value = path[0]
|
|
for entity in path_entities[1:]:
|
|
display_list.append((entity.get_name(),
|
|
self.qs(**{'entity': delete_value})))
|
|
delete_value = '%s,%s' % (delete_value, entity.pk)
|
|
self.filters.append(('entity', current, display_list))
|
|
parent = None
|
|
if len(path) > 1:
|
|
parent = by_code.get(path[-2])
|
|
if current:
|
|
if current.depth == max_depth or not current.children().exists():
|
|
depth = current.depth
|
|
path = path[:-1]
|
|
else:
|
|
depth = current.depth+1
|
|
else:
|
|
depth = min_depth
|
|
path = []
|
|
# filter on depth
|
|
choices = [entity for entity in by_code.itervalues() if entity.depth==depth]
|
|
# filter on parenting
|
|
if parent:
|
|
choices = [entity for entity in choices if
|
|
entity.left_bound>=parent.left_bound and
|
|
entity.right_bound<=parent.right_bound]
|
|
# create filter choices
|
|
choices = sorted(choices, key=lambda entity: entity.get_description().title())
|
|
choices = [(self.qs(**{'entity': ','.join(path+[str(entity.pk)])}),
|
|
entity.get_description().title()) for entity in choices if entity != current]
|
|
if choices:
|
|
self.filter_choices.append({
|
|
'caption': _('Sur le service ou la composante'),
|
|
'choices': choices,
|
|
})
|
|
|
|
def apply_filter_entity(self, qs, value):
|
|
return qs.filter(entity__left_bound__gte=value.left_bound,
|
|
entity__right_bound__lte=value.right_bound)
|
|
|
|
def get_filters_field(self, field, caption):
|
|
result = {}
|
|
result['caption'] = caption
|
|
current = self.filters.get(field)
|
|
values = self.get_queryset().values_list(field, flat=True).distinct()
|
|
if current:
|
|
choices = [({field: None}, _('< Tous')), (None, values[0])]
|
|
else:
|
|
if not values:
|
|
return []
|
|
choices = [({field: a}, a) for a in values]
|
|
choices = [ (self.qs(**a) if a else a, b) for a, b in choices ]
|
|
result['choices'] = choices
|
|
return [result]
|
|
|
|
def qs(self, **kwargs):
|
|
params = dict()
|
|
for k in self.request.GET:
|
|
params[k] = self.request.GET[k]
|
|
for k,v in kwargs.iteritems():
|
|
k = 'filter_%s' % k
|
|
if v is None:
|
|
params.pop(k, None)
|
|
else:
|
|
params[k] = v
|
|
return '?%s' % urlencode(params)
|
|
|
|
def handle_date_filter(self):
|
|
from_date = self.request.GET.get('filter_from_date')
|
|
to_date = self.request.GET.get('filter_to_date')
|
|
if from_date:
|
|
try:
|
|
from_date = datetime.datetime.strptime(from_date, '%Y-%m-%d').date()
|
|
except ValueError:
|
|
from_date = None
|
|
if to_date:
|
|
try:
|
|
to_date = datetime.datetime.strptime(to_date, '%Y-%m-%d').date()
|
|
except ValueError:
|
|
to_date = None
|
|
display_list = []
|
|
if from_date:
|
|
display_list.append(
|
|
(_(u'À livrer après le %s') % from_date, self.qs(**{'from_date': None})))
|
|
if to_date:
|
|
display_list.append(
|
|
(_(u'À livrer avant le %s') % to_date, self.qs(**{'to_date': None})))
|
|
if display_list:
|
|
self.filters.append(('date', (from_date, to_date), display_list))
|
|
|
|
def apply_filter_date(self, qs, value):
|
|
from_date, to_date = value
|
|
if from_date:
|
|
qs = qs.filter(delivery_date__gte=from_date)
|
|
if to_date:
|
|
qs = qs.filter(delivery_date__lte=to_date)
|
|
return qs
|
|
|
|
def handle_licence_filter(self):
|
|
licences = DocumentLicence.objects.all()
|
|
choices = [(self.qs(**{'licence': licence.id}), licence.name) for licence in licences]
|
|
self.filter_choices.append({
|
|
'caption': _('Autorisation de diffusion'),
|
|
'choices': choices})
|
|
licence = self.request.GET.get('filter_licence')
|
|
try:
|
|
licence = DocumentLicence.objects.get(id=int(licence))
|
|
except (TypeError, ValueError, DocumentLicence.DoesNotExist):
|
|
pass
|
|
else:
|
|
self.filters.append(('licence',
|
|
licence, ((_(u'Diffusion: %s') % licence.name,
|
|
self.qs(**{'licence': None})),)))
|
|
|
|
def handle_free_text_filter(self):
|
|
text = self.request.GET.get('filter_free_text')
|
|
if text:
|
|
self.filters.append(('free_text', text,
|
|
((_(u'Contenant: %s') % text,
|
|
self.qs(**{'free_text': None})),)))
|
|
|
|
|
|
def apply_filter_free_text(self, qs, value):
|
|
for elt in value.split():
|
|
m = Request.NUMBER_RE.match(elt)
|
|
if m:
|
|
year, month, month_order = m.groups()
|
|
qs = qs.filter(creation_date__year=int(year),
|
|
creation_date__month=int(month),
|
|
month_order=int(month_order))
|
|
continue
|
|
m = Request.FINANCIAL_CODE_RE.match(elt)
|
|
if m:
|
|
financial_code = m.group()
|
|
qs = qs.filter(financial_code=financial_code)
|
|
continue
|
|
qs = qs.filter(Q(entity__description__icontains=elt)
|
|
|Q(entity__description_override__icontains=elt)
|
|
|Q(entity__code__icontains=elt)
|
|
|Q(user__username__icontains=elt)
|
|
|Q(user__first_name__icontains=elt)
|
|
|Q(user__last_name__icontains=elt)
|
|
|Q(user__email__icontains=elt)
|
|
|Q(sponsor__icontains=elt)
|
|
|Q(name__icontains=elt)
|
|
|Q(contact_email__icontains=elt))
|
|
return qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super(ListRequest, self).get_context_data(**kwargs)
|
|
ctx['sort'] = self.sort
|
|
ctx['filters'] = self.filters
|
|
ctx['filter_choices'] = self.filter_choices
|
|
ctx['total_cost'] = self.get_queryset().total_cost()
|
|
ctx['qs'] = self.qs()
|
|
ctx['now'] = now().strftime('%Y-%m-%d')
|
|
ctx['csv_qs'] = self.qs(**{'csv': 1})
|
|
return ctx
|
|
|
|
class CSVListRequest(ListRequest):
|
|
def get(self, request, *args, **kwargs):
|
|
response = HttpResponse(content_type="text/csv")
|
|
response['Content-disposition'] = 'attachment; filename=listing.csv'
|
|
writer = csv.writer(response, delimiter=';')
|
|
response.write(u''.encode('utf_8_sig'))
|
|
fields = [('Numéro de demande', 'request_number_csv'),
|
|
('Commanditaire', 'sponsor_display'),
|
|
'name',
|
|
'uploadfile',
|
|
'nb_pages',
|
|
'usage__name',
|
|
'licence__name',
|
|
'copyright',
|
|
'comments',
|
|
'sponsor',
|
|
('Entité', 'entity__description')]
|
|
def loader(kind):
|
|
def entity_parent(x):
|
|
return getattr(x.entity.parent_of_type(kind), 'description', None)
|
|
return entity_parent
|
|
for caption, ref in app_settings.ENTITY_TYPE_TO_SHOW:
|
|
fields.append((caption.encode('utf-8'), loader(ref)))
|
|
fields += [
|
|
'copies',
|
|
'status__name',
|
|
'base_profile__name',
|
|
'details',
|
|
'delivery_date',
|
|
'delivery_place__name',
|
|
'comments',
|
|
'creation_date',
|
|
'modification_date',
|
|
'contact_email',
|
|
'contact_telephone1',
|
|
'contact_telephone2',
|
|
'contact_bureau',
|
|
'financial_code',
|
|
'financial_comment',
|
|
'estimated_cost',
|
|
'cost' ]
|
|
normalized_fields = []
|
|
for field in fields:
|
|
if isinstance(field, tuple):
|
|
caption, ref = field
|
|
else:
|
|
caption, ref = field, field
|
|
if callable(ref):
|
|
pass
|
|
elif callable(getattr(Request, ref, None)):
|
|
ref = getattr(Request, ref)
|
|
normalized_fields.append((caption, ref))
|
|
# Write headers
|
|
writer.writerow([field[0] for field in normalized_fields])
|
|
qs = self.get_queryset()
|
|
value_fields = [field[1] for field in normalized_fields if not callable(field[1])]
|
|
for instance, values in zip(qs, qs.values(*value_fields)):
|
|
row = []
|
|
for caption, ref in normalized_fields:
|
|
if callable(ref):
|
|
cell = ref(instance)
|
|
else:
|
|
cell = values[ref]
|
|
if cell is None:
|
|
cell = _(u'Aucun(e)')
|
|
try:
|
|
cell = float(cell)
|
|
cell = unicode(cell)
|
|
cell = cell.replace('.',',')
|
|
except:
|
|
pass
|
|
# try to fix newlines for excel
|
|
cell.replace('\r\n', '\n')
|
|
cell.replace('\r', '')
|
|
row.append(unicode(cell).encode('utf-8'))
|
|
writer.writerow(row)
|
|
return response
|
|
|
|
def autocomplete(request, field_names=('name',)):
|
|
completion_data = field_completion(request.user, field_names, without_scores='without_scores' in request.GET)
|
|
response = HttpResponse(json.dumps(completion_data), mimetype='application/javascript')
|
|
return response
|
|
|
|
@csrf_exempt
|
|
def remote_request(request):
|
|
contents = []
|
|
entity = None
|
|
if 'uploadfile' not in request.FILES:
|
|
return HttpResponseBadRequest(u'paramètres invalides', content_type='text/plain')
|
|
if 'username' in request.POST and \
|
|
not User.objects.filter(username=request.POST['username']).exists():
|
|
contents.append(u"l'utilisateur « %s » n'est pas enregistré dans polynum" % request.POST['username'])
|
|
if 'entity_hint' in request.POST:
|
|
entities = Entity.objects.filter(code=request.POST['entity_hint'].lower())
|
|
if entities:
|
|
entity = entities[0]
|
|
else:
|
|
contents.append(u'entité « %s » inconnue' % request.POST['entity_hint'])
|
|
else:
|
|
entity = None
|
|
try:
|
|
utils.check_pdf(request.FILES['uploadfile'])
|
|
except ValueError, e:
|
|
return HttpResponseBadRequest(e.args[0], content_type='text/plain')
|
|
request.FILES['uploadfile'].seek(0)
|
|
temp_remote_request = models.RemoteRequest.objects.create(
|
|
uploadfile=request.FILES['uploadfile'], entity=entity)
|
|
response = redirect('finish_remote_request', pk=temp_remote_request.pk)
|
|
response['Content-Type'] = 'text/plain'
|
|
response.content = u''.join(contents)
|
|
return response
|
|
|
|
@transaction.commit_on_success
|
|
def finish_remote_request(request, pk):
|
|
temp_remote_request = get_object_or_404(models.RemoteRequest, pk=pk)
|
|
document = Request.objects.create(user=request.user,
|
|
status=get_default_status(),
|
|
entity=temp_remote_request.entity,
|
|
is_from_remote_request=True)
|
|
document.uploadfile.save(os.path.basename(temp_remote_request.uploadfile.name),
|
|
temp_remote_request.uploadfile, save=False)
|
|
utils.fill_document_attributes_from_pdf_file(document, document.uploadfile)
|
|
document.clean()
|
|
document.save()
|
|
temp_remote_request.delete()
|
|
return redirect('request_wizard', pk=document.pk)
|
|
|