AtramhasisView.search_result()   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 41
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 41
rs 8.2026
c 0
b 0
f 0
cc 6
nop 1
1
import os
2
import urllib
3
4
from pyramid.httpexceptions import HTTPFound
5
from pyramid.i18n import TranslationStringFactory
6
from pyramid.response import FileResponse
7
from pyramid.response import Response
8
from pyramid.threadlocal import get_current_registry
9
from pyramid.view import view_config
10
from pyramid.view import view_defaults
11
from skosprovider_sqlalchemy.models import Collection
12
from skosprovider_sqlalchemy.models import Concept
13
from skosprovider_sqlalchemy.models import LabelType
14
from skosprovider_sqlalchemy.models import NoteType
15
from sqlalchemy.exc import NoResultFound
16
17
from atramhasis.audit import audit
18
from atramhasis.cache import invalidate_cache
19
from atramhasis.cache import invalidate_scheme_cache
20
from atramhasis.cache import list_region
21
from atramhasis.cache import tree_region
22
from atramhasis.errors import ConceptNotFoundException
23
from atramhasis.errors import ConceptSchemeNotFoundException
24
from atramhasis.errors import SkosRegistryNotFoundException
25
from atramhasis.utils import provider_is_external
26
from atramhasis.utils import update_last_visited_concepts
27
28
29
def labels_to_string(labels, ltype):
30
    labelstring = ''
31
    for label in (label for label in labels if label.labeltype_id == ltype):
32
        labelstring += label.label + ' (' + label.language_id + '), '
33
    return labelstring[:-2]
34
35
36
def get_definition(notes):
37
    for note in notes:
38
        if note.notetype_id == 'definition':
39
            return note.note
40
41
42
def sort_by_labels(concepts, locale, reverse=False):
43
    return sorted((x for x in concepts if x.label(locale)),
44
                  reverse=reverse,
45
                  key=lambda child: child.label(locale).label.lower()
46
                  ) + [x for x in concepts if not x.label(locale)]
47
48
def get_public_conceptschemes(skos_registry):
49
    """
50
    Get all conceptschemes that are visible through the public UI.
51
    """
52
    conceptschemes = [
53
        {'id': x.get_metadata()['id'],
54
         'conceptscheme': x.concept_scheme}
55
        for x in skos_registry.get_providers() if not any([not_shown in x.get_metadata()['subject']
56
                                                           for not_shown in ['external', 'hidden']])
57
        ]
58
59
    return conceptschemes
60
61
62
@view_defaults(accept='text/html', request_method='GET')
63
class AtramhasisView:
64
    """
65
    This object groups HTML views part of the public user interface.
66
    """
67
68
    def __init__(self, request):
69
        self.request = request
70
        self.skos_manager = self.request.data_managers['skos_manager']
71
        self.conceptscheme_manager = self.request.data_managers['conceptscheme_manager']
72
        if hasattr(request, 'skos_registry') and request.skos_registry is not None:
73
            self.skos_registry = self.request.skos_registry
74
        else:
75
            raise SkosRegistryNotFoundException()
76
77
    def _read_request_param(self, param):
78
        value = None
79
        if param in self.request.params:
80
            value = self.request.params.getone(param).strip()
81
            if not value:
82
                value = None  # pragma: no cover
83
        return value
84
85
    @view_config(name='favicon.ico')
86
    def favicon_view(self):
87
        """
88
        Return the favicon when requested from the web root.
89
        """
90
        here = os.path.dirname(__file__)
91
        icon = os.path.join(os.path.dirname(here), 'static', 'img', 'favicon.ico')
92
        response = FileResponse(
93
            icon,
94
            request=self.request,
95
            content_type='image/x-icon'
96
        )
97
        return response
98
99
100
    @view_config(route_name='home', renderer='atramhasis:templates/home.jinja2')
101
    def home_view(self):
102
        """
103
        Display the homepage.
104
        """
105
        return {'conceptschemes': get_public_conceptschemes(self.skos_registry)}
106
107
    @view_config(route_name='skosprovider.conceptschemes', renderer='atramhasis:templates/conceptschemes.jinja2')
108
    def conceptschemes_view(self):
109
        """
110
        Display a list of available conceptschemes.
111
        """
112
        return {'conceptschemes': get_public_conceptschemes(self.skos_registry)}
113
114
    @audit
