recherche: première ébauche
[auf_framonde.git] / project / aldryn_search / utils.py
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 import six
5
6 from lxml.html.clean import Cleaner as LxmlCleaner
7
8 from django.core.exceptions import ImproperlyConfigured
9 from django.db import models
10 try:
11 from django.utils.encoding import force_unicode
12 except ImportError:
13 from django.utils.encoding import force_text as force_unicode
14 from django.utils.importlib import import_module
15 from django.utils.html import strip_tags as _strip_tags
16
17 from haystack import DEFAULT_ALIAS
18 from haystack.indexes import SearchIndex
19
20 from cms.utils.i18n import get_language_code
21
22 from .conf import settings
23
24
25 def alias_from_language(language):
26 """
27 Returns alias if alias is a valid language.
28 """
29 language = get_language_code(language)
30
31 if language == settings.ALDRYN_SEARCH_DEFAULT_LANGUAGE:
32 return DEFAULT_ALIAS
33 return language
34
35
36 def clean_join(separator, iterable):
37 """
38 Filters out iterable to only join non empty items.
39 """
40 return separator.join(filter(None, iterable))
41
42
43 def get_callable(string_or_callable):
44 """
45 If given a callable then it returns it, otherwise it resolves the path
46 and returns an object.
47 """
48 if callable(string_or_callable):
49 return string_or_callable
50 else:
51 module_name, object_name = string_or_callable.rsplit('.', 1)
52 if module_name.startswith('aldryn_search'):
53 module_name = "project." + module_name
54 module = import_module(module_name)
55 return getattr(module, object_name)
56
57
58 def _get_language_from_alias_func():
59 path_or_callable = settings.ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS
60
61 if path_or_callable:
62 try:
63 func = get_callable(path_or_callable)
64 except AttributeError as error:
65 raise ImproperlyConfigured(
66 'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s' % (str(error)))
67 if not callable(func):
68 raise ImproperlyConfigured(
69 'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s is not callable' % func)
70 else:
71 func = None
72 return func
73
74
75 def get_index_base():
76 index_string = settings.ALDRYN_SEARCH_INDEX_BASE_CLASS
77 try:
78 BaseClass = get_callable(index_string)
79 except AttributeError as error:
80 raise ImproperlyConfigured(
81 'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s' % (str(error)))
82
83 if not issubclass(BaseClass, SearchIndex):
84 raise ImproperlyConfigured(
85 'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s is not a subclass of haystack.indexes.SearchIndex' % index_string)
86
87 required_fields = ['text', 'language']
88
89 if not all(field in BaseClass.fields for field in required_fields):
90 raise ImproperlyConfigured('ALDRYN_SEARCH_INDEX_BASE_CLASS: %s must contain at least these fields: %s' % (
91 index_string, required_fields))
92 return BaseClass
93
94
95 def language_from_alias(alias):
96 """
97 Returns alias if alias is a valid language.
98 """
99 languages = [language[0] for language in settings.LANGUAGES]
100
101 return alias if alias in languages else None
102
103
104 def get_field_value(obj, name):
105 """
106 Given a model instance and a field name (or attribute),
107 returns the value of the field or an empty string.
108 """
109 fields = name.split('__')
110
111 name = fields[0]
112
113 try:
114 obj._meta.get_field(name)
115 except (AttributeError, models.FieldDoesNotExist):
116 # we catch attribute error because obj will not always be a model
117 # specially when going through multiple relationships.
118 value = getattr(obj, name, None) or ''
119 else:
120 value = getattr(obj, name)
121
122 if len(fields) > 1:
123 remaining = '__'.join(fields[1:])
124 return get_field_value(value, remaining)
125 return value
126
127
128 def get_model_path(model_or_string):
129 if not isinstance(model_or_string, six.string_types):
130 # it's a model class
131 app_label = model_or_string._meta.app_label
132 model_name = model_or_string._meta.object_name
133 model_or_string = '{0}.{1}'.format(app_label, model_name)
134 return model_or_string.lower()
135
136
137 def strip_tags(value):
138 """
139 Returns the given HTML with all tags stripped.
140 We use lxml to strip all js tags and then hand the result to django's strip tags.
141 """
142 # strip any new lines
143 value = value.strip()
144
145 if value:
146 partial_strip = LxmlCleaner().clean_html(value)
147 value = _strip_tags(partial_strip)
148 return value