diff options
Diffstat (limited to 'ishtar_common/views.py')
| -rw-r--r-- | ishtar_common/views.py | 1003 | 
1 files changed, 47 insertions, 956 deletions
| diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 187712676..81397dd70 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -17,74 +17,50 @@  # See the file COPYING for details. -from copy import copy, deepcopy  import csv  import datetime  import json  import logging -from markdown import markdown -import optparse -import re -from tempfile import NamedTemporaryFile -from tidylib import tidy_document as tidy  import unicodedata -from unidecode import unidecode -import unicodecsv -from weasyprint import HTML, CSS -from weasyprint.fonts import FontConfiguration - -from extra_views import ModelFormSetView +import unicodecsv  from django.conf import settings  from django.contrib.auth import logout  from django.contrib.auth.decorators import login_required -from django.contrib.postgres.search import SearchQuery -from django.contrib.staticfiles.templatetags.staticfiles import static  from django.core.exceptions import ObjectDoesNotExist  from django.core.urlresolvers import reverse, NoReverseMatch -from django.db.models import Q, ImageField -from django.db.models.fields import FieldDoesNotExist +from django.db.models import Q  from django.forms.models import modelformset_factory  from django.http import HttpResponse, Http404, HttpResponseRedirect, \      HttpResponseBadRequest  from django.shortcuts import redirect, render -from django.template import loader  from django.utils.decorators import method_decorator  from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView, UpdateView, TemplateView, FormView +from django.views.generic import ListView, UpdateView, TemplateView  from django.views.generic.edit import CreateView, DeleteView, FormView +from extra_views import ModelFormSetView +from markdown import markdown -from xhtml2odt import xhtml2odt - -from menus import Menu - -from archaeological_files.models import File -from archaeological_operations.models import Operation +import models  from archaeological_context_records.models import ContextRecord -from archaeological_finds.models import Find, Treatment, TreatmentFile, \ -    FindBasket - -from archaeological_operations.forms import DashboardForm as DashboardFormOpe  from archaeological_files.forms import DashboardForm as DashboardFormFile +from archaeological_files.models import File  from archaeological_finds.forms import DashboardTreatmentForm, \      DashboardTreatmentFileForm - -from ishtar_common.forms import FinalForm, FinalDeleteForm -from ishtar_common.widgets import JQueryAutoComplete -from ishtar_common.utils import clean_session_cache, \ -    get_all_field_names, get_field_labels_from_path, \ -    get_random_item_image_link, shortify +from archaeological_finds.models import Find, Treatment, TreatmentFile +from archaeological_operations.forms import DashboardForm as DashboardFormOpe +from archaeological_operations.models import Operation  from ishtar_common import forms_common as forms  from ishtar_common import wizards -from ishtar_common.models import HistoryError, PRIVATE_FIELDS, \ -    get_current_profile - +from ishtar_common.forms import FinalForm, FinalDeleteForm +from ishtar_common.models import get_current_profile  from ishtar_common.templatetags.link_to_window import link_to_window +from ishtar_common.utils import clean_session_cache, CSV_OPTIONS, \ +    get_field_labels_from_path, get_random_item_image_link, shortify +from ishtar_common.widgets import JQueryAutoComplete -import models - -CSV_OPTIONS = {'delimiter': ',', 'quotechar': '"', 'quoting': csv.QUOTE_ALL} -ENCODING = settings.ENCODING or 'utf-8' +from views_item import CURRENT_ITEM_KEYS, display_item, get_item, new_item, \ +    show_item  logger = logging.getLogger(__name__) @@ -367,15 +343,6 @@ def shortcut_menu(request):      return render(request, 'ishtar/blocks/shortcut_menu.html', dct) -CURRENT_ITEM_KEYS = (('file', File), -                     ('operation', Operation), -                     ('contextrecord', ContextRecord), -                     ('find', Find), -                     ('treatmentfile', TreatmentFile), -                     ('treatment', Treatment)) -CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) - -  def get_current_items(request):      currents = {}      for key, model in CURRENT_ITEM_KEYS: @@ -466,17 +433,27 @@ def update_current_item(request, item_type=None, pk=None):      return HttpResponse('ok') -def check_permission(request, action_slug, obj_id=None): -    MAIN_MENU = Menu(None) -    MAIN_MENU.init() -    if action_slug not in MAIN_MENU.items: -        # TODO -        return True -    if obj_id: -        return MAIN_MENU.items[action_slug].is_available( -            request.user, obj_id, session=request.session) -    return MAIN_MENU.items[action_slug].can_be_available( -        request.user, session=request.session) +def get_by_importer(request, slug, data_type='json', full=False, +                    force_own=False, **dct): +    q = models.ImporterType.objects.filter(slug=slug) +    if not q.count(): +        res = '' +        if data_type == "json": +            res = '{}' +        return HttpResponse(res, content_type='text/plain') +    imp = q.all()[0].get_importer_class() +    cols, col_names = [], [] +    for formater in imp.LINE_EXPORT_FORMAT: +        if not formater: +            cols.append('') +            col_names.append("") +            continue +        cols.append(formater.export_field_name) +        col_names.append(formater.label) +    obj_name = imp.OBJECT_CLS.__name__.lower() +    return get_item( +        imp.OBJECT_CLS, 'get_' + obj_name, obj_name, own_table_cols=cols +    )(request, data_type, full, force_own, col_names=col_names, **dct)  def autocomplete_person_permissive(request, person_types=None, @@ -603,873 +580,6 @@ def department_by_state(request, state_id=''):      return HttpResponse(data, content_type='text/plain') -def format_val(val): -    if val is None: -        return u"" -    if type(val) == bool: -        if val: -            return unicode(_(u"True")) -        else: -            return unicode(_(u"False")) -    if type(val) == str: -        val = val.decode('utf-8') -    return unicode(val) - - -HIERARCHIC_LEVELS = 5 -HIERARCHIC_FIELDS = ['periods', 'period', 'unit', 'material_types', -                     'material_type', 'conservatory_state', 'object_types'] - - -def _get_values(request, val): -    if hasattr(val, 'all'):  # manage related objects -        vals = list(val.all()) -    else: -        vals = [val] -    new_vals = [] -    for v in vals: -        if callable(v): -            v = v() -        if hasattr(v, 'url'): -            v = request.is_secure() and \ -                'https' or 'http' + '://' + \ -                           request.get_host() + v.url -        new_vals.append(v) -    return new_vals - - -def _search_manage_search_vector(dct): -    if 'search_vector' in dct: -        dct['search_vector'] = SearchQuery( -            unidecode(dct['search_vector']), -            config=settings.ISHTAR_SEARCH_LANGUAGE -        ) -    return dct - - -DEFAULT_ROW_NUMBER = 10 -# length is used by ajax DataTables requests -EXCLUDED_FIELDS = ['length'] - - -def get_item(model, func_name, default_name, extra_request_keys=[], -             base_request=None, bool_fields=[], reversed_bool_fields=[], -             dated_fields=[], associated_models=[], relative_session_names=[], -             specific_perms=[], own_table_cols=None, relation_types_prefix={}, -             do_not_deduplicate=False): -    """ -    Generic treatment of tables - -    :param model: model used for query -    :param func_name: name of the function (used for session storage) -    :param default_name: key used for default search in session -    :param extra_request_keys: default query limitation -    :param base_request: -    :param bool_fields: -    :param reversed_bool_fields: -    :param dated_fields: -    :param associated_models: -    :param relative_session_names: -    :param specific_perms: -    :param own_table_cols: -    :param relation_types_prefix: -    :param do_not_deduplicate: duplication of id can occurs on large queryset a -    mecanism of deduplication is used. But duplicate ids can be normal (for -    instance for record_relations view). -    :return: -    """ -    def func(request, data_type='json', full=False, force_own=False, -             col_names=None, **dct): -        available_perms = [] -        if specific_perms: -            available_perms = specific_perms[:] -        EMPTY = '' -        if 'type' in dct: -            data_type = dct.pop('type') -        if not data_type: -            EMPTY = '[]' -            data_type = 'json' - -        allowed, own = models.check_model_access_control(request, model, -                                                         available_perms) -        if not allowed: -            return HttpResponse(EMPTY, content_type='text/plain') - -        if force_own: -            own = True -        if full == 'shortcut' and 'SHORTCUT_SEARCH' in request.session and \ -                request.session['SHORTCUT_SEARCH'] == 'own': -            own = True - -        # get defaults from model -        if not extra_request_keys and hasattr(model, 'EXTRA_REQUEST_KEYS'): -            my_extra_request_keys = copy(model.EXTRA_REQUEST_KEYS) -        else: -            my_extra_request_keys = copy(extra_request_keys) -        if base_request is None and hasattr(model, 'BASE_REQUEST'): -            my_base_request = copy(model.BASE_REQUEST) -        elif base_request is not None: -            my_base_request = copy(base_request) -        else: -            my_base_request = {} -        if not bool_fields and hasattr(model, 'BOOL_FIELDS'): -            my_bool_fields = model.BOOL_FIELDS[:] -        else: -            my_bool_fields = bool_fields[:] -        if not reversed_bool_fields and hasattr(model, 'REVERSED_BOOL_FIELDS'): -            my_reversed_bool_fields = model.REVERSED_BOOL_FIELDS[:] -        else: -            my_reversed_bool_fields = reversed_bool_fields[:] -        if not dated_fields and hasattr(model, 'DATED_FIELDS'): -            my_dated_fields = model.DATED_FIELDS[:] -        else: -            my_dated_fields = dated_fields[:] -        if not associated_models and hasattr(model, 'ASSOCIATED_MODELS'): -            my_associated_models = model.ASSOCIATED_MODELS[:] -        else: -            my_associated_models = associated_models[:] -        if not relative_session_names and hasattr(model, -                                                  'RELATIVE_SESSION_NAMES'): -            my_relative_session_names = model.RELATIVE_SESSION_NAMES[:] -        else: -            my_relative_session_names = relative_session_names[:] -        if not relation_types_prefix and hasattr(model, -                                                 'RELATION_TYPES_PREFIX'): -            my_relation_types_prefix = copy(model.RELATION_TYPES_PREFIX) -        else: -            my_relation_types_prefix = copy(relation_types_prefix) - -        fields = [model._meta.get_field(k) -                  for k in get_all_field_names(model)] - -        request_keys = dict([ -            (field.name, -             field.name + (hasattr(field, 'rel') and field.rel and '__pk' -                           or '')) -            for field in fields]) -        for associated_model, key in my_associated_models: -            if type(associated_model) in (str, unicode): -                if associated_model not in globals(): -                    continue -                associated_model = globals()[associated_model] -            associated_fields = [ -                associated_model._meta.get_field(k) -                for k in get_all_field_names(associated_model)] -            request_keys.update( -                dict([(key + "__" + field.name, -                       key + "__" + field.name + -                       (hasattr(field, 'rel') and field.rel and '__pk' or '')) -                      for field in associated_fields])) -        request_keys.update(my_extra_request_keys) -        request_items = request.method == 'POST' and request.POST \ -            or request.GET - -        # pager -        try: -            row_nb = int(request_items.get('length')) -        except (ValueError, TypeError): -            row_nb = DEFAULT_ROW_NUMBER -        dct_request_items = {} - -        # filter requested fields -        for k in request_items: -            if k in EXCLUDED_FIELDS: -                continue -            key = k[:] -            if key.startswith('searchprefix_'): -                key = key[len('searchprefix_'):] -            dct_request_items[key] = request_items[k] -        request_items = dct_request_items - -        dct = my_base_request -        if full == 'shortcut': -            dct['cached_label__icontains'] = request.GET.get('term', None) -        and_reqs, or_reqs = [], [] -        try: -            old = 'old' in request_items and int(request_items['old']) -        except ValueError: -            return HttpResponse('[]', content_type='text/plain') - -        # manage relations types -        if 'relation_types' not in my_relation_types_prefix: -            my_relation_types_prefix['relation_types'] = '' -        relation_types = {} -        for rtype_key in my_relation_types_prefix: -            relation_types[my_relation_types_prefix[rtype_key]] = set() -            for k in request_items: -                if k.startswith(rtype_key): -                    relation_types[my_relation_types_prefix[rtype_key]].add( -                        request_items[k]) -                    continue - -        for k in request_keys: -            val = request_items.get(k) -            if not val: -                continue -            req_keys = request_keys[k] -            if type(req_keys) not in (list, tuple): -                dct[req_keys] = val -                continue -            # multiple choice target -            reqs = Q(**{req_keys[0]: val}) -            for req_key in req_keys[1:]: -                q = Q(**{req_key: val}) -                reqs |= q -            and_reqs.append(reqs) - -        pinned_search = "" -        if 'submited' not in request_items and full != 'shortcut': -            # default search -            # an item is selected in the default menu -            if default_name in request.session and \ -               request.session[default_name]: -                value = request.session[default_name] -                if 'basket-' in value: -                    try: -                        dct = {"basket__pk": -                               request.session[default_name].split('-')[-1]} -                        pinned_search = unicode(FindBasket.objects.get( -                            pk=dct["basket__pk"])) -                    except FindBasket.DoesNotExist: -                        pass -                else: -                    try: -                        dct = {"pk": request.session[default_name]} -                        pinned_search = unicode(model._meta.verbose_name)\ -                            + u" - " + unicode( -                                model.objects.get(pk=dct["pk"])) -                    except model.DoesNotExist: -                        pass -            elif dct == (my_base_request or {}): -                # a parent item may be selected in the default menu -                for name, key in my_relative_session_names: -                    if name in request.session and request.session[name] \ -                            and 'basket-' not in request.session[name] \ -                            and name in CURRENT_ITEM_KEYS_DICT: -                        up_model = CURRENT_ITEM_KEYS_DICT[name] -                        try: -                            dct.update({key: request.session[name]}) -                            pinned_search = unicode(up_model._meta.verbose_name)\ -                                + u" - " + unicode( -                                    up_model.objects.get(pk=dct[key])) -                            break -                        except up_model.DoesNotExist: -                            pass -            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 (list(my_bool_fields) + list(my_reversed_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 -                    if k in my_reversed_bool_fields: -                        dct[k] = not dct[k] -                    # check also for empty value with image field -                    field_name = k.split('__')[0] -                    # TODO: can be improved in later version of Django -                    try: -                        c_field = model._meta.get_field(field_name) -                        if k.endswith('__isnull') and \ -                           isinstance(c_field, ImageField): -                            if dct[k]: -                                or_reqs.append( -                                    (k, {k.split('__')[0] + '__exact': ''})) -                            else: -                                dct[k.split('__')[0] + '__regex'] = '.{1}.*' -                    except FieldDoesNotExist: -                        pass -        for k in my_dated_fields: -            if k in dct: -                if not dct[k]: -                    dct.pop(k) -                try: -                    items = dct[k].split('/') -                    assert len(items) == 3 -                    dct[k] = datetime.date(*map(lambda x: int(x), -                                                reversed(items)))\ -                                     .strftime('%Y-%m-%d') -                except AssertionError: -                    dct.pop(k) -        # manage hierarchic conditions -        for req in dct.copy(): -            if req.endswith('town__pk') or req.endswith('towns__pk'): -                val = dct.pop(req) -                reqs = Q(**{req: val}) -                base_req = req[:-2] + '__' -                req = base_req[:] -                for idx in range(HIERARCHIC_LEVELS): -                    req = req[:-2] + 'parents__pk' -                    q = Q(**{req: val}) -                    reqs |= q -                req = base_req[:] -                for idx in range(HIERARCHIC_LEVELS): -                    req = req[:-2] + 'children__pk' -                    q = Q(**{req: val}) -                    reqs |= q -                and_reqs.append(reqs) -                continue - -            for k_hr in HIERARCHIC_FIELDS: -                if type(req) in (list, tuple): -                    val = dct.pop(req) -                    q = None -                    for idx, r in enumerate(req): -                        if not idx: -                            q = Q(**{r: val}) -                        else: -                            q |= Q(**{r: val}) -                    and_reqs.append(q) -                    break -                elif req.endswith(k_hr + '__pk'): -                    val = dct.pop(req) -                    reqs = Q(**{req: val}) -                    req = req[:-2] + '__' -                    for idx in range(HIERARCHIC_LEVELS): -                        req = req[:-2] + 'parent__pk' -                        q = Q(**{req: val}) -                        reqs |= q -                    and_reqs.append(reqs) -                    break -        dct = _search_manage_search_vector(dct) -        query = Q(**dct) -        for k, or_req in or_reqs: -            alt_dct = dct.copy() -            alt_dct.pop(k) -            alt_dct.update(or_req) -            query |= Q(**alt_dct) - -        for rtype_prefix in relation_types: -            vals = list(relation_types[rtype_prefix]) -            if not vals: -                continue -            alt_dct = { -                rtype_prefix + 'right_relations__relation_type__pk__in': vals} -            for k in dct: -                val = dct[k] -                if rtype_prefix: -                    # only get conditions related to the object -                    if rtype_prefix not in k: -                        continue -                    # tricky: reconstruct the key to make sense - remove the -                    # prefix from the key -                    k = k[0:k.index(rtype_prefix)] + k[ -                        k.index(rtype_prefix) + len(rtype_prefix):] -                if k.endswith('year'): -                    k += '__exact' -                alt_dct[rtype_prefix + 'right_relations__right_record__' + k] =\ -                    val -            if not dct: -                # fake condition to trick Django (1.4): without it only the -                # alt_dct is managed -                query &= Q(pk__isnull=False) -            query |= Q(**alt_dct) -            for k, or_req in or_reqs: -                altor_dct = alt_dct.copy() -                altor_dct.pop(k) -                for j in or_req: -                    val = or_req[j] -                    if j == 'year': -                        j = 'year__exact' -                    altor_dct[ -                        rtype_prefix + 'right_relations__right_record__' + j] =\ -                        val -                query |= Q(**altor_dct) - -        if own: -            q = models.IshtarUser.objects.filter(user_ptr=request.user) -            if q.count(): -                query = query & model.get_query_owns(q.all()[0]) -            else: -                return HttpResponse(EMPTY, content_type='text/plain') - -        for and_req in and_reqs: -            query = query & and_req - -        # manage hierarchic in shortcut menu -        if full == 'shortcut': -            ASSOCIATED_ITEMS = { -                Operation: (File, 'associated_file__pk'), -                ContextRecord: (Operation, 'operation__pk'), -                Find: (ContextRecord, 'base_finds__context_record__pk'), -            } -            if model in ASSOCIATED_ITEMS: -                upper_model, upper_key = ASSOCIATED_ITEMS[model] -                model_name = upper_model.SLUG -                current = model_name in request.session \ -                    and request.session[model_name] -                if current: -                    dct = {upper_key: current} -                    query &= Q(**dct) - -        items = model.objects.filter(query).distinct() -        # print(items.query) - -        if 'search_vector' in dct:  # for serialization -            dct['search_vector'] = dct['search_vector'].value - -        # table cols -        if own_table_cols: -            table_cols = own_table_cols -        else: -            if full: -                table_cols = [field.name for field in model._meta.fields -                              if field.name not in PRIVATE_FIELDS] -                table_cols += [field.name for field in model._meta.many_to_many -                               if field.name not in PRIVATE_FIELDS] -                if hasattr(model, 'EXTRA_FULL_FIELDS'): -                    table_cols += model.EXTRA_FULL_FIELDS -            else: -                table_cols = model.TABLE_COLS -        query_table_cols = [] -        for cols in table_cols: -            if type(cols) not in (list, tuple): -                cols = [cols] -            for col in cols: -                query_table_cols += col.split('|') - -        # contextual (full, simple, etc.) col -        contxt = full and 'full' or 'simple' -        if hasattr(model, 'CONTEXTUAL_TABLE_COLS') and \ -                contxt in model.CONTEXTUAL_TABLE_COLS: -            for idx, col in enumerate(table_cols): -                if col in model.CONTEXTUAL_TABLE_COLS[contxt]: -                    query_table_cols[idx] = \ -                        model.CONTEXTUAL_TABLE_COLS[contxt][col] -        if full == 'shortcut': -            query_table_cols = ['cached_label'] -            table_cols = ['cached_label'] - -        # manage sort tables -        manual_sort_key = None - -        sorts = {} -        for k in request_items: -            if not k.startswith('order['): -                continue -            num = int(k.split(']')[0][len("order["):]) -            if num not in sorts: -                sorts[num] = ['', '']  # sign, col_num -            if k.endswith('[dir]'): -                order = request_items[k] -                sign = order and order == u'desc' and "-" or '' -                sorts[num][0] = sign -            if k.endswith('[column]'): -                sorts[num][1] = request_items[k] -        sign = "" -        if not sorts and model._meta.ordering: -            orders = [k for k in model._meta.ordering] -            items = items.order_by(*orders) -        else: -            orders = [] -            for idx in sorted(sorts.keys()): -                signe, col_num = sorts[idx] -                k = query_table_cols[int(col_num) - 2]  # remove id and link col -                if k in request_keys: -                    ks = request_keys[k] -                    if type(ks) not in (tuple, list): -                        ks = [ks] -                    for k in ks: -                        if k.endswith("__pk"): -                            k = k[:-len("__pk")] + "__label" -                        if '__' in k: -                            k = k.split('__')[0] -                        orders.append(signe + k) -                else: -                    # not a standard request key -                    if idx:  # not the first - we ignore this sort -                        continue -                    sign = signe -                    manual_sort_key = k -                    logger.warning( -                        "**WARN get_item - {}**: manual sort key '{}'".format( -                            func_name, k)) -                    break -            if not manual_sort_key: -                items = items.order_by(*orders) - -        # pager management -        start, end = 0, None -        page_nb = 1 -        if row_nb and data_type == "json": -            try: -                start = int(request_items.get('start')) -                page_nb = start / row_nb + 1 -                assert page_nb >= 1 -            except (TypeError, ValueError, AssertionError): -                start = 0 -                page_nb = 1 -            end = page_nb * row_nb -        if full == 'shortcut': -            start = 0 -            end = 20 - -        items_nb = items.count() -        if manual_sort_key: -            items = items.all() -        else: -            items = items[start:end] - -        datas = [] -        if old: -            items = [item.get_previous(old) for item in items] -        c_ids = [] -        for item in items: -            # manual deduplicate when distinct is not enough -            if not do_not_deduplicate and item.pk in c_ids: -                continue -            c_ids.append(item.pk) -            data = [item.pk] -            for keys in query_table_cols: -                if type(keys) not in (list, tuple): -                    keys = [keys] -                my_vals = [] -                for k in keys: -                    if hasattr(model, 'EXTRA_REQUEST_KEYS') \ -                            and k in model.EXTRA_REQUEST_KEYS: -                        k = model.EXTRA_REQUEST_KEYS[k] -                        if type(k) in (list, tuple): -                            k = k[0] -                    for filtr in ('__icontains', '__contains'): -                        if k.endswith(filtr): -                            k = k[:len(k) - len(filtr)] -                    vals = [item] -                    # foreign key may be divided by "." or "__" -                    splitted_k = [] -                    for ky in k.split('.'): -                        if '__' in ky: -                            splitted_k += ky.split('__') -                        else: -                            splitted_k.append(ky) -                    for ky in splitted_k: -                        new_vals = [] -                        for val in vals: -                            if hasattr(val, 'all'):  # manage related objects -                                val = list(val.all()) -                                for v in val: -                                    v = getattr(v, ky) -                                    new_vals += _get_values(request, v) -                            elif val: -                                try: -                                    val = getattr(val, ky) -                                    new_vals += _get_values(request, val) -                                except AttributeError: -                                    # must be a query key such as "contains" -                                    pass -                        vals = new_vals -                    # manage last related objects -                    if vals and hasattr(vals[0], 'all'): -                        new_vals = [] -                        for val in vals: -                            new_vals += list(val.all()) -                        vals = new_vals -                    if not my_vals: -                        my_vals = [format_val(va) for va in vals] -                    else: -                        new_vals = [] -                        if not vals: -                            for idx, my_v in enumerate(my_vals): -                                new_vals.append(u"{}{}{}".format( -                                    my_v, u' - ', '')) -                        else: -                            for idx, v in enumerate(vals): -                                new_vals.append(u"{}{}{}".format( -                                    vals[idx], u' - ', format_val(v))) -                        my_vals = new_vals[:] -                data.append(u" & ".join(my_vals) or u"") -            datas.append(data) -        if manual_sort_key: -            # +1 because the id is added as a first col -            idx_col = None -            if manual_sort_key in query_table_cols: -                idx_col = query_table_cols.index(manual_sort_key) + 1 -            else: -                for idx, col in enumerate(query_table_cols): -                    if type(col) in (list, tuple) and \ -                            manual_sort_key in col: -                        idx_col = idx + 1 -            if idx_col is not None: -                datas = sorted(datas, key=lambda x: x[idx_col]) -                if sign == '-': -                    datas = reversed(datas) -                datas = list(datas)[start:end] -        link_template = "<a class='display_details' href='#' "\ -            "onclick='load_window(\"%s\")'>"\ -            "<i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>" -        link_ext_template = '<a href="{}" target="_blank">{}</a>' -        if data_type == "json": -            rows = [] -            for data in datas: -                try: -                    lnk = link_template % reverse('show-' + default_name, -                                                  args=[data[0], '']) -                except NoReverseMatch: -                    logger.warning( -                        '**WARN "show-' + default_name + '" args (' -                        + unicode(data[0]) + ") url not available") -                    lnk = '' -                res = {'id': data[0], 'link': lnk} -                for idx, value in enumerate(data[1:]): -                    if value: -                        table_col = table_cols[idx] -                        if type(table_col) not in (list, tuple): -                            table_col = [table_col] -                        tab_cols = [] -                        # foreign key may be divided by "." or "__" -                        for tc in table_col: -                            if '.' in tc: -                                tab_cols += tc.split('.') -                            elif '__' in tc: -                                tab_cols += tc.split('__') -                            else: -                                tab_cols.append(tc) -                        k = "__".join(tab_cols) -                        if hasattr(model, 'COL_LINK') and k in model.COL_LINK: -                            value = link_ext_template.format(value, value) -                        res[k] = value -                if full == 'shortcut' and 'cached_label' in res: -                    res['value'] = res.pop('cached_label') -                rows.append(res) -            if full == 'shortcut': -                data = json.dumps(rows) -            else: -                data = json.dumps({ -                    "recordsTotal": items_nb, -                    "recordsFiltered": items_nb, -                    "rows": rows, -                    "pinned-search": pinned_search, -                    "page": page_nb, -                    "total": (items_nb / row_nb + 1) if row_nb else items_nb, -                }) -            return HttpResponse(data, content_type='text/plain') -        elif data_type == "csv": -            response = HttpResponse(content_type='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) -            if col_names: -                col_names = [name.encode(ENCODING, errors='replace') -                             for name in col_names] -            else: -                col_names = [] -                for field_name in table_cols: -                    if type(field_name) in (list, tuple): -                        field_name = u" & ".join(field_name) -                    if hasattr(model, 'COL_LABELS') and\ -                            field_name in model.COL_LABELS: -                        field = model.COL_LABELS[field_name] -                        col_names.append(unicode(field).encode(ENCODING)) -                        continue -                    else: -                        try: -                            field = model._meta.get_field(field_name) -                        except: -                            col_names.append(u"".encode(ENCODING)) -                            logger.warning( -                                "**WARN get_item - csv export**: no col name " -                                "for {}\nadd explicit label to " -                                "COL_LABELS attribute of " -                                "{}".format(field_name, model)) -                            continue -                        col_names.append( -                            unicode(field.verbose_name).encode(ENCODING)) -            writer.writerow(col_names) -            for data in datas: -                row, delta = [], 0 -                # regroup cols with join "|" -                for idx, col_name in enumerate(table_cols): -                    if len(data[1:]) <= idx + delta: -                        break -                    val = data[1:][idx + delta].encode( -                        ENCODING, errors='replace') -                    if col_name and "|" in col_name[0]: -                        for delta_idx in range( -                                len(col_name[0].split('|')) - 1): -                            delta += 1 -                            val += data[1:][idx + delta].encode( -                                ENCODING, errors='replace') -                    row.append(val) -                writer.writerow(row) -            return response -        return HttpResponse('{}', content_type='text/plain') - -    return func - - -def get_by_importer(request, slug, data_type='json', full=False, -                    force_own=False, **dct): -    q = models.ImporterType.objects.filter(slug=slug) -    if not q.count(): -        res = '' -        if data_type == "json": -            res = '{}' -        return HttpResponse(res, content_type='text/plain') -    imp = q.all()[0].get_importer_class() -    cols, col_names = [], [] -    for formater in imp.LINE_EXPORT_FORMAT: -        if not formater: -            cols.append('') -            col_names.append("") -            continue -        cols.append(formater.export_field_name) -        col_names.append(formater.label) -    obj_name = imp.OBJECT_CLS.__name__.lower() -    return get_item( -        imp.OBJECT_CLS, 'get_' + obj_name, obj_name, own_table_cols=cols -    )(request, data_type, full, force_own, col_names=col_names, **dct) - - -def display_item(model, extra_dct=None, show_url=None): -    def func(request, pk, **dct): -        if show_url: -            dct['show_url'] = "/{}{}/".format(show_url, pk) -        else: -            dct['show_url'] = "/show-{}/{}/".format(model.SLUG, pk) -        return render(request, 'ishtar/display_item.html', dct) -    return func - - -def show_item(model, name, extra_dct=None): -    def func(request, pk, **dct): -        allowed, own = models.check_model_access_control(request, model) -        if not allowed: -            return HttpResponse('', content_type="application/xhtml") -        q = model.objects -        if own: -            query_own = model.get_query_owns(request.user) -            if query_own: -                q = q.filter(query_own) -        try: -            item = q.get(pk=pk) -        except ObjectDoesNotExist: -            return HttpResponse('NOK') -        doc_type = 'type' in dct and dct.pop('type') -        url_name = u"/".join(reverse('show-' + name, args=['0', ''] -                                     ).split('/')[:-2]) + u"/" -        dct['CURRENCY'] = get_current_profile().currency -        dct['ENCODING'] = settings.ENCODING -        dct['DOT_GENERATION'] = settings.DOT_BINARY and True -        dct['current_window_url'] = url_name -        date = None -        if 'date' in dct: -            date = dct.pop('date') -        dct['sheet_id'] = "%s-%d" % (name, item.pk) -        dct['window_id'] = "%s-%d-%s" % ( -            name, item.pk, datetime.datetime.now().strftime('%M%s')) -        if hasattr(item, 'history'): -            if date: -                try: -                    date = datetime.datetime.strptime(date, -                                                      '%Y-%m-%dT%H:%M:%S.%f') -                    item = item.get_previous(date=date) -                    assert item is not None -                except (ValueError, AssertionError): -                    return HttpResponse(None, content_type='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 -        # add context -        if extra_dct: -            dct.update(extra_dct(request, item)) -        context_instance = deepcopy(dct) -        context_instance['output'] = 'html' -        if hasattr(item, 'history_object'): -            filename = item.history_object.associated_filename -        else: -            filename = item.associated_filename -        if doc_type == "odt" and settings.ODT_TEMPLATE: -            tpl = loader.get_template('ishtar/sheet_%s.html' % name) -            context_instance['output'] = 'ODT' -            content = tpl.render(context_instance, request) -            try: -                tidy_options = {'output-xhtml': 1, 'indent': 1, -                                'tidy-mark': 0, 'doctype': 'auto', -                                'add-xml-decl': 1, 'wrap': 1} -                html, errors = tidy(content, options=tidy_options) -                html = html.encode('utf-8').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: -                return HttpResponse(content, content_type="application/xhtml") -            response = HttpResponse( -                content_type='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('ishtar/sheet_%s_pdf.html' % name) -            context_instance['output'] = 'PDF' -            html = tpl.render(context_instance, request) -            font_config = FontConfiguration() -            css = CSS(string=''' -            @font-face { -                font-family: Gentium; -                src: url(%s); -            } -            body{ -                font-family: Gentium -            } -            ''' % (static("gentium/GentiumPlus-R.ttf"))) -            css2 = CSS(filename=settings.STATIC_ROOT + '/media/style_basic.css') -            pdf = HTML(string=html, base_url=request.build_absolute_uri() -                       ).write_pdf(stylesheets=[css, css2], -                                   font_config=font_config) -            response = HttpResponse(pdf, content_type='application/pdf') -            response['Content-Disposition'] = 'attachment; filename=%s.pdf' % \ -                                              filename -            return response -        else: -            tpl = loader.get_template('ishtar/sheet_%s_window.html' % name) -            content = tpl.render(context_instance, request) -            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, content_type='text/plain') -        return HttpResponse("True", content_type='text/plain') -    return func - -  def autocomplete_organization(request, orga_type=None):      if (not request.user.has_perm('ishtar_common.view_organization',          models.Organization) and @@ -1521,31 +631,6 @@ def autocomplete_author(request):      return HttpResponse(data, content_type='text/plain') -def new_item(model, frm, many=False): -    def func(request, parent_name, limits=''): -        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) -        dct = {'title': unicode(_(u'New %s' % model_name.lower())), -               'many': many} -        if request.method == 'POST': -            dct['form'] = frm(request.POST, limits=limits) -            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(request, 'window.html', dct) -        else: -            dct['form'] = frm(limits=limits) -        return render(request, 'window.html', dct) -    return func -  new_person = new_item(models.Person, forms.PersonForm)  new_person_noorga = new_item(models.Person, forms.NoOrgaPersonForm)  new_organization = new_item(models.Organization, forms.OrganizationForm) @@ -2488,6 +1573,7 @@ class OrganizationPersonEdit(LoginRequiredMixin, UpdateView):  show_document = show_item(models.Document, 'document')  get_document = get_item(models.Document, 'get_document', 'document') +display_document = display_item(models.Document)  document_search_wizard = wizards.SearchWizard.as_view( @@ -2497,11 +1583,16 @@ document_search_wizard = wizards.SearchWizard.as_view(  ) -class NewDocumentFormView(IshtarMixin, LoginRequiredMixin, -                          FormView): +class DocumentFormView(IshtarMixin, LoginRequiredMixin, +                       CreateView): +    page_name = _(u"New Document")      form_class = forms.DocumentForm      template_name = 'ishtar/form.html' -    success_url = 'document_search' +    model = models.Document + +    def get_success_url(self): +        return reverse('display-document', args=[self.object.pk]) +  """  class DocumentSelectMixin(IshtarMixin, LoginRequiredMixin, | 
