diff options
Diffstat (limited to 'ishtar/ishtar_base/views.py')
-rw-r--r-- | ishtar/ishtar_base/views.py | 581 |
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(" ", " ") + 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)) + |