115
    @view_config(route_name='skosprovider.conceptscheme', renderer='atramhasis:templates/conceptscheme.jinja2')
116
    def conceptscheme_view(self):
117
        """
118
        Display a single conceptscheme.
119
        """
120
        conceptschemes = get_public_conceptschemes(self.skos_registry)
121
122
        scheme_id = self.request.matchdict['scheme_id']
123
124
        provider = self.request.skos_registry.get_provider(scheme_id)
125
        if provider is False:
126
            raise ConceptSchemeNotFoundException(scheme_id)
127
128
        conceptscheme = provider.concept_scheme
129
        if 'atramhasis.force_display_label_language' in provider.metadata:
130
            locale = provider.metadata['atramhasis.force_display_label_language']
131
        else:
132
            locale = self.request.locale_name
133
        title = (conceptscheme.label(locale).label if (conceptscheme.label())
134
                 else scheme_id)
135
136
        scheme = {
137
            'scheme_id': scheme_id,
138
            'title': title,
139
            'uri': conceptscheme.uri,
140
            'labels': conceptscheme.labels,
141
            'notes': conceptscheme.notes,
142
            'sources': conceptscheme.sources,
143
            'top_concepts': provider.get_top_concepts(),
144
        }
145
146
        return {
147
            'conceptscheme': scheme,
148
            'conceptschemes': conceptschemes,
149
            'locale': locale,
150
            'show_tree': not provider_is_external(provider),
151
        }
152
153
    @audit
154
    @view_config(route_name='skosprovider.c', renderer='atramhasis:templates/concept.jinja2')
155
    def concept_view(self):
156
        """
157
        Display all about a single concept or collection.
158
        """
159
        conceptschemes = [
160
            {'id': x.get_metadata()['id'],
161
             'conceptscheme': x.concept_scheme}
162
            for x in self.skos_registry.get_providers() if not any([not_shown in x.get_metadata()['subject']
163
                                                                    for not_shown in ['external', 'hidden']])
164
            ]
165
166
        scheme_id = self.request.matchdict['scheme_id']
167
        c_id = self.request.matchdict['c_id']
168
        provider = self.request.skos_registry.get_provider(scheme_id)
169
        label = self._read_request_param('label')
170
        requested_type = self._read_request_param('type')
171
172
        if not provider:
173
            raise ConceptSchemeNotFoundException(scheme_id)
174
        if 'atramhasis.force_display_label_language' in provider.metadata:
175
            locale = provider.metadata['atramhasis.force_display_label_language']
176
        else:
177
            locale = self.request.locale_name
178
        try:
179
            c = self.skos_manager.get_thing(c_id, provider.conceptscheme_id)
180
            if isinstance(c, Concept):
181
                concept_type = "Concept"
182
            elif isinstance(c, Collection):
183
                concept_type = "Collection"
184
            else:
185
                return Response('Thing without type: ' + str(c_id), status_int=500)
186
            url = self.request.route_url('skosprovider.c', scheme_id=scheme_id, c_id=c_id)
187
            update_last_visited_concepts(self.request, {'label': c.label(locale).label, 'url': url})
188
            return {
189
                'concept': c, 
190
                'conceptType': concept_type, 
191
                'scheme_id': scheme_id,
192
                'conceptschemes': conceptschemes, 
193
                'provider': provider,
194
                'locale': locale,
195
                'type': requested_type,
196
                'label': label,
197
                'show_tree': not provider_is_external(provider)
198
            }
199
        except NoResultFound:
200
            raise ConceptNotFoundException(c_id)
201
202
    @view_config(route_name='skosprovider.conceptscheme.cs', renderer='atramhasis:templates/search_result.jinja2')
203
    def search_result(self):
204
        """
205
        Display search results
206
        """
207
        conceptschemes = [
208
            {'id': x.get_metadata()['id'],
209
             'conceptscheme': x.concept_scheme}
210
            for x in self.skos_registry.get_providers() if not any([not_shown in x.get_metadata()['subject']
211
                                                                    for not_shown in ['external', 'hidden']])
212
            ]
213
214
        scheme_id = self.request.matchdict['scheme_id']
215
        label = self._read_request_param('label')
216
        requested_type = self._read_request_param('type')
217
        provider = self.skos_registry.get_provider(scheme_id)
218
        if provider:
