diff options
Diffstat (limited to 'ishtar_common/views_item.py')
-rw-r--r-- | ishtar_common/views_item.py | 182 |
1 files changed, 143 insertions, 39 deletions
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index cde3f8d87..6f4abdee9 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -22,7 +22,8 @@ from django.db.models.fields import FieldDoesNotExist from django.http import HttpResponse from django.shortcuts import render from django.template import loader -from django.utils.translation import ugettext, ugettext_lazy as _ +from django.utils.translation import ugettext, ugettext_lazy as _, \ + activate, deactivate, pgettext_lazy from tidylib import tidy_document as tidy from unidecode import unidecode from weasyprint import HTML, CSS @@ -65,17 +66,29 @@ CURRENT_ITEM_KEYS = ( CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) +def get_autocomplete_query(request, label_attributes, extra=None): + q = request.GET.get('term') or "" + if not label_attributes: + return Q(pk__isnull=True) + query = Q() + if extra: + query = Q(**extra) + for q in q.split(' '): + if not q: + continue + sub_q = Q(**{label_attributes[0] + "__icontains": q}) + for other_label in label_attributes[1:]: + sub_q = sub_q | Q(**{other_label + "__icontains": q}) + query = query & sub_q + return query + + def get_autocomplete_item(model, extra=None): if not extra: extra = {} def func(request, current_right=None): - q = request.GET.get('term') or "" - query = Q(**extra) - for q in q.split(' '): - if not q: - continue - query = query & Q(cached_label__icontains=q) + query = get_autocomplete_query(request, ['cached_label'], extra=extra) limit = 20 objects = model.objects.filter(query)[:limit] data = json.dumps([{'id': obj.pk, 'value': obj.cached_label} @@ -133,22 +146,25 @@ def display_item(model, extra_dct=None, show_url=None): return func -def show_item(model, name, extra_dct=None): +def show_item(model, name, extra_dct=None, model_for_perms=None): def func(request, pk, **dct): - allowed, own = check_model_access_control(request, model) + check_model = model + if model_for_perms: + check_model = model_for_perms + allowed, own = check_model_access_control(request, check_model) if not allowed: return HttpResponse('', content_type="application/xhtml") q = model.objects if own: if not hasattr(request.user, 'ishtaruser'): - return HttpResponse('NOK') + return HttpResponse('') query_own = model.get_query_owns(request.user.ishtaruser) if query_own: q = q.filter(query_own).distinct() try: item = q.get(pk=pk) - except ObjectDoesNotExist: - return HttpResponse('NOK') + except (ObjectDoesNotExist, ValueError): + return HttpResponse('') doc_type = 'type' in dct and dct.pop('type') url_name = u"/".join(reverse('show-' + name, args=['0', ''] ).split('/')[:-2]) + u"/" @@ -325,6 +341,12 @@ def _push_to_list(obj, current_group, depth): current_group.append(obj) +def is_true_string(val): + val = unicode(val).lower().replace(u'"', u"") + if val in (u"1", u"true", unicode(_(u"True")).lower()): + return True + + def _parse_parentheses(s): """ Parse parentheses into list. @@ -368,14 +390,31 @@ def _parse_query_string(string, request_keys, current_dct, exc_dct): if excluded: term = term[1:] if term in request_keys: - term = request_keys[term] dct = current_dct - if excluded: - dct = exc_dct - if term in dct: - dct[term] += u";" + query + term = request_keys[term] + # callable request key for complex queries + if callable(term): + is_true = is_true_string(query) + if excluded: + is_true = not is_true + cfltr, cexclude, cextra = term(is_true=is_true) + if cfltr: + if 'and_reqs' not in dct: + dct['and_reqs'] = [] + dct['and_reqs'].append(cfltr) + if cexclude: + if 'exc_and_reqs' not in dct: + dct['exc_and_reqs'] = [] + dct['exc_and_reqs'].append(cexclude) + if cextra: + dct['extras'].append(cextra) else: - dct[term] = query + if excluded: + dct = exc_dct + if term in dct: + dct[term] += u";" + query + else: + dct[term] = query return u"" for reserved_char in FORBIDDEN_CHAR: string = string.replace(reserved_char, u"") @@ -487,6 +526,8 @@ def _search_manage_search_vector(model, dct, exc_dct, request_keys): search_query = \ search_query.replace(u'(', u'').replace(u')', u'').strip() if search_query: + if 'extras' not in dct: + dct['extras'] = [] dct['extras'].append( {'where': [model._meta.db_table + ".search_vector @@ (to_tsquery(%s, %s)) = true"], @@ -526,13 +567,47 @@ def _manage_bool_fields(model, bool_fields, reversed_bool_fields, dct, or_reqs): pass +today_lbl = pgettext_lazy("key for text search", u"today"), +TODAYS = ['today'] + +for language_code, language_lbl in settings.LANGUAGES: + activate(language_code) + TODAYS.append(unicode(today_lbl)) + deactivate() + + def _manage_dated_fields(dated_fields, dct): for k in dated_fields: if k in dct: if not dct[k]: dct.pop(k) + continue + value = dct[k].replace('"', '').strip() + has_today = False + for today in TODAYS: + if value.startswith(today): + base_date = datetime.date.today() + value = value[len(today):].replace(' ', '') + if value and value[0] in (u"-", u"+"): + sign = value[0] + try: + days = int(value[1:]) + except ValueError: + days = 0 + if days: + if sign == u"-": + base_date = base_date - datetime.timedelta( + days=days) + else: + base_date = base_date + datetime.timedelta( + days=days) + dct[k] = base_date.strftime('%Y-%m-%d') + has_today = True + break + if has_today: + continue try: - items = dct[k].replace('"', '').split('/') + items = value.split('/') assert len(items) == 3 dct[k] = virtualtime.datetime(*map(lambda x: int(x), reversed(items))) \ @@ -795,7 +870,12 @@ def _construct_query(relation_types, dct, or_reqs, and_reqs): query |= Q(**alt_dct) query = _manage_relation_types(relation_types, dct, query, or_reqs) + done = [] for and_req in and_reqs: + str_q = unicode(and_req) + if str_q in done: + continue + done.append(str_q) query = query & and_req return query @@ -880,11 +960,13 @@ DEFAULT_ROW_NUMBER = 10 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): +def get_item(model, func_name, default_name, extra_request_keys=None, + base_request=None, bool_fields=None, reversed_bool_fields=None, + dated_fields=None, associated_models=None, + relative_session_names=None, specific_perms=None, + own_table_cols=None, relation_types_prefix=None, + do_not_deduplicate=False, model_for_perms=None, + alt_query_own=None): """ Generic treatment of tables @@ -904,6 +986,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[], :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). + :param model_for_perms: use another model to check permission + :param alt_query_own: name of alternate method to get query_own :return: """ def func(request, data_type='json', full=False, force_own=False, @@ -915,10 +999,15 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if 'type' in dct: data_type = dct.pop('type') if not data_type: - EMPTY = '[]' data_type = 'json' + if data_type == "json": + EMPTY = '[]' - allowed, own = check_model_access_control(request, model, + model_to_check = model + if model_for_perms: + model_to_check = model_for_perms + + allowed, own = check_model_access_control(request, model_to_check, available_perms) if not allowed: return HttpResponse(EMPTY, content_type='text/plain') @@ -934,13 +1023,16 @@ def get_item(model, func_name, default_name, extra_request_keys=[], q = models.IshtarUser.objects.filter(user_ptr=request.user) if not q.count(): return HttpResponse(EMPTY, content_type='text/plain') - query_own = model.get_query_owns(q.all()[0]) + if alt_query_own: + query_own = getattr(model, alt_query_own)(q.all()[0]) + else: + query_own = model.get_query_owns(q.all()[0]) # 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) + my_extra_request_keys = copy(extra_request_keys or []) if base_request is None and hasattr(model, 'BASE_REQUEST'): if callable(model.BASE_REQUEST): my_base_request = model.BASE_REQUEST(request) @@ -953,32 +1045,35 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if not bool_fields and hasattr(model, 'BOOL_FIELDS'): my_bool_fields = model.BOOL_FIELDS[:] else: - my_bool_fields = bool_fields[:] + my_bool_fields = bool_fields[:] if bool_fields else [] 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[:] + my_reversed_bool_fields = reversed_bool_fields[:] \ + if reversed_bool_fields else [] if not dated_fields and hasattr(model, 'DATED_FIELDS'): my_dated_fields = model.DATED_FIELDS[:] else: - my_dated_fields = dated_fields[:] + my_dated_fields = dated_fields[:] if dated_fields else [] if not associated_models and hasattr(model, 'ASSOCIATED_MODELS'): my_associated_models = model.ASSOCIATED_MODELS[:] else: - my_associated_models = associated_models[:] + my_associated_models = associated_models[:] \ + if associated_models else [] 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[:] + my_relative_session_names = relative_session_names[:] \ + if relative_session_names else [] 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) + my_relation_types_prefix = copy(relation_types_prefix) \ + if relation_types_prefix else {} - fields = [model._meta.get_field(k) - for k in get_all_field_names(model)] + fields = [model._meta.get_field(k) for k in get_all_field_names(model)] request_keys = dict([ (field.name, @@ -1039,6 +1134,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[], excluded_dct = {} and_reqs, or_reqs = [], [] exc_and_reqs, exc_or_reqs = [], [] + dct['extras'], dct['and_reqs'], dct['exc_and_reqs'] = [], [], [] if full == 'shortcut': if model.SLUG == "warehouse": @@ -1057,7 +1153,10 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if not val: continue req_keys = request_keys[k] - if type(req_keys) not in (list, tuple): + if callable(req_keys): + # callable request key for complex queries not managed on GET + continue + elif type(req_keys) not in (list, tuple): dct[req_keys] = val continue # multiple choice target @@ -1080,7 +1179,6 @@ def get_item(model, func_name, default_name, extra_request_keys=[], else: request.session[func_name] = dct - dct['extras'] = [] dct, excluded_dct = _search_manage_search_vector( model, dct, excluded_dct, request_keys) search_vector = "" @@ -1116,7 +1214,13 @@ def get_item(model, func_name, default_name, extra_request_keys=[], _manage_facet_search(model, dct, and_reqs) _manage_facet_search(model, excluded_dct, exc_and_reqs) - extras = dct.pop('extras') + extras = [] + if 'extras' in dct: + extras = dct.pop('extras') + if 'and_reqs' in dct: + and_reqs += dct.pop('and_reqs') + if 'exc_and_reqs' in dct: + exc_and_reqs += dct.pop('exc_and_reqs') _manage_clean_search_field(dct) _manage_clean_search_field(excluded_dct) |