1
|
|
|
""" |
2
|
|
|
Module containing utility functions used by Atramhasis. |
3
|
|
|
""" |
4
|
|
|
import contextlib |
5
|
|
|
import copy |
6
|
|
|
from collections import deque |
7
|
|
|
|
8
|
|
|
from pyramid.httpexceptions import HTTPMethodNotAllowed |
9
|
|
|
from skosprovider.skos import Collection |
10
|
|
|
from skosprovider.skos import Concept |
11
|
|
|
from skosprovider.skos import ConceptScheme |
12
|
|
|
from skosprovider.skos import Label |
13
|
|
|
from skosprovider.skos import Note |
14
|
|
|
from skosprovider.skos import Source |
15
|
|
|
from skosprovider.uri import UriPatternGenerator |
16
|
|
|
from skosprovider_sqlalchemy.providers import SQLAlchemyProvider |
17
|
|
|
from sqlalchemy import engine_from_config |
18
|
|
|
from sqlalchemy import orm |
19
|
|
|
from sqlalchemy.orm import sessionmaker |
20
|
|
|
|
21
|
|
|
from atramhasis.data.models import Provider |
22
|
|
|
|
23
|
|
|
|
24
|
|
|
def from_thing(thing): |
25
|
|
|
""" |
26
|
|
|
Map a :class:`skosprovider_sqlalchemy.models.Thing` to a |
27
|
|
|
:class:`skosprovider.skos.Concept` or |
28
|
|
|
a :class:`skosprovider.skos.Collection`, depending on the type. |
29
|
|
|
|
30
|
|
|
:param skosprovider_sqlalchemy.models.Thing thing: Thing to map. |
31
|
|
|
:rtype: :class:`~skosprovider.skos.Concept` or |
32
|
|
|
:class:`~skosprovider.skos.Collection`. |
33
|
|
|
""" |
34
|
|
|
if thing.type and thing.type == 'collection': |
35
|
|
|
return Collection( |
36
|
|
|
id=thing.concept_id, |
37
|
|
|
uri=thing.uri, |
38
|
|
|
concept_scheme=ConceptScheme(thing.conceptscheme.uri), |
39
|
|
|
labels=[ |
40
|
|
|
Label(label.label, label.labeltype_id, label.language_id) |
41
|
|
|
for label in thing.labels |
42
|
|
|
], |
43
|
|
|
notes=[ |
44
|
|
|
Note(n.note, n.notetype_id, n.language_id) |
45
|
|
|
for n in thing.notes |
46
|
|
|
], |
47
|
|
|
sources=[ |
48
|
|
|
Source(s.citation) |
49
|
|
|
for s in thing.sources |
50
|
|
|
], |
51
|
|
|
members=[member.concept_id for member in thing.members] if hasattr(thing, 'members') else [], |
52
|
|
|
member_of=[c.concept_id for c in thing.member_of], |
53
|
|
|
superordinates=[broader_concept.concept_id for broader_concept in thing.broader_concepts], |
54
|
|
|
infer_concept_relations=thing.infer_concept_relations |
55
|
|
|
) |
56
|
|
|
else: |
57
|
|
|
matches = {} |
58
|
|
|
for m in thing.matches: |
59
|
|
|
key = m.matchtype.name[:m.matchtype.name.find('Match')] |
60
|
|
|
if key not in matches: |
61
|
|
|
matches[key] = [] |
62
|
|
|
matches[key].append(m.uri) |
63
|
|
|
return Concept( |
64
|
|
|
id=thing.concept_id, |
65
|
|
|
uri=thing.uri, |
66
|
|
|
concept_scheme=ConceptScheme(thing.conceptscheme.uri), |
67
|
|
|
labels=[ |
68
|
|
|
Label(label.label, label.labeltype_id, label.language_id) |
69
|
|
|
for label in thing.labels |
70
|
|
|
], |
71
|
|
|
notes=[ |
72
|
|
|
Note(n.note, n.notetype_id, n.language_id) |
73
|
|
|
for n in thing.notes |
74
|
|
|
], |
75
|
|
|
sources=[ |
76
|
|
|
Source(s.citation) |
77
|
|
|
for s in thing.sources |
78
|
|
|
], |
79
|
|
|
broader=[c.concept_id for c in thing.broader_concepts], |
80
|
|
|
narrower=[c.concept_id for c in thing.narrower_concepts], |
81
|
|
|
related=[c.concept_id for c in thing.related_concepts], |
82
|
|
|
member_of=[c.concept_id for c in thing.member_of], |
83
|
|
|
subordinate_arrays=[narrower_collection.concept_id for narrower_collection in thing.narrower_collections], |
84
|
|
|
matches=matches, |
85
|
|
|
) |
86
|
|
|
|
87
|
|
|
|
88
|
|
|
def internal_providers_only(fn): |
89
|
|
|
""" |
90
|
|
|
aspect oriented way to check if provider is internal when calling the decorated function |
91
|
|
|
|
92
|
|
|
:param fn: the decorated function |
93
|
|
|
:return: around advice |
94
|
|
|
:raises pyramid.httpexceptions.HTTPMethodNotAllowed: when provider is not internal |
95
|
|
|
""" |
96
|
|
|
def advice(parent_object, *args, **kw): |
97
|
|
|
|
98
|
|
|
if isinstance(parent_object.provider, SQLAlchemyProvider) and \ |
99
|
|
|
'external' not in parent_object.provider.get_metadata()['subject']: |
100
|
|
|
return fn(parent_object, *args, **kw) |
101
|
|
|
else: |
102
|
|
|
raise HTTPMethodNotAllowed() |
103
|
|
|
|
104
|
|
|
return advice |
105
|
|
|
|
106
|
|
|
|
107
|
|
|
def update_last_visited_concepts(request, concept_data): |
108
|
|
|
deque_last_visited = deque(maxlen=4) |
109
|
|
|
deque_last_visited.extend(request.session.get('last_visited', [])) |
110
|
|
|
try: |
111
|
|
|
# Try to remove concept from the queue to prevent double entries |
112
|
|
|
deque_last_visited.remove(concept_data) |
113
|
|
|
except ValueError: |
114
|
|
|
# Concept is not in the queue |
115
|
|
|
pass |
116
|
|
|
# Add concept to the queue |
117
|
|
|
deque_last_visited.append(concept_data) |
118
|
|
|
request.session['last_visited'] = list(deque_last_visited) |
119
|
|
|
|
120
|
|
|
|
121
|
|
|
def label_sort(concepts, language='any'): |
122
|
|
|
if not concepts: |
123
|
|
|
return [] |
124
|
|
|
return sorted( |
125
|
|
|
concepts, key=lambda concept: concept._sortkey(key='sortlabel', |
126
|
|
|
language=language) |
127
|
|
|
) |
128
|
|
|
|
129
|
|
|
|
130
|
|
|
def db_provider_to_skosprovider(db_provider: Provider) -> SQLAlchemyProvider: |
131
|
|
|
"""Create a SQLAlchemyProvider from a atramhasis.data.models.Provider. |
132
|
|
|
|
133
|
|
|
:param db_provider: The Provider to use as basis for the SQLAlchemyProvider. |
134
|
|
|
:return: An SQLAlchemyProvider with the data from the `db_provider` |
135
|
|
|
""" |
136
|
|
|
metadata = copy.deepcopy(db_provider.meta) |
137
|
|
|
metadata["conceptscheme_id"] = db_provider.conceptscheme_id |
138
|
|
|
metadata['atramhasis.id_generation_strategy'] = db_provider.id_generation_strategy |
139
|
|
|
metadata["id"] = db_provider.id |
140
|
|
|
return SQLAlchemyProvider( |
141
|
|
|
metadata=metadata, |
142
|
|
|
session=orm.object_session(db_provider), |
143
|
|
|
expand_strategy=db_provider.expand_strategy.value, |
144
|
|
|
uri_generator=UriPatternGenerator(db_provider.uri_pattern), |
145
|
|
|
) |
146
|
|
|
|
147
|
|
|
|
148
|
|
|
@contextlib.contextmanager |
149
|
|
|
def db_session(settings): |
150
|
|
|
engine = engine_from_config(settings, 'sqlalchemy.') |
151
|
|
|
session_maker = sessionmaker(bind=engine) |
152
|
|
|
session = session_maker() |
153
|
|
|
try: |
154
|
|
|
yield session |
155
|
|
|
session.commit() |
156
|
|
|
except Exception: |
157
|
|
|
session.rollback() |
158
|
|
|
raise |
159
|
|
|
finally: |
160
|
|
|
session.close() |
161
|
|
|
|