diff options
Diffstat (limited to 'ishtar_common/views.py')
| -rw-r--r-- | ishtar_common/views.py | 1333 | 
1 files changed, 1333 insertions, 0 deletions
diff --git a/ishtar_common/views.py b/ishtar_common/views.py new file mode 100644 index 000000000..9b06276f8 --- /dev/null +++ b/ishtar_common/views.py @@ -0,0 +1,1333 @@ +#!/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. + +try: +    import tidy +except: +    from tidylib import tidy_document as 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.conf import settings +from django.contrib.formtools.wizard.views import NamedUrlWizardView +from django.core import serializers +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse, NoReverseMatch +from django.db.models import Q +from django.http import HttpResponse, Http404 +from django.shortcuts import render_to_response, redirect +from django.template import RequestContext, loader +from django.template.defaultfilters import slugify +from django.utils.translation import ugettext, ugettext_lazy as _ + +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 + +from ishtar_common.forms import FinalForm +from ishtar_common.forms_common import PersonForm +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)) + +class Wizard(NamedUrlWizardView): +    model = None +    modification = None # True when the wizard modify an item +    storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + +    @staticmethod +    def _check_right(step, condition=True): +        '''Return a method to check the right for a specific step''' +        """ +        def check_right(self, request, storage): +            cond = condition +            if callable(condition): +                cond = condition(self, request, storage) +            if not cond: +                return False +            person_type = request.user.ishtaruser.person.person_type +            if person_type.txt_idx == 'administrator': +                return True +            if person_type.rights.filter(url_name=step).count(): +                return True""" +        def check_right(self): +            cond = condition +            if callable(condition): +                cond = condition(self) +            if not cond: +                return False +            person_type = self.request.user.ishtaruser.person.person_type +            if person_type.txt_idx == 'administrator': +                return True +            if person_type.rights.filter(url_name=step).count(): +                return True +        return check_right + +    def __init__(self, *args, **kwargs): +        """Check right for each step of the wizard""" +        super(Wizard, self).__init__(*args, **kwargs) +        for form_key in self.form_list.keys()[:-1]: +            condition = True +            if form_key in self.condition_dict: +                condition = self.condition_dict.get(form_key, True) +            cond = self._check_right(form_key, condition) +            self.condition_dict[form_key] = cond +        """ +        for form_key in self.form_list.keys()[:-1]: +            condition = True +            if form_key in self.condition_list: +                condition = self.condition_list.get(form_key, True) +            cond = self._check_right(form_key, condition) +            self.condition_list[form_key] = cond""" + +    def get_wizard_name(self): +        """As the class name can interfere when reused, use the url_name""" +        return self.url_name + +    def get_template_names(self): +        templates = ['ishtar/wizard/default_wizard.html'] +        current_step = self.steps.current +        if current_step == self.steps.last: +            templates = ['ishtar/wizard/confirm_wizard.html'] + templates +        return templates + +    def get_context_data(self, form, **kwargs): +        """Add previous, next and current steps to manage the wizard path""" +        context = super(Wizard, self).get_context_data(form) +        step = self.steps.first +        current_step = self.steps.current +        context.update({'current_step':self.form_list[current_step]}) +        if step == current_step: +            return context +        previous_steps, next_steps, previous_step_counter = [], [], 0 +        while step: +            if step == current_step: +                break +            previous_steps.append(self.form_list[step]) +            step = self.steps.next +            previous_step_counter += 1 +        context.update({'previous_steps':previous_steps, +                        'previous_step_counter':previous_step_counter}) +        # if modification: show the next steps +        if self.modification: +            next_step = step +            while next_step: +                # check if the form is initialized otherwise initialize it +                if not storage.get_step_data(next_step): +                    values = self.get_form_initial(request, storage, next_step) +                    prefixed_values = {} +                    if not isinstance(values, list): +                        for key in values: +                            form_key = next_step + '-' + key +                            prefixed_values[form_key] = values[key] +                    else: +                        for formset_idx, v in enumerate(values): +                            prefix = u"-%d-" % formset_idx +                            for key in v: +                                form_key = next_step + prefix + key +                                prefixed_values[form_key] = v[key] +                    storage.set_step_data(next_step, prefixed_values) +                if step != next_step: # if not current step +                    next_steps.append(self.form_list[next_step]) +                next_step = self.get_next_step(request, storage, next_step) +        context.update({'next_steps':next_steps}) +        # not last step: validation +        if current_step != self.steps.last: +            return context +        final_form_list = [] +        for form_key in self.get_form_list().keys(): +            form_obj = self.get_form(step=form_key, +                    data=self.storage.get_step_data(form_key), +                    files=self.storage.get_step_files(form_key)) +            form_obj.is_valid() +            final_form_list.append(form_obj) +        last_form = final_form_list[-1] +        context.update({'datas':self.get_formated_datas(final_form_list)}) +        if hasattr(last_form, 'confirm_msg'): +            context.update({'confirm_msg':last_form.confirm_msg}) +        if hasattr(last_form, 'confirm_end_msg'): +            context.update({'confirm_end_msg':last_form.confirm_end_msg}) +        return context + +    def get_formated_datas(self, forms): +        """Get the data to present in the last page""" +        datas = [] +        for form in forms: +            form_datas = [] +            base_form = hasattr(form, 'forms') and form.forms[0] or form +            associated_models = hasattr(base_form, 'associated_models') and \ +                                base_form.associated_models or {} +            if not hasattr(form, 'cleaned_data') and hasattr(form, 'forms'): +                cleaned_datas = [frm.cleaned_data for frm in form.forms +                                                  if frm.is_valid()] +                if not cleaned_datas: +                    continue +            elif not hasattr(form, 'cleaned_data'): +                continue +            else: +                cleaned_datas = type(form.cleaned_data) == list and \ +                                              form.cleaned_data \ +                            or [form.cleaned_data] +            for cleaned_data in cleaned_datas: +                if not cleaned_data: +                    continue +                if form_datas: +                    form_datas.append(("", "", "spacer")) +                items = hasattr(base_form, 'fields') and \ +                        base_form.fields.keyOrder or cleaned_data.keys() +                for key in items: +                    lbl = None +                    if key.startswith('hidden_'): +                        continue +                    if hasattr(base_form, 'fields') and key in base_form.fields: +                        lbl = base_form.fields[key].label +                        if hasattr(base_form, 'associated_labels') \ +                           and key in base_form.associated_labels: +                            lbl = base_form.associated_labels[key] +                    if not lbl: +                        continue +                    value = cleaned_data[key] +                    if not value and value != False: +                        continue +                    if type(value) == bool: +                        if value == True: +                            value = _(u"Yes") +                        elif value == False: +                            value = _(u"No") +                    elif key in associated_models: +                        values = [] +                        if "," in unicode(value): +                            values = unicode(value).split(",") +                        else: +                            values = [value] +                        rendered_values = [] +                        for val in values: +                            item = associated_models[key].objects.get(pk=val) +                            if hasattr(item, 'short_label'): +                                value = item.short_label() +                            else: +                                value = unicode(item) +                            rendered_values.append(value) +                        value = u" ; ".join(rendered_values) +                    form_datas.append((lbl, value, '')) +            if form_datas: +                datas.append((form.form_label, form_datas)) +        return datas + +    def get_extra_model(self, dct, form_list): +        dct['history_modifier'] = self.request.user +        return dct + +    def done(self, form_list, return_object=False, **kwargs): +        """Save to the model""" +        dct, m2m, whole_associated_models = {}, [], [] +        for form in form_list: +            if not form.is_valid(): +                return self.render(form) +            base_form = hasattr(form, 'forms') and form.forms[0] or form +            associated_models = hasattr(base_form, 'associated_models') and \ +                                base_form.associated_models or {} +            if hasattr(form, 'forms'): +                multi = False +                if form.forms: +                    frm = form.forms[0] +                    if hasattr(frm, 'base_model') and frm.base_model: +                        whole_associated_models.append(frm.base_model) +                    else: +                        whole_associated_models += associated_models.keys() +                    fields = frm.fields.copy() +                    if 'DELETE' in fields: +                        fields.pop('DELETE') +                    multi = len(fields) > 1 +                    if multi: +                        assert hasattr(frm, 'base_model'), \ +                       u"Must define a base_model for " + unicode(frm.__class__) +                for frm in form.forms: +                    if not frm.is_valid(): +                        continue +                    vals = {} +                    if "DELETE" in frm.cleaned_data: +                        if frm.cleaned_data["DELETE"]: +                            continue +                        frm.cleaned_data.pop('DELETE') +                    for key in frm.cleaned_data: +                        value = frm.cleaned_data[key] +                        if not value and value != False: +                            continue +                        if key in associated_models: +                            value = associated_models[key].objects.get(pk=value) +                        if multi: +                            vals[key] = value +                        else: +                            m2m.append((key, value)) +                    if multi and vals: +                        m2m.append((frm.base_model, vals)) +            elif type(form.cleaned_data) == dict: +                for key in form.cleaned_data: +                    if key.startswith('hidden_'): +                        continue +                    value = form.cleaned_data[key] +                    if key in associated_models: +                        if value: +                            model = associated_models[key] +                            if isinstance(value, unicode) \ +                             or isinstance(value, str) and "," in value: +                                value = value.split(",") +                            if isinstance(value, list) \ +                             or isinstance(value, tuple): +                                value = [model.objects.get(pk=val) +                                         for val in value if val] +                                if len(value) == 1: +                                    value = value[0] +                            else: +                                value = model.objects.get(pk=value) +                        else: +                            value = None +                    dct[key] = value +        return self.save_model(dct, m2m, whole_associated_models, form_list, +                               return_object) + +    def get_saved_model(self): +        """Permit a distinguo when saved model is not the base selected model""" +        return self.model + +    def get_current_saved_object(self): +        """Permit a distinguo when saved model is not the base selected model""" +        return self.get_current_object() + +    def save_model(self, dct, m2m, whole_associated_models, form_list, +                   return_object): +        dct = self.get_extra_model(dct, form_list) +        obj = self.get_current_saved_object() +        # manage dependant items +        other_objs = {} +        for k in dct.keys(): +            if '__' not in k: +                continue +            vals = k.split('__') +            assert len(vals) == 2, "Only one level of dependant item is managed" +            dependant_item, key = vals +            if dependant_item not in other_objs: +                other_objs[dependant_item] = {} +            other_objs[dependant_item][key] = dct.pop(k) +        if obj: +            for k in dct: +                if k.startswith('pk'): +                    continue +                setattr(obj, k, dct[k]) +            try: +                obj.full_clean() +            except forms.ValidationError, msg: +                return self.render(form_list[-1]) +            for dependant_item in other_objs: +                c_item = getattr(obj, dependant_item) +                # manage ManyToMany if only one associated +                if hasattr(c_item, "all"): +                    c_items = c_item.all() +                    if len(c_items) != 1: +                        continue +                    c_item = c_items[0] +                if c_item: +                    # to check # +                    for k in other_objs[dependant_item]: +                        setattr(c_item, k, other_objs[dependant_item][k]) +                    c_item.save() +                else: +                    m = getattr(self.model, dependant_item) +                    if hasattr(m, 'related'): +                        c_item = m.related.model(**other_objs[dependant_item]) +                        setattr(obj, dependant_item, c_item) +                    obj.save() +            obj.save() +        else: +            adds = {} +            for dependant_item in other_objs: +                m = getattr(self.model, dependant_item) +                model = m.field.rel.to +                c_dct = other_objs[dependant_item].copy() +                if issubclass(model, models.BaseHistorizedItem): +                    c_dct['history_modifier'] = self.request.user +                c_item = model(**c_dct) +                c_item.save() +                if hasattr(m, 'through'): +                    adds[dependant_item] = c_item +                elif hasattr(m, 'field'): +                    dct[dependant_item] = c_item +            if 'pk' in dct: +                dct.pop('pk') +            obj = self.get_saved_model()(**dct) +            try: +                obj.full_clean() +            except forms.ValidationError, msg: +                return self.render(form_list[-1]) +            obj.save() +            for k in adds: +                getattr(obj, k).add(adds[k]) +            # necessary to manage interaction between models like +            # material_index management for baseitems +            obj.save() +        m2m_items = {} +        for model in whole_associated_models: +            getattr(obj, model+'s').clear() +        for key, value in m2m: +            if key not in m2m_items: +                if type(key) == dict: +                    vals = [] +                    for item in getattr(obj, key+'s').all(): +                        v = {} +                        for k in value.keys(): +                            v[k] = getattr(item, k) +                        vals.append(v) +                    m2m_items[key] = vals +                else: +                    m2m_items[key] = getattr(obj, key+'s').all() +            if value not in m2m_items[key]: +                if type(value) == dict: +                    model = getattr(obj, key+'s').model +                    if issubclass(model, models.BaseHistorizedItem): +                        value['history_modifier'] = self.request.user +                    value = model.objects.create(**value) +                    value.save() +                getattr(obj, key+'s').add(value) +                # necessary to manage interaction between models like +                # material_index management for baseitems +                obj.save() +        res = render_to_response('wizard_done.html', {}, +                                  context_instance=RequestContext(self.request)) +        return return_object and (obj, res) or res + +    def get_deleted(self, keys): +        """Get the deleted and non-deleted items in formsets""" +        not_to_delete, to_delete = set(), set() +        for key in keys: +            items = key.split('-') +            if len(items) < 2 or items[-2] in to_delete: +                continue +            idx = items[-2] +            try: +                int(idx) +            except: +                continue +            if items[-1] == u'DELETE': +                to_delete.add(idx) +                if idx in not_to_delete: +                    not_to_delete.remove(idx) +            elif idx not in not_to_delete: +                not_to_delete.add(idx) +        return (to_delete, not_to_delete) + +    def get_form(self, step=None, data=None, files=None): +        """Manage formset""" +        request = self.request +        storage = self.storage +        if data: +            data = data.copy() +            if not step: +                step = self.steps.current +            form = self.get_form_list()[step] +            if hasattr(form, 'management_form'): +                # manage deletion +                to_delete, not_to_delete = self.get_deleted(data.keys()) +                # raz deleted fields +                for key in data.keys(): +                    items = key.split('-') +                    if len(items) < 2 or items[-2] not in to_delete: +                        continue +                    data.pop(key) +                if to_delete: +                    # reorganize +                    for idx, number in enumerate(sorted(not_to_delete)): +                        idx = unicode(idx) +                        if idx == number: +                            continue +                        for key in data.keys(): +                            items = key.split('-') +                            if len(items) > 2 and number == items[-2]: +                                items[-2] = unicode(idx) +                                k = u'-'.join(items) +                                data[k] = data.pop(key)[0] +                # get a form key +                base_key = form.form.base_fields.keys()[0] +                init = self.get_form_initial(request, storage, step) +                total_field = len([key for key in data.keys() +                                        if base_key in key.split('-') +                                           and data[key]]) +                if init and not to_delete: +                    total_field = max((total_field, len(init))) +                data[step + u'-INITIAL_FORMS'] = unicode(total_field) +                data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1) +        data = data or None +        form = super(Wizard, self).get_form(step, data, files) +        return form + +    def render_next_step(self, form, **kwargs): +        """ +        Manage: +         - modify or delete button in formset: next step = current step +         - validate and end: nextstep = last step +        """ +        request = self.request +        if request.POST.has_key('formset_modify') \ +           and request.POST['formset_modify'] \ +           or [key for key in request.POST.keys() +               if key.endswith('DELETE') and request.POST[key]]: +            return self.render(form) +        elif request.POST.has_key('validate_and_end') \ +           and request.POST['validate_and_end']: +            last_step = self.steps.last +            new_form = self.get_form(last_step, +                data=self.storage.get_step_data(last_step), +                files=self.storage.get_step_files(last_step)) +            self.storage.current_step = last_step +            return self.render(new_form) +        return super(Wizard, self).render_next_step(form, **kwargs) + +    def post(self, *args, **kwargs): +        """Convert numerical step number to step name""" +        request = self.request +        post_data = request.POST.copy() +        if request.POST.has_key('form_prev_step'): +            try: +                step_number = int(request.POST['form_prev_step']) +                post_data['wizard_goto_step'] = self.get_form_list().keys( +                                                                 )[step_number] +            except ValueError: +                pass +        request.POST = post_data +        return super(Wizard, self).post(*args, **kwargs) + +    @classmethod +    def session_has_key(cls, request, storage, form_key, key=None, multi=None): +        """Check if the session has value of a specific form and (if provided) +        of a key +        """ +        test = storage.prefix in request.session \ +          and 'step_data' in request.session[storage.prefix] \ +          and form_key in request.session[storage.prefix]['step_data'] +        if not key or not test: +            return test +        key = key.startswith(form_key) and key or \ +              not multi and form_key + '-' + key or \ +              form_key + '-0-' + key #only check if the first field is available +        return key in request.session[storage.prefix]['step_data'][form_key] + +    @classmethod +    def session_get_value(cls, request, storage, form_key, key, multi=False): +        """Get the value of a specific form""" +        if not cls.session_has_key(request, storage, form_key, key, multi): +            return +        if not multi: +            key = key.startswith(form_key) and key or form_key + '-' + key +            return request.session[storage.prefix]['step_data'][form_key][key] +        vals = [] +        for k in request.session[storage.prefix]['step_data'][form_key]: +            if k.startswith(form_key) and k.endswith(key) and \ +               request.session[storage.prefix]['step_data'][form_key][k]: +                vals.append(request.session[storage.prefix]['step_data']\ +                                           [form_key][k]) +        return vals + +    def get_current_object(self): +        """Get the current object for an instancied wizard""" +        current_obj = None +        main_form_key = 'selec-' + self.url_name +        try: +            idx = int(self.session_get_value(self.request, self.storage, +                                             main_form_key, 'pk')) +            current_obj = self.model.objects.get(pk=idx) +        except(TypeError, ValueError, ObjectDoesNotExist): +            pass +        return current_obj + +    def get_form_initial(self, step): +        current_obj = self.get_current_object() +        current_step = self.steps.current +        if step.startswith('selec-') and step in self.form_list \ +           and 'pk' in self.form_list[step].associated_models: +            model_name = self.form_list[step].associated_models['pk' +                                              ].__name__.lower() +            if step == current_step: +                self.reset_wizard(request, storage) +            val = model_name in request.session and request.session[model_name] +            if val: +                return {'pk':val} +        elif current_obj: +            return self.get_instanced_init(current_obj, step) +        current_form = self.form_list[current_step] +        if hasattr(current_form, 'currents'): +            initial = {} +            for key in current_form.currents: +                model_name = current_form.currents[key].__name__.lower() +                val = model_name in request.session and \ +                      request.session[model_name] +                if val: +                    initial[key] = val +            if initial: +                return initial +        return super(Wizard, self).get_form_initial(step) + +    def get_instanced_init(self, obj, step=None): +        """Get initial data from an init""" +        current_step = step or self.steps.current +        c_form = self.form_list[current_step] +        # make the current object the default item for the session +        obj_name = obj.__class__.__name__.lower() +        # prefer a specialized name if available +        prefixes = self.storage.prefix.split('_') +        if len(prefixes) > 1 and prefixes[-2].startswith(obj_name): +            obj_name = prefixes[-2] +        self.request.session[obj_name] = unicode(obj.pk) +        initial = {} +        if self.request.POST or \ +          (step in self.request.session[self.storage.prefix] and\ +           self.request.session[self.storage.prefix]['step_data'][step]): +            return {} +        if hasattr(c_form, 'base_fields'): +            for base_field in c_form.base_fields.keys(): +                fields = base_field.split('__') +                value = obj +                for field in fields: +                    if not hasattr(value, field) or \ +                       getattr(value, field) == None: +                        value = obj +                        break +                    value = getattr(value, field) +                if value == obj: +                    continue +                if hasattr(value, 'pk'): +                    value = value.pk +                if value in (True, False): +                    initial[base_field] = value +                elif value != None: +                    initial[base_field] = unicode(value) +        elif hasattr(c_form, 'management_form'): +            initial = [] +            if hasattr(c_form.form, 'base_model'): +                key = c_form.form.base_model + 's' +            else: +                key = current_step.split('-')[0] +            if not hasattr(obj, key): +                return initial +            keys = c_form.form.base_fields.keys() +            for child_obj in getattr(obj, key).order_by('pk').all(): +                if not keys: +                    break +                vals = {} +                if len(keys) == 1: +                    # only one field: must be the id of the object +                    vals[keys[0]] = unicode(child_obj.pk) +                else: +                    for field in keys: +                        if hasattr(child_obj, field): +                            value = getattr(child_obj, field) +                            if hasattr(value, 'pk'): +                                value = value.pk +                            if value != None: +                                vals[field] = unicode(value) +                if vals: +                    initial.append(vals) +        return initial + +class PersonWizard(Wizard): +    model = models.Person + +person_creation_wizard = PersonWizard.as_view([ +                        ('identity-person_creation', PersonForm), +                        ('final-person_creation', FinalForm)], +                         url_name='person_creation_step',) + + +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): +    person_types = request.user.ishtaruser.person.person_type +    if (not request.user.has_perm('ishtar_common.view_person', models.Person) and +       not request.user.has_perm('ishtar_common.view_own_person', models.Person) +       and not person_types.rights.filter(wizard__url_name='person_search' +                                                                     ).count()): +        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): +    person_types = request.user.ishtaruser.person.person_type +    if (not request.user.has_perm('ishtar_common.view_file', models.File) and \ +       not request.user.has_perm('ishtar_common.view_own_file', models.File) +       and not person_types.rights.filter(wizard__url_name='file_search' +                                                                     ).count()): +        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_type'] +PRIVATE_FIELDS = ('id', 'history_modifier', 'order') +def get_item(model, func_name, default_name, extra_request_keys=[], +                                        base_request={}, bool_fields=[]): +    """ +    Generic treatment of tables +    """ +    def func(request, data_type='json', full=False, **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 = base_request.copy() +        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 req in dct.copy(): +            for k_hr in HIERARCHIC_FIELDS: +                if req.endswith(k_hr + '__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) +                    break +        query = Q(**dct) +        for or_req in or_reqs: +            query = query & or_req +        items = model.objects.filter(query) +        q = request_items.get('sidx') + +        # manage sort tables +        if q and q in request_keys: +            ks = request_keys[q] +            if type(ks) not in (list, tuple): +                ks = [ks] +            orders = [] +            for k in ks: +                if k.endswith("__pk"): +                    k = k[:-len("__pk")] + "__label" +                q = request_items.get('sord') +                sign = q and q == u'desc' and "-" or '' +                if '__' in k: +                    k = k.split('__')[0] +                orders.append(sign+k) +            items = items.order_by(*orders) + +        # pager management +        start, end = 0, None +        page_nb = 1 +        try: +            row_nb = int(request_items.get('rows')) +        except (ValueError, TypeError): +            row_nb = None +        if row_nb: +            try: +                page_nb = int(request_items.get('page')) +                assert page_nb >= 1 +            except (ValueError, AssertionError): +                pass +            start = (page_nb-1)*row_nb +            end = page_nb*row_nb +        items_nb = items.count() +        items = items[start:end] + +        datas = [] +        if old: +            items = [item.get_previous(old) for item in items] +        table_cols = full and [field.name for field in model._meta.fields +                               if field.name not in PRIVATE_FIELDS] \ +                     or model.TABLE_COLS +        for item in items: +            data = [item.pk] +            for k in 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[table_cols[idx].split('.')[-1]] = value +                rows.append(res) +            data = json.dumps({ +                "records":items_nb, +                "rows":rows, +                "page":page_nb, +                "total":items_nb/row_nb + 1, +            }) +            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 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): +    person_types = request.user.ishtaruser.person.person_type +    if (not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ +       and not request.user.has_perm('ishtar_common.view_own_operation', +                                                              models.Operation) +       and not person_types.rights.filter(wizard__url_name='operation_search' +                                                                     ).count()): +        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') + +def get_available_operation_code(request, year=None): +    if not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ +       and not request.user.has_perm('ishtar_common.view_own_operation', +                                                              models.Operation): +        return HttpResponse(mimetype='text/plain') +    data = json.dumps({'id':models.Operation.get_available_operation_code(year)}) +    return HttpResponse(data, mimetype='text/plain') + +get_operation = get_item(models.Operation, 'get_operation', 'operation', +      bool_fields = ['end_date__isnull'], +      extra_request_keys={'common_name':'common_name__icontains', +                          'end_date':'end_date__isnull', +                          'year_index':('year', 'operation_code')}) +show_operation = show_item(models.Operation, 'operation') +revert_operation = revert_item(models.Operation) + +get_operationsource = get_item(models.OperationSource, +        'get_operationsource', 'operationsource', +      extra_request_keys={'operation__towns':'operation__towns__pk', +                  'operation__operation_type':'operation__operation_type__pk', +                  'operation__year':'operation__year'}) + +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): +    person_types = request.user.ishtaruser.person.person_type +    if (not request.user.has_perm('ishtar_common.view_organization', +                                 models.Organization) and \ +       not request.user.has_perm('ishtar_common.view_own_organization', +                                 models.Organization) +       and not person_types.rights.filter(wizard__url_name='person_search' +                                                                     ).count()): +        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_contextrecordsource = get_item(models.ContextRecordSource, +        'get_contextrecordsource', 'contextrecordsource', +      extra_request_keys={ +    'context_record__parcel__town':'context_record__parcel__town__pk', +    'context_record__operation__year':'context_record__operation__year', +    'context_record__datings__period':'context_record__datings__period__pk', +    'context_record__unit':'context_record__unit__pk', +    }) +get_archaeologicalitem = get_item(models.Item, +        'get_archaeologicalitem', 'item', +        bool_fields = ['base_items__is_isolated'], +        base_request={'downstream_treatment__isnull':True}, +      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'}) +get_itemsource = get_item(models.ItemSource, +        'get_itemsource', 'itemsource', +      extra_request_keys={ +'item__context_record__operation__year':'item__context_record__operation__year', +'item__dating__period':'item__dating__period__pk', +'item__description':'item__description__icontains', +    }) +get_container = get_item(models.Container, +        'get_container', 'container', +      extra_request_keys={ +'location':'location__pk', +'container_type':'container_type__pk', +'reference':'reference__icontains', +    }) + +def autocomplete_warehouse(request): +    if not request.user.has_perm('ishtar_common.view_warehouse', models.Warehouse)\ +       and not request.user.has_perm('ishtar_common.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_common.view_author', models.Author)\ +       and not request.user.has_perm('ishtar_common.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 autocomplete_container(request): +    if not request.user.has_perm('ishtar_common.view_warehouse', +                                     models.Warehouse)\ +       and not request.user.has_perm('ishtar_common.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(container_type__label__icontains=q) | \ +                Q(container_type__reference__icontains=q) | \ +                Q(reference__icontains=q) | \ +                Q(location__name=q) | \ +                Q(location__town=q) +        query = query & extra +    limit = 15 +    containers = models.Container.objects.filter(query)[:limit] +    data = json.dumps([{'id':container.pk, 'value':unicode(container)} +                                          for container in containers]) +    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 dct['parent_pk'] and '_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) +new_container = new_item(models.Container) + +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)) + +def dashboard_main(request, dct, obj_id=None, *args, **kwargs): +    """ +    Main dashboard +    """ +    dct = {'items':[ +        (_(u"Archaeological files"), models.Dashboard(models.File)), +        (_(u"Operations"), models.Dashboard(models.Operation)), +        (_(u"Context records"), models.Dashboard(models.ContextRecord)), +        (_(u"Archaeological items"), models.Dashboard(models.Item)), +        ], +           'ishtar_users':models.UserDashboard()} +    return render_to_response('dashboard_main.html', dct, +                               context_instance=RequestContext(request)) + +def dashboard_file(request, dct, obj_id=None, *args, **kwargs): +    """ +    Main dashboard +    """ +    dct = {'dashboard': models.FileDashboard()} +    return render_to_response('dashboard_file.html', dct, +                              context_instance=RequestContext(request)) + +def dashboard_operation(request, dct, obj_id=None, *args, **kwargs): +    """ +    Operation dashboard +    """ +    dct = {'dashboard': models.OperationDashboard()} +    return render_to_response('dashboard_operation.html', dct, +                              context_instance=RequestContext(request))  | 