219
            if 'atramhasis.force_display_label_language' in provider.metadata:
220
                locale = provider.metadata['atramhasis.force_display_label_language']
221
            else:
222
                locale = self.request.locale_name
223
            if label is not None:
224
                concepts = provider.find(
225
                    {'label': label, 'type': requested_type},
226
                    language=locale,
227
                    sort='label'
228
                )
229
            elif (label is None) and (requested_type is not None):
230
                concepts = provider.find(
231
                    {'type': requested_type}, language=locale, sort='label'
232
                )
233
            else:
234
                concepts = provider.get_all(language=locale, sort='label')
235
            return {
236
                'concepts': concepts,
237
                'scheme_id': scheme_id,
238
                'conceptschemes': conceptschemes,
239
                'type': requested_type,
240
                'label': label
241
            }
242
        return Response(content_type='text/plain', status_int=404)
243
244
    @view_config(route_name='locale')
245
    def set_locale_cookie(self):
246
        """
247
        Set a language cookie to remember what language a user requested.
248
        """
249
        settings = get_current_registry().settings
250
        default_lang = settings.get('pyramid.default_locale_name')
251
        available_languages = settings.get('available_languages', default_lang).split()
252
        [x.lower() for x in available_languages]
253
        language = self.request.GET.get('language', default_lang).lower()
254
        if language not in available_languages:
255
            language = default_lang
256
257
        referer = self.request.referer
258
        if referer is not None:
259
            response = HTTPFound(location=referer)
260
        else:
261
            response = HTTPFound(location=self.request.route_url('home'))
262
263
        response.set_cookie('_LOCALE_',
264
                            value=language,
265
                            max_age=31536000)  # max_age = year
266
        return response
267
268
    @audit
269
    @view_config(route_name='skosprovider.conceptscheme.csv', renderer='csv')
270
    def results_csv(self):
271
        """
272
        Download search results in CSV format, allowing further processing.
273
        """
274
        header = ['conceptscheme', 'id', 'uri', 'type', 'label', 'prefLabels', 'altLabels', 'definition', 'broader',
275
                  'narrower', 'related']
276
        rows = []
277
        scheme_id = self.request.matchdict['scheme_id']
278
        label = self._read_request_param('label')
279
        requested_type = self._read_request_param('type')
280
        provider = self.skos_registry.get_provider(scheme_id)
281
        if provider:
282
            if label is not None:
283
                concepts = self.conceptscheme_manager.find(
284
                    provider.conceptscheme_id, {'label': label, 'type': requested_type}
285
                )
286
            elif (label is None) and (requested_type is not None):
287
                concepts = self.conceptscheme_manager.find(
288
                    provider.conceptscheme_id, {'type': requested_type}
289
                )
290
            else:
291
                concepts = self.conceptscheme_manager.get_all(provider.conceptscheme_id)
292
            for concept in concepts:
293
                if concept.type == 'concept':
294
                    rows.append((
295
                        scheme_id, concept.concept_id, concept.uri, concept.type,
296
                        concept.label(self.request.locale_name).label,
297
                        labels_to_string(concept.labels, 'prefLabel'), labels_to_string(concept.labels, 'altLabel'),
298
                        get_definition(concept.notes), [c.concept_id for c in concept.broader_concepts],
299
                        [c.concept_id for c in concept.narrower_concepts],
300
                        [c.concept_id for c in concept.related_concepts]))
301
                else:
302
                    rows.append((
303
                        scheme_id, concept.concept_id, concept.uri, concept.type,
304
                        concept.label(self.request.locale_name).label,
305
                        labels_to_string(concept.labels, 'prefLabel'), labels_to_string(concept.labels, 'altLabel'),
306
                        get_definition(concept.notes), '', [c.concept_id for c in concept.members], ''))
307
        return {
308
            'header': header,
309
            'rows': rows,
310
            'filename': 'atramhasis_export'
311
        }
312
313
    @view_config(route_name='scheme_tree_html', renderer='scheme_tree.jinja2')
314
    @view_config(route_name='scheme_tree', renderer='json', accept='application/json')
315
    def results_tree_json(self):
316
        scheme_id = self.request.matchdict['scheme_id']
317
        language = self.request.params.get('language') or self.request.locale_name
318
        dicts = self.get_results_tree(scheme_id, language)
319
        if dicts:
