#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2011 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # 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 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 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 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('furnitures.view_person', models.Person) and \ not request.user.has_perm('furnitures.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', 15) try: limit = int(limit) except ValueError: return HttpResponseBadRequest() query = Q() for q in q.split(' '): query = query & (Q(name__istartswith=q) | Q(surname__istartswith=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 = 15 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 = 15 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('furnitures.view_file', models.File) and \ not request.user.has_perm('furnitures.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 = 15 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') def get_item(model, func_name, default_name, extra_request_keys=[]): """ Generic treatment of tables """ def func(request, data_type='json', **dct): 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) dct = {} for k in request_keys: q = request.GET.get(k) if not q: continue dct[request_keys[k]] = q if not dct and 'submited' not in request.GET: if default_name in request.session: dct = {"pk":request.session[default_name]} elif func_name in request.session: dct = request.session[func_name] if not dct: return HttpResponse(mimetype='text/plain') else: request.session[func_name] = dct query = Q(**dct) items = model.objects.filter(query) q = request.GET.get('sidx') if q and q in request_keys: k = request_keys[q] if k.endswith("__pk"): k = k[:-len("__pk")] + "__label" q = request.GET.get('sord') sign = q and q == u'desc' and "-" or '' items = items.order_by(sign + k) datas = [] for item in items: data = [item.pk] for k in model.TABLE_COLS: val = item for ky in k.split('.'): if val: val = getattr(val, ky) if hasattr(val, 'all'): # manage related objects data.append(", ".join([v and unicode(v) or u"" for v in getattr(val, 'all')()])) else: data.append(val and unicode(val) or u"") datas.append(data) link_template = "%s" % \ (unicode(_("Details"))) if data_type == "json": rows = [] for data in datas: res = {'id':data[0], 'link':link_template % reverse('show-'+default_name, args=[data[0], ''])} 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') 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(']*)>\n', '', 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.html' % name) content = tpl.render(context_instance) result = StringIO.StringIO() pdf = pisa.pisaDocument(StringIO.StringIO(content.encode("utf-8")), 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 get_file = get_item(models.File, 'get_file', 'file') show_file = show_item(models.File, 'file') def autocomplete_operation(request, non_closed=True): if not request.user.has_perm('furnitures.view_operation', models.Operation)\ and not request.user.has_perm('furnitures.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') get_administrativeact = get_item(models.AdministrativeAct, 'get_administrativeact', 'administrativeact', extra_request_keys={'associated_file__towns':'associated_file__towns__pk', 'operation__towns':'operation__towns__pk'}) def autocomplete_organization(request, orga_type=None): if not request.user.has_perm('furnitures.view_organization', models.Organization) and \ not request.user.has_perm('furnitures.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') 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))