summaryrefslogtreecommitdiff
path: root/ishtar/ishtar_base/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar/ishtar_base/views.py')
-rw-r--r--ishtar/ishtar_base/views.py581
1 files changed, 581 insertions, 0 deletions
diff --git a/ishtar/ishtar_base/views.py b/ishtar/ishtar_base/views.py
new file mode 100644
index 000000000..9e998c2dc
--- /dev/null
+++ b/ishtar/ishtar_base/views.py
@@ -0,0 +1,581 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+"""
+Furnitures views
+"""
+
+import tidy
+import re
+import csv
+import json
+import datetime
+import optparse
+import cStringIO as StringIO
+from tempfile import NamedTemporaryFile
+import ho.pisa as pisa
+
+from django.http import HttpResponse, Http404
+from django.template import RequestContext, loader
+from django.template.defaultfilters import slugify
+from django.shortcuts import render_to_response, redirect
+from django.utils.translation import ugettext, ugettext_lazy as _
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.db.models import Q
+from django.core import serializers
+
+from ishtar import settings
+if settings.XHTML2ODT_PATH:
+ import sys
+ sys.path.append(settings.XHTML2ODT_PATH)
+ from xhtml2odt import xhtml2odt
+
+from menus import menu
+import forms_main as ishtar_forms
+import models
+
+CSV_OPTIONS = {'delimiter':';', 'quotechar':'"', 'quoting':csv.QUOTE_ALL}
+ENCODING = settings.ENCODING or 'utf-8'
+
+def index(request):
+ """
+ Main page
+ """
+ dct = {}
+ return render_to_response('index.html', dct,
+ context_instance=RequestContext(request))
+
+def update_current_item(request):
+ if not request.is_ajax() and not request.method == 'POST':
+ raise Http404
+ if 'value' in request.POST and 'item' in request.POST:
+ request.session[request.POST['item']] = request.POST['value']
+ return HttpResponse('ok')
+
+def check_permission(request, action_slug, obj_id=None):
+ if action_slug not in menu.items:
+ #! TODO
+ return True
+ if obj_id:
+ return menu.items[action_slug].is_available(request.user, obj_id)
+ return menu.items[action_slug].can_be_available(request.user)
+
+def autocomplete_person(request, person_type=None):
+ if not request.user.has_perm('ishtar_base.view_person', models.Person) and \
+ not request.user.has_perm('ishtar_base.view_own_person', models.Person) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ limit = request.GET.get('limit', 20)
+ try:
+ limit = int(limit)
+ except ValueError:
+ return HttpResponseBadRequest()
+ query = Q()
+ for q in q.split(' '):
+ query = query & (Q(name__icontains=q) | Q(surname__icontains=q) | \
+ Q(email__icontains=q))
+ if person_type:
+ try:
+ typs = [int(tp) for tp in person_type.split('_') if tp]
+ typ = models.PersonType.objects.filter(pk__in=typs).all()
+ query = query & Q(person_type__in=typ)
+ except (ValueError, ObjectDoesNotExist):
+ pass
+ limit = 20
+ persons = models.Person.objects.filter(query)[:limit]
+ data = json.dumps([{'id':person.pk, 'value':unicode(person)}
+ for person in persons if person])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_town(request):
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q)
+ if settings.COUNTRY == 'fr':
+ extra = (extra | Q(numero_insee__istartswith=q) | \
+ Q(departement__label__istartswith=q))
+ query = query & extra
+ limit = 20
+ towns = models.Town.objects.filter(query)[:limit]
+ data = json.dumps([{'id':town.pk, 'value':unicode(town)}
+ for town in towns])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_file(request):
+ if not request.user.has_perm('ishtar_base.view_file', models.File) and \
+ not request.user.has_perm('ishtar_base.view_own_file', models.File) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(internal_reference__icontains=q) | \
+ Q(towns__name__icontains=q)
+ try:
+ value = int(q)
+ extra = extra | Q(year=q) | Q(numeric_reference=q)
+ except ValueError:
+ pass
+ query = query & extra
+ limit = 20
+ files = models.File.objects.filter(query)[:limit]
+ data = json.dumps([{'id':file.pk, 'value':unicode(file)}
+ for file in files])
+ return HttpResponse(data, mimetype='text/plain')
+
+from types import NoneType
+
+def format_val(val):
+ if type(val) == NoneType:
+ return u""
+ if type(val) == bool:
+ if val:
+ return unicode(_(u"True"))
+ else:
+ return unicode(_(u"False"))
+ return unicode(val)
+
+HIERARCHIC_LEVELS = 5
+HIERARCHIC_FIELDS = ['period', 'unit', 'material']
+def get_item(model, func_name, default_name, extra_request_keys=[],
+ bool_fields=[]):
+ """
+ Generic treatment of tables
+ """
+ def func(request, data_type='json', **dct):
+ if 'type' in dct:
+ data_type = dct.pop('type')
+ if not data_type:
+ data_type = 'json'
+ fields = [model._meta.get_field_by_name(k)[0]
+ for k in model._meta.get_all_field_names()]
+ request_keys = dict([(field.name,
+ field.name + (hasattr(field, 'rel') and field.rel and '__pk' or ''))
+ for field in fields])
+ request_keys.update(extra_request_keys)
+ request_items = request.method == 'POST' and request.POST or request.GET
+ dct = {}
+ try:
+ old = 'old' in request_items and int(request_items['old'])
+ except ValueError:
+ return HttpResponse(None, mimetype='text/plain')
+ for k in request_keys:
+ q = request_items.get(k)
+ if not q:
+ continue
+ dct[request_keys[k]] = q
+ if not dct and 'submited' not in request_items:
+ if default_name in request.session and \
+ request.session[default_name]:
+ dct = {"pk":request.session[default_name]}
+ if (not dct or data_type == 'csv') and func_name in request.session:
+ dct = request.session[func_name]
+ else:
+ request.session[func_name] = dct
+ for k in bool_fields:
+ if k in dct:
+ if dct[k] == u"1":
+ dct.pop(k)
+ else:
+ dct[k] = dct[k] == u"2" and True or False
+ # manage hierarchic conditions
+ or_reqs = []
+ for k in HIERARCHIC_FIELDS:
+ for req in dct.copy():
+ if req.endswith(k + '__pk'):
+ val = dct.pop(req)
+ reqs = Q(**{req:val})
+ req = req[:-2] + '__'
+ for idx in xrange(HIERARCHIC_LEVELS):
+ req = req[:-2] + 'parent__pk'
+ q = Q(**{req:val})
+ reqs = reqs | q
+ or_reqs.append(reqs)
+ query = Q(**dct)
+ for or_req in or_reqs:
+ query = query & or_req
+ items = model.objects.filter(query)
+ q = request_items.get('sidx')
+ # manage tables
+ if q and q in request_keys:
+ k = request_keys[q]
+ if k.endswith("__pk"):
+ k = k[:-len("__pk")] + "__label"
+ q = request_items.get('sord')
+ sign = q and q == u'desc' and "-" or ''
+ items = items.order_by(sign + k)
+ datas = []
+ if old:
+ items = [item.get_previous(old) for item in items]
+ for item in items:
+ data = [item.pk]
+ for k in model.TABLE_COLS:
+ vals = [item]
+ for ky in k.split('.'):
+ new_vals = []
+ for val in vals:
+ if hasattr(val, 'all'): # manage related objects
+ val = list(val.all())
+ for v in val:
+ new_vals.append(getattr(v, ky))
+ elif val:
+ new_vals.append(getattr(val, ky))
+ vals = new_vals
+ if vals and hasattr(vals[0], 'all'): # manage last related objects
+ new_vals = []
+ for val in vals:
+ new_vals += list(val.all())
+ vals = new_vals
+ data.append(", ".join([format_val(v) for v in vals]) or u"")
+ datas.append(data)
+ link_template = "<a href='#' onclick='load_window(\"%%s\")'>%s</a>" % \
+ (unicode(_("Details")))
+ if data_type == "json":
+ rows = []
+ for data in datas:
+ try:
+ lnk = link_template % reverse('show-'+default_name,
+ args=[data[0], ''])
+ except NoReverseMatch:
+ lnk = ''
+ res = {'id':data[0], 'link':lnk}
+ for idx, value in enumerate(data[1:]):
+ if value:
+ res[model.TABLE_COLS[idx].split('.')[-1]] = value
+ rows.append(res)
+ data = json.dumps({
+ "records":len(items),
+ "rows":rows
+ })
+ return HttpResponse(data, mimetype='text/plain')
+ elif data_type == "csv":
+ response = HttpResponse(mimetype='text/csv')
+ n = datetime.datetime.now()
+ filename = u'%s_%s.csv' % (default_name,
+ n.strftime('%Y%m%d-%H%M%S'))
+ response['Content-Disposition'] = 'attachment; filename=%s'%filename
+ writer = csv.writer(response, **CSV_OPTIONS)
+ col_names = []
+ for field_name in model.TABLE_COLS:
+ try:
+ field = model._meta.get_field(field_name)
+ except:
+ col_names.append(u"".encode(ENCODING))
+ continue
+ col_names.append(unicode(field.verbose_name).encode(ENCODING))
+ writer.writerow(col_names)
+ for data in datas:
+ writer.writerow([val.encode(ENCODING) for val in data[1:]])
+ return response
+ return HttpResponse(None, mimetype='text/plain')
+
+ return func
+
+def show_item(model, name):
+ def func(request, pk, **dct):
+ try:
+ item = model.objects.get(pk=pk)
+ except ObjectDoesNotExist:
+ return HttpResponse(None)
+ doc_type = 'type' in dct and dct.pop('type')
+ date = 'date' in dct and dct.pop('date')
+ dct['window_id'] = "%s-%d-%s" % (name, item.pk,
+ datetime.datetime.now().strftime('%M%s'))
+ if date:
+ try:
+ date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f')
+ item = item.get_previous(date=date)
+ assert item != None
+ except (ValueError, AssertionError):
+ return HttpResponse(None, mimetype='text/plain')
+ dct['previous'] = item._previous
+ dct['next'] = item._next
+ else:
+ historized = item.history.all()
+ if historized:
+ item.history_date = historized[0].history_date
+ if len(historized) > 1:
+ dct['previous'] = historized[1].history_date
+ dct['item'], dct['item_name'] = item, name
+ context_instance = RequestContext(request)
+ context_instance.update(dct)
+ n = datetime.datetime.now()
+ filename = u'%s_%s_%s' % (name, slugify(unicode(item)),
+ n.strftime('%Y%m%d-%H%M%S'))
+ if doc_type == "odt" and settings.XHTML2ODT_PATH and \
+ settings.ODT_TEMPLATE:
+ tpl = loader.get_template('sheet_%s.html' % name)
+ content = tpl.render(context_instance)
+ try:
+ tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1,
+ tidy_mark=0, output_encoding='utf8', doctype='auto',
+ wrap=0, char_encoding='utf8')
+ html = str(tidy.parseString(content.encode('utf-8'),
+ **tidy_options))
+ html = html.replace("&nbsp;", "&#160;")
+ html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html)
+
+ odt = NamedTemporaryFile()
+ options = optparse.Values()
+ options.with_network = True
+ for k, v in (('input', ''),
+ ('output', odt.name),
+ ('template', settings.ODT_TEMPLATE),
+ ('with_network', True),
+ ('top_header_level', 1),
+ ('img_width', '8cm'),
+ ('img_height', '6cm'),
+ ('verbose', False),
+ ('replace_keyword', 'ODT-INSERT'),
+ ('cut_start', 'ODT-CUT-START'),
+ ('htmlid', None),
+ ('url', "#")):
+ setattr(options, k, v)
+ odtfile = xhtml2odt.ODTFile(options)
+ odtfile.open()
+ odtfile.import_xhtml(html)
+ odtfile = odtfile.save()
+ except xhtml2odt.ODTExportError, ex:
+ return HttpResponse(content, content_type="application/xhtml")
+ response = HttpResponse(
+ mimetype='application/vnd.oasis.opendocument.text')
+ response['Content-Disposition'] = 'attachment; filename=%s.odt' % \
+ filename
+ response.write(odtfile)
+ return response
+ elif doc_type == 'pdf':
+ tpl = loader.get_template('sheet_%s_pdf.html' % name)
+ content = tpl.render(context_instance)
+ result = StringIO.StringIO()
+ html = content.encode('utf-8')
+ html = html.replace("<table", "<pdf:nextpage/><table repeat='1'")
+ pdf = pisa.pisaDocument(StringIO.StringIO(html), result)
+ response = HttpResponse(result.getvalue(),
+ mimetype='application/pdf')
+ response['Content-Disposition'] = 'attachment; filename=%s.pdf' % \
+ filename
+ if not pdf.err:
+ return response
+ return HttpResponse(content, content_type="application/xhtml")
+ else:
+ tpl = loader.get_template('sheet_%s_window.html' % name)
+ content = tpl.render(context_instance)
+ return HttpResponse(content, content_type="application/xhtml")
+ return func
+
+def revert_item(model):
+ def func(request, pk, date, **dct):
+ try:
+ item = model.objects.get(pk=pk)
+ date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f')
+ item.rollback(date)
+ except (ObjectDoesNotExist, ValueError, HistoryError):
+ return HttpResponse(None, mimetype='text/plain')
+ return HttpResponse("True", mimetype='text/plain')
+ return func
+
+
+get_file = get_item(models.File, 'get_file', 'file')
+show_file = show_item(models.File, 'file')
+revert_file = revert_item(models.File)
+
+def autocomplete_operation(request, non_closed=True):
+ if not request.user.has_perm('ishtar_base.view_operation', models.Operation)\
+ and not request.user.has_perm('ishtar_base.view_own_operation',
+ models.Operation):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(towns__name__icontains=q)
+ try:
+ value = int(q)
+ extra = extra | Q(year=q) | Q(operation_code=q)
+ except ValueError:
+ pass
+ query = query & extra
+ if non_closed:
+ query = query & Q(end_date__isnull=True)
+ limit = 15
+ operations = models.Operation.objects.filter(query)[:limit]
+ data = json.dumps([{'id':operation.pk, 'value':unicode(operation)}
+ for operation in operations])
+ return HttpResponse(data, mimetype='text/plain')
+
+get_operation = get_item(models.Operation, 'get_operation', 'operation')
+show_operation = show_item(models.Operation, 'operation')
+revert_operation = revert_item(models.Operation)
+
+get_administrativeactfile = get_item(models.AdministrativeAct,
+ 'get_administrativeactfile', 'administrativeactfile',
+ extra_request_keys={'associated_file__towns':'associated_file__towns__pk',
+ 'operation__towns':'operation__towns__pk',
+ 'act_type__intented_to':'act_type__intented_to'})
+get_administrativeactop = get_item(models.AdministrativeAct,
+ 'get_administrativeactop', 'administrativeactop',
+ extra_request_keys={'associated_file__towns':'associated_file__towns__pk',
+ 'operation__towns':'operation__towns__pk',
+ 'act_type__intented_to':'act_type__intented_to'})
+
+def autocomplete_organization(request, orga_type=None):
+ if not request.user.has_perm('ishtar_base.view_organization',
+ models.Organization) and \
+ not request.user.has_perm('ishtar_base.view_own_organization',
+ models.Organization):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q)
+ query = query & extra
+ if orga_type:
+ try:
+ typs = [int(tp) for tp in orga_type.split('_') if tp]
+ typ = models.OrganizationType.objects.filter(pk__in=typs).all()
+ query = query & Q(organization_type__in=typ)
+ except (ValueError, ObjectDoesNotExist):
+ pass
+ limit = 15
+ organizations = models.Organization.objects.filter(query)[:limit]
+ data = json.dumps([{'id':org.pk, 'value':unicode(org)}
+ for org in organizations])
+ return HttpResponse(data, mimetype='text/plain')
+
+show_contextrecord = show_item(models.ContextRecord, 'contextrecord')
+get_contextrecord = get_item(models.ContextRecord,
+ 'get_contextrecord', 'contextrecord',
+ extra_request_keys={'parcel__town':'parcel__town__pk',
+ 'operation__year':'operation__year__contains',
+ 'datings__period':'datings__period__pk'},)
+get_archaeologicalitem = get_item(models.Item,
+ 'get_archaeologicalitem', 'item',
+ bool_fields = ['base_items__is_isolated'],
+ extra_request_keys={
+'base_items__context_record__parcel__town':
+ 'base_items__context_record__parcel__town',
+'base_items__context_record__operation__year':
+ 'base_items__context_record__operation__year__contains',
+'base_items__context_record__operation__code_patriarche':
+ 'base_items__context_record__operation__code_patriarche',
+'dating__period':'dating__period__pk',
+'base_items__item__description':'base_items__item__description__icontains',
+'base_items__is_isolated':'base_items__is_isolated'})
+
+def autocomplete_warehouse(request):
+ if not request.user.has_perm('ishtar_base.view_warehouse', models.Warehouse)\
+ and not request.user.has_perm('ishtar_base.view_own_warehouse',
+ models.Warehouse) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q) | \
+ Q(warehouse_type__label__icontains=q)
+ query = query & extra
+ limit = 15
+ warehouses = models.Warehouse.objects.filter(query)[:limit]
+ data = json.dumps([{'id':warehouse.pk, 'value':unicode(warehouse)}
+ for warehouse in warehouses])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_author(request):
+ if not request.user.has_perm('ishtar_base.view_author', models.Author)\
+ and not request.user.has_perm('ishtar_base.view_own_author',
+ models.Warehouse) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(person__name__icontains=q) | \
+ Q(person__surname__icontains=q) | \
+ Q(person__email__icontains=q) | \
+ Q(author_type__label__icontains=q)
+ query = query & extra
+ limit = 15
+ authors = models.Author.objects.filter(query)[:limit]
+ data = json.dumps([{'id':author.pk, 'value':unicode(author)}
+ for author in authors])
+ return HttpResponse(data, mimetype='text/plain')
+
+def new_item(model):
+ def func(request, parent_name):
+ model_name = model._meta.object_name
+ if not check_permission(request, 'add_'+model_name.lower()):
+ not_permitted_msg = ugettext(u"Operation not permitted.")
+ return HttpResponse(not_permitted_msg)
+ frm = getattr(ishtar_forms, model_name + 'Form')
+ dct = {'title':unicode(_(u'New %s' % model_name.lower()))}
+ if request.method == 'POST':
+ dct['form'] = frm(request.POST)
+ if dct['form'].is_valid():
+ new_item = dct['form'].save(request.user)
+ dct['new_item_label'] = unicode(new_item)
+ dct['new_item_pk'] = new_item.pk
+ dct['parent_name'] = parent_name
+ dct['parent_pk'] = parent_name
+ if '_select_' in dct['parent_pk']:
+ parents = dct['parent_pk'].split('_')
+ dct['parent_pk'] = "_".join([parents[0]] + parents[2:])
+ return render_to_response('window.html', dct,
+ context_instance=RequestContext(request))
+ else:
+ dct['form'] = frm()
+ return render_to_response('window.html', dct,
+ context_instance=RequestContext(request))
+ return func
+
+new_warehouse = new_item(models.Warehouse)
+new_person = new_item(models.Person)
+new_organization = new_item(models.Organization)
+new_author = new_item(models.Author)
+
+def action(request, action_slug, obj_id=None, *args, **kwargs):
+ """
+ Action management
+ """
+ if not check_permission(request, action_slug, obj_id):
+ not_permitted_msg = ugettext(u"Operation not permitted.")
+ return HttpResponse(not_permitted_msg)
+ request.session['CURRENT_ACTION'] = action_slug
+ associated_wizard = action_slug + '_wizard'
+ dct = {}
+ globals_dct = globals()
+ if action_slug in globals_dct:
+ return globals_dct[action_slug](request, dct, obj_id, *args, **kwargs)
+ elif hasattr(ishtar_forms, action_slug + "_wizard"):
+ return getattr(ishtar_forms, action_slug+"_wizard")(request, *args,
+ **kwargs)
+ return render_to_response('index.html', dct,
+ context_instance=RequestContext(request))
+