320
            if 'text/html' not in self.request.accept:
321
                return dicts
322
            else:
323
                return {'tree': dicts}
324
        else:
325
            return Response(status_int=404)
326
327
    @tree_region.cache_on_arguments()
328
    def get_results_tree(self, scheme_id, locale):
329
        skostree = self.get_scheme(scheme_id, locale)
330
        return [self.parse_thing(thing, None) for thing in skostree]
331
332
    def get_scheme(self, scheme, locale):
333
        scheme_tree = []
334
        provider = self.skos_registry.get_provider(scheme)
335
        if provider:
336
            conceptscheme_id = provider.conceptscheme_id
337
338
            tco = self.conceptscheme_manager.get_concepts_for_scheme_tree(conceptscheme_id)
339
            tcl = self.conceptscheme_manager.get_collections_for_scheme_tree(conceptscheme_id)
340
341
            scheme_tree = sort_by_labels(tco, locale) + sort_by_labels(tcl, locale)
342
343
        return scheme_tree
344
345
    def parse_thing(self, thing, parent_tree_id):
346
        tree_id = self.create_treeid(parent_tree_id, thing.concept_id)
347
        locale = self.request.params.get('language', self.request.locale_name)
348
349
        if thing.type and thing.type == 'collection':
350
            cs = [member for member in thing.members] if hasattr(thing, 'members') else []
351
        else:
352
            cs = [c for c in thing.narrower_concepts]
353
            cs = cs + [c for c in thing.narrower_collections]
354
355
        sortedcs = sort_by_labels(cs, locale)
356
        children = [self.parse_thing(c, tree_id) for index, c in enumerate(sortedcs, 1)]
357
        dict_thing = {
358
            'id': tree_id,
359
            'concept_id': thing.concept_id,
360
            'type': thing.type,
361
            'label': thing.label(locale).label if thing.label(locale) else None,
362
            'children': children
363
        }
364
365
        return dict_thing
366
367
    @staticmethod
368
    def create_treeid(parent_tree_id, concept_id):
369
        if parent_tree_id is None:
370
            return urllib.parse.quote(str(concept_id), safe="")
371
        else:
372
            return parent_tree_id + "|" + urllib.parse.quote(str(concept_id), safe="")
373
374
375
@view_defaults(accept='application/json', renderer='json')
376
class AtramhasisListView:
377
    """
378
    This object groups list views part for the user interface.
379
    """
380
381
    def __init__(self, request):
382
        self.request = request
383
        self.skos_manager = self.request.data_managers['skos_manager']
384
        self.localizer = request.localizer
385
        self._ = TranslationStringFactory('atramhasis')
386
387
    @view_config(route_name='labeltypes')
388
    def labeltype_list_view(self):
389
        return self.get_list(LabelType)
390
391
    @view_config(route_name='notetypes')
392
    def notetype_list_view(self):
393
        return self.get_list(NoteType)
394
395
    @list_region.cache_on_arguments()
396
    def get_list(self, listtype):
397
        return [{"key": ltype.name, "label": self.localizer.translate(self._(ltype.name))}
398
                for ltype in self.skos_manager.get_by_list_type(listtype)]
399
400
401
@view_defaults(accept='text/html')
402
class AtramhasisAdminView:
403
    """
404
    This object groups HTML views part of the admin user interface.
405
    """
406
407
    def __init__(self, request):
408
        self.request = request
409
        self.logged_in = request.authenticated_userid
410
        if hasattr(request, 'skos_registry') and request.skos_registry is not None:
411
            self.skos_registry = self.request.skos_registry
412
        else:
413
            raise SkosRegistryNotFoundException()
414
415
    @view_config(route_name='admin', renderer='atramhasis:templates/admin.jinja2', permission='edit')
416
    def admin_view(self):
417
        return {'admin': None}
418
419
    @view_config(route_name='scheme_tree_invalidate', renderer='json', accept='application/json', permission='edit')
420
    def invalidate_scheme_tree(self):
421
        scheme_id = self.request.matchdict['scheme_id']
422
        invalidate_scheme_cache(scheme_id)
423
        return Response(status_int=200)
424
425
    @view_config(route_name='tree_invalidate', renderer='json', accept='application/json', permission='edit')
426
    def invalidate_tree(self):
427
        invalidate_cache()
428
        return Response(status_int=200)
429