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