kobai-sdk 0.3.4rc2__py3-none-any.whl → 0.3.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kobai-sdk might be problematic. Click here for more details.
- kobai/mobi.py +733 -0
- kobai/mobi_config.py +19 -0
- kobai/ms_authenticate.py +2 -2
- kobai/tenant_client.py +123 -1
- {kobai_sdk-0.3.4rc2.dist-info → kobai_sdk-0.3.5rc1.dist-info}/METADATA +2 -1
- {kobai_sdk-0.3.4rc2.dist-info → kobai_sdk-0.3.5rc1.dist-info}/RECORD +9 -7
- {kobai_sdk-0.3.4rc2.dist-info → kobai_sdk-0.3.5rc1.dist-info}/WHEEL +0 -0
- {kobai_sdk-0.3.4rc2.dist-info → kobai_sdk-0.3.5rc1.dist-info}/licenses/LICENSE +0 -0
- {kobai_sdk-0.3.4rc2.dist-info → kobai_sdk-0.3.5rc1.dist-info}/top_level.txt +0 -0
kobai/mobi.py
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import urllib.parse
|
|
3
|
+
import json
|
|
4
|
+
import base64
|
|
5
|
+
from random import randrange
|
|
6
|
+
from requests_toolbelt.multipart.encoder import MultipartEncoder
|
|
7
|
+
|
|
8
|
+
from .mobi_config import MobiSettings
|
|
9
|
+
|
|
10
|
+
def special_request(api_url, mobi_config, **kwargs):
|
|
11
|
+
if mobi_config.use_cookies:
|
|
12
|
+
response = requests.get(api_url, cookies={'mobi_web_token':mobi_config.cookies}, **kwargs)
|
|
13
|
+
else:
|
|
14
|
+
response = requests.get(api_url, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password), **kwargs)
|
|
15
|
+
return(response)
|
|
16
|
+
|
|
17
|
+
def special_post(api_url, mobi_config, **kwargs):
|
|
18
|
+
if mobi_config.use_cookies:
|
|
19
|
+
response = requests.post(api_url, cookies={'mobi_web_token':mobi_config.cookies}, **kwargs)
|
|
20
|
+
else:
|
|
21
|
+
response = requests.post(api_url, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password), **kwargs)
|
|
22
|
+
return(response)
|
|
23
|
+
|
|
24
|
+
def special_put(api_url, mobi_config, **kwargs):
|
|
25
|
+
if mobi_config.use_cookies:
|
|
26
|
+
response = requests.put(api_url, cookies={'mobi_web_token':mobi_config.cookies}, **kwargs)
|
|
27
|
+
else:
|
|
28
|
+
response = requests.put(api_url, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password), **kwargs)
|
|
29
|
+
return(response)
|
|
30
|
+
|
|
31
|
+
def special_delete(api_url, mobi_config, **kwargs):
|
|
32
|
+
if mobi_config.use_cookies:
|
|
33
|
+
response = requests.delete(api_url, cookies={'mobi_web_token':mobi_config.cookies}, **kwargs)
|
|
34
|
+
else:
|
|
35
|
+
response = requests.delete(api_url, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password), **kwargs)
|
|
36
|
+
return(response)
|
|
37
|
+
|
|
38
|
+
##############################
|
|
39
|
+
# Mobi Pull
|
|
40
|
+
##############################
|
|
41
|
+
|
|
42
|
+
def get_tenant(top_level_ontology_name, mobi_config: MobiSettings):
|
|
43
|
+
#Find Ontology Record
|
|
44
|
+
|
|
45
|
+
ont_record_id = _get_ont_record_by_name(top_level_ontology_name, mobi_config)
|
|
46
|
+
print("Mobi Ontology Record ID:", ont_record_id)
|
|
47
|
+
#Get Deprecated Nodes
|
|
48
|
+
|
|
49
|
+
api_url = mobi_config.mobi_api_url + "/ontologies/" + urllib.parse.quote_plus(ont_record_id) + "/property-ranges"
|
|
50
|
+
#response = requests.get(api_url, verify=False, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
51
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
52
|
+
|
|
53
|
+
prop_ranges = response.json()["propertyToRanges"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
api_url = mobi_config.mobi_api_url + "/ontologies/" + urllib.parse.quote_plus(ont_record_id) + "/ontology-stuff"
|
|
57
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
58
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
59
|
+
|
|
60
|
+
########################
|
|
61
|
+
# Deprecated Classes
|
|
62
|
+
########################
|
|
63
|
+
|
|
64
|
+
deprecated = []
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
resp_data = response.json()["iriList"]["deprecatedIris"]
|
|
68
|
+
except requests.exceptions.JSONDecodeError:
|
|
69
|
+
resp_data = []
|
|
70
|
+
|
|
71
|
+
for iri in resp_data:
|
|
72
|
+
deprecated.append(iri)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
resp_data = response.json()["importedIRIs"]
|
|
76
|
+
except requests.exceptions.JSONDecodeError:
|
|
77
|
+
resp_data = []
|
|
78
|
+
|
|
79
|
+
for o in resp_data:
|
|
80
|
+
for iri in o["deprecatedIris"]:
|
|
81
|
+
deprecated.append(iri)
|
|
82
|
+
|
|
83
|
+
########################
|
|
84
|
+
# Properties
|
|
85
|
+
########################
|
|
86
|
+
|
|
87
|
+
data_properties = []
|
|
88
|
+
object_properties = []
|
|
89
|
+
|
|
90
|
+
for p in response.json()["iriList"]["dataProperties"]:
|
|
91
|
+
data_properties.append(p)
|
|
92
|
+
|
|
93
|
+
for p in response.json()["iriList"]["objectProperties"]:
|
|
94
|
+
object_properties.append(p)
|
|
95
|
+
|
|
96
|
+
for o in response.json()["importedIRIs"]:
|
|
97
|
+
for p in o["dataProperties"]:
|
|
98
|
+
data_properties.append(p)
|
|
99
|
+
|
|
100
|
+
for o in response.json()["importedIRIs"]:
|
|
101
|
+
for p in o["objectProperties"]:
|
|
102
|
+
object_properties.append(p)
|
|
103
|
+
|
|
104
|
+
all_properties = data_properties + object_properties
|
|
105
|
+
|
|
106
|
+
########################
|
|
107
|
+
# Classes and Domains
|
|
108
|
+
########################
|
|
109
|
+
|
|
110
|
+
domains = {}
|
|
111
|
+
concepts = {}
|
|
112
|
+
prop_domains = {}
|
|
113
|
+
|
|
114
|
+
for ont in _get_classes_by_ont(ont_record_id, mobi_config):
|
|
115
|
+
for c in ont["classes"]:
|
|
116
|
+
if c not in deprecated:
|
|
117
|
+
d = _domain_from_uri(c, mobi_config)
|
|
118
|
+
c_lu = _fix_uri(c, "concept", mobi_config)
|
|
119
|
+
|
|
120
|
+
if d not in domains:
|
|
121
|
+
domains[d] = {"name": d, "concepts": [], "color": ""}
|
|
122
|
+
|
|
123
|
+
#Add Leaf Ontology Concepts
|
|
124
|
+
loc = _parent_uri_from_uri(c)
|
|
125
|
+
loc_lu = _fix_uri(loc, "concept", mobi_config)
|
|
126
|
+
if loc_lu not in concepts:
|
|
127
|
+
name = _name_from_uri(loc, mobi_config)
|
|
128
|
+
concepts[loc_lu] = {"label": name, "domainName": d, "name": name, "uri": loc_lu, "properties": [], "relations": [], "inheritedConcepts": []}
|
|
129
|
+
|
|
130
|
+
#Add Class Concepts
|
|
131
|
+
name = _name_from_uri(c, mobi_config)
|
|
132
|
+
concepts[c_lu] = {"label": name, "domainName": d, "name": name, "uri": c_lu, "properties": [], "relations": [], "inheritedConcepts": [loc_lu]}
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
resp_data = response.json()["classToAssociatedProperties"]
|
|
136
|
+
except requests.exceptions.JSONDecodeError:
|
|
137
|
+
resp_data = {}
|
|
138
|
+
|
|
139
|
+
class_to_props = resp_data
|
|
140
|
+
|
|
141
|
+
props_with_domain = []
|
|
142
|
+
for c in class_to_props:
|
|
143
|
+
for p in class_to_props[c]:
|
|
144
|
+
props_with_domain.append(p)
|
|
145
|
+
|
|
146
|
+
#for p in resp_data:
|
|
147
|
+
for p in all_properties:
|
|
148
|
+
if p not in props_with_domain:
|
|
149
|
+
leaf_ont_concept = _parent_uri_from_uri(p)
|
|
150
|
+
if leaf_ont_concept not in class_to_props:
|
|
151
|
+
class_to_props[leaf_ont_concept] = []
|
|
152
|
+
class_to_props[leaf_ont_concept].append(p)
|
|
153
|
+
|
|
154
|
+
for c in class_to_props:
|
|
155
|
+
if c not in deprecated:
|
|
156
|
+
for p in class_to_props[c]:
|
|
157
|
+
#loc_lu = _fix_uri(c, "concept", mobi_config)
|
|
158
|
+
|
|
159
|
+
if p in prop_ranges:
|
|
160
|
+
range = prop_ranges[p][0]
|
|
161
|
+
if range not in ["http://www.w3.org/2001/XMLSchema#string", "http://www.w3.org/2001/XMLSchema#number", "http://www.w3.org/2001/XMLSchema#boolean", "http://www.w3.org/2001/XMLSchema#dateTime"]:
|
|
162
|
+
range = "http://www.w3.org/2001/XMLSchema#string"
|
|
163
|
+
else:
|
|
164
|
+
range = "http://www.w3.org/2001/XMLSchema#string"
|
|
165
|
+
|
|
166
|
+
#range_lu = _fix_uri(prop_ranges[p][0], "concept", mobi_config)
|
|
167
|
+
|
|
168
|
+
#if p in prop_domains:
|
|
169
|
+
#for dc in prop_domains[p]:
|
|
170
|
+
#cp = dc + "/" + _label_from_uri(p)
|
|
171
|
+
cp = c + "/" + _label_from_uri(p)
|
|
172
|
+
#dc_lu = _fix_uri(dc, "concept", mobi_config)
|
|
173
|
+
dc_lu = _fix_uri(c, "concept", mobi_config)
|
|
174
|
+
|
|
175
|
+
#deal with case where literal and relation have same name
|
|
176
|
+
relation_labels = []
|
|
177
|
+
for p in object_properties:
|
|
178
|
+
if p in prop_ranges:
|
|
179
|
+
relation_labels.append(_label_from_uri(p))
|
|
180
|
+
|
|
181
|
+
if p in data_properties:
|
|
182
|
+
prop = {"label": _label_from_uri(p), "uri": _fix_uri(cp, "prop", mobi_config), "conceptUri": dc_lu, "propTypeUri": range, "dataClassTags": []}
|
|
183
|
+
if dc_lu in concepts:
|
|
184
|
+
if prop not in concepts[dc_lu]["properties"]:
|
|
185
|
+
if prop["label"] not in relation_labels:
|
|
186
|
+
concepts[dc_lu]["properties"].append(prop)
|
|
187
|
+
if p in object_properties:
|
|
188
|
+
if p in prop_ranges:
|
|
189
|
+
range_lu = _fix_uri(prop_ranges[p][0], "concept", mobi_config)
|
|
190
|
+
prop = {"label": _label_from_uri(p), "uri": _fix_uri(cp, "prop", mobi_config), "conceptUri": dc_lu, "relationTypeUri": range_lu, "dataClassTags": []}
|
|
191
|
+
if dc_lu in concepts:
|
|
192
|
+
if range_lu in concepts:
|
|
193
|
+
if prop not in concepts[dc_lu]["relations"]:
|
|
194
|
+
concepts[dc_lu]["relations"].append(prop)
|
|
195
|
+
else:
|
|
196
|
+
print("PROPERTY RANGE MISSING", p)
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
resp_data = response.json()["classHierarchy"]["childMap"]
|
|
200
|
+
except requests.exceptions.JSONDecodeError:
|
|
201
|
+
resp_data = {}
|
|
202
|
+
|
|
203
|
+
for c in resp_data:
|
|
204
|
+
c_lu = _fix_uri(c, "concept", mobi_config)
|
|
205
|
+
for pc in resp_data[c]:
|
|
206
|
+
pc_lu = _fix_uri(pc, "concept", mobi_config)
|
|
207
|
+
if c not in deprecated:
|
|
208
|
+
if c_lu in concepts:
|
|
209
|
+
concepts[c_lu]["inheritedConcepts"].append(pc_lu)
|
|
210
|
+
else:
|
|
211
|
+
print("RELATION TARGET MISSING", c_lu)
|
|
212
|
+
|
|
213
|
+
empty_leaf_concepts = []
|
|
214
|
+
for c in concepts:
|
|
215
|
+
if c.split("#")[1][0] == "_":
|
|
216
|
+
if len(concepts[c]["properties"]) == 0 and len(concepts[c]["relations"]) == 0:
|
|
217
|
+
empty_leaf_concepts.append(c)
|
|
218
|
+
for cc in concepts:
|
|
219
|
+
if c in concepts[cc]["inheritedConcepts"]:
|
|
220
|
+
concepts[cc]["inheritedConcepts"].remove(c)
|
|
221
|
+
else:
|
|
222
|
+
print("KEEPING LEAF ONTOLOGY", c)
|
|
223
|
+
for c in empty_leaf_concepts:
|
|
224
|
+
print("REMOVING LEAF ONTOLOGY", c)
|
|
225
|
+
del concepts[c]
|
|
226
|
+
|
|
227
|
+
tenant = {"solutionId": 0, "model": {"name": "AssetModel", "uri": "http://kobai/" + mobi_config.default_tenant_id + "/AssetModel"}, "tenantId": mobi_config.default_tenant_id, "domains": []}
|
|
228
|
+
tenant_encoded = {"solutionId": 0, "model": {"name": "AssetModel", "uri": "http://kobai/" + mobi_config.default_tenant_id + "/AssetModel"}, "tenantId": mobi_config.default_tenant_id, "domains": []}
|
|
229
|
+
_add_empty_tenant_metadata(tenant)
|
|
230
|
+
_add_empty_tenant_metadata(tenant_encoded)
|
|
231
|
+
|
|
232
|
+
di = 0
|
|
233
|
+
for dk, d in domains.items():
|
|
234
|
+
d['id'] = di
|
|
235
|
+
d['color'] = "#" + str(randrange(222222, 888888))
|
|
236
|
+
d_encoded = {}
|
|
237
|
+
d_encoded['id'] = di
|
|
238
|
+
d_encoded['color'] = "#" + str(randrange(222222, 888888))
|
|
239
|
+
d_encoded['name'] = dk
|
|
240
|
+
|
|
241
|
+
for _, c in concepts.items():
|
|
242
|
+
if dk == c['domainName']:
|
|
243
|
+
cprime = {"uri": c['uri'], "label": c['label'], "relations": c['relations'], "properties": c['properties'], "inheritedConcepts": c['inheritedConcepts']}
|
|
244
|
+
d['concepts'].append(cprime)
|
|
245
|
+
encodedConcepts = base64.b64encode(json.dumps(d['concepts']).encode('ascii')).decode('ascii')
|
|
246
|
+
d_encoded['concepts'] = encodedConcepts
|
|
247
|
+
tenant['domains'].append(d)
|
|
248
|
+
tenant_encoded['domains'].append(d_encoded)
|
|
249
|
+
di += 1
|
|
250
|
+
|
|
251
|
+
return tenant, tenant_encoded
|
|
252
|
+
|
|
253
|
+
def _get_classes_by_ont(ont_record_id, mobi_config):
|
|
254
|
+
api_url = mobi_config.mobi_api_url + "/ontologies/" + urllib.parse.quote_plus(ont_record_id) + "/imported-classes"
|
|
255
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
256
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
257
|
+
|
|
258
|
+
data = []
|
|
259
|
+
try:
|
|
260
|
+
resp_data = response.json()
|
|
261
|
+
except requests.exceptions.JSONDecodeError:
|
|
262
|
+
resp_data = []
|
|
263
|
+
|
|
264
|
+
#for ont in response.json():
|
|
265
|
+
for ont in resp_data:
|
|
266
|
+
record = {}
|
|
267
|
+
record["id"] = _trim_trailing_slash(ont["id"])
|
|
268
|
+
record["classes"] = []
|
|
269
|
+
for c in ont["classes"]:
|
|
270
|
+
record["classes"].append(c)
|
|
271
|
+
data.append(record)
|
|
272
|
+
|
|
273
|
+
api_url = mobi_config.mobi_api_url + "/ontologies/" + urllib.parse.quote_plus(ont_record_id) + "/classes"
|
|
274
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
275
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
resp_data = response.json()
|
|
279
|
+
except requests.exceptions.JSONDecodeError:
|
|
280
|
+
resp_data = []
|
|
281
|
+
|
|
282
|
+
record = {}
|
|
283
|
+
if len(resp_data) == 0:
|
|
284
|
+
return data
|
|
285
|
+
record["id"] = _parent_uri_from_uri(_trim_trailing_slash(response.json()[0]["@id"]))
|
|
286
|
+
record["classes"] = []
|
|
287
|
+
|
|
288
|
+
for c in resp_data:
|
|
289
|
+
record["classes"].append(c["@id"])
|
|
290
|
+
data.append(record)
|
|
291
|
+
|
|
292
|
+
return data
|
|
293
|
+
|
|
294
|
+
def _get_ont_record_by_name(name, mobi_config):
|
|
295
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records"
|
|
296
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
297
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
298
|
+
|
|
299
|
+
ont_record_id = ""
|
|
300
|
+
for r in response.json():
|
|
301
|
+
if r["http://purl.org/dc/terms/title"][0]["@value"] == name:
|
|
302
|
+
ont_record_id = r["@id"]
|
|
303
|
+
return ont_record_id
|
|
304
|
+
|
|
305
|
+
def _get_ont_record_by_url(url, mobi_config):
|
|
306
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records"
|
|
307
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
308
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
309
|
+
|
|
310
|
+
ont_record_id = ""
|
|
311
|
+
for r in response.json():
|
|
312
|
+
for u in r["http://mobi.com/ontologies/ontology-editor#ontologyIRI"]:
|
|
313
|
+
if url == _trim_trailing_slash(u["@id"]):
|
|
314
|
+
ont_record_id = r["@id"]
|
|
315
|
+
return ont_record_id
|
|
316
|
+
|
|
317
|
+
def _add_empty_tenant_metadata(tenant):
|
|
318
|
+
tenant["dataAccessTags"] = []
|
|
319
|
+
tenant["conceptAccessTags"] = []
|
|
320
|
+
tenant["dataSources"] = []
|
|
321
|
+
tenant["dataSets"] = []
|
|
322
|
+
tenant["collections"] = []
|
|
323
|
+
tenant["visualizations"] = []
|
|
324
|
+
tenant["queries"] = []
|
|
325
|
+
tenant["mappingDefs"] = []
|
|
326
|
+
tenant["dataSourceFileKeys"] = []
|
|
327
|
+
tenant["apiQueryProfiles"] = []
|
|
328
|
+
tenant["collectionVizs"] = []
|
|
329
|
+
tenant["collectionVizOrders"] = []
|
|
330
|
+
tenant["queryDataTags"] = []
|
|
331
|
+
tenant["queryCalcs"] = []
|
|
332
|
+
tenant["dataSourceSettings"] = []
|
|
333
|
+
tenant["publishedAPIs"] = []
|
|
334
|
+
tenant["scenarios"] = []
|
|
335
|
+
|
|
336
|
+
##############################
|
|
337
|
+
# Mobi Replace
|
|
338
|
+
##############################
|
|
339
|
+
|
|
340
|
+
def replace_tenant_to_mobi(kobai_tenant, top_level_ontology, mobi_config: MobiSettings):
|
|
341
|
+
json_ld = _create_jsonld(kobai_tenant, top_level_ontology)
|
|
342
|
+
_post_model(json_ld, top_level_ontology, mobi_config)
|
|
343
|
+
|
|
344
|
+
def replace_tenant_to_file(kobai_tenant, top_level_ontology):
|
|
345
|
+
return _create_jsonld(kobai_tenant, top_level_ontology)
|
|
346
|
+
|
|
347
|
+
def _create_jsonld(kobai_tenant, top_level_ontology):
|
|
348
|
+
output_json = []
|
|
349
|
+
uri = kobai_tenant["model"]["uri"]
|
|
350
|
+
uri = uri.replace("AssetModel", top_level_ontology)
|
|
351
|
+
|
|
352
|
+
group = {
|
|
353
|
+
"@id": uri,
|
|
354
|
+
"@type": ["http://www.w3.org/2002/07/owl#Ontology"],
|
|
355
|
+
"http://purl.org/dc/terms/description": [{"@value": "This model was exported from Kobai."}],
|
|
356
|
+
"http://purl.org/dc/terms/title": [{"@value": top_level_ontology}]
|
|
357
|
+
}
|
|
358
|
+
output_json.append(group)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
for dom in kobai_tenant["domains"]:
|
|
362
|
+
for con in dom["concepts"]:
|
|
363
|
+
|
|
364
|
+
group = {
|
|
365
|
+
"@id": con["uri"].replace("AssetModel", top_level_ontology),
|
|
366
|
+
"@type": ["http://www.w3.org/2002/07/owl#Class"],
|
|
367
|
+
"http://purl.org/dc/terms/title": [{"@value": con["label"]}]
|
|
368
|
+
}
|
|
369
|
+
if len(con["inheritedConcepts"]) > 0:
|
|
370
|
+
group["http://www.w3.org/2000/01/rdf-schema#subClassOf"] = []
|
|
371
|
+
for parent in con["inheritedConcepts"]:
|
|
372
|
+
group["http://www.w3.org/2000/01/rdf-schema#subClassOf"].append(
|
|
373
|
+
{"@id": parent.replace("AssetModel", top_level_ontology)}
|
|
374
|
+
)
|
|
375
|
+
output_json.append(group)
|
|
376
|
+
|
|
377
|
+
for prop in con["properties"]:
|
|
378
|
+
group = {
|
|
379
|
+
"@id": prop["uri"].replace("AssetModel", top_level_ontology),
|
|
380
|
+
"@type": ["http://www.w3.org/2002/07/owl#DatatypeProperty"],
|
|
381
|
+
"http://purl.org/dc/terms/title": [{"@value": prop["label"]}],
|
|
382
|
+
"http://www.w3.org/2000/01/rdf-schema#domain": [{"@id": con["uri"].replace("AssetModel", top_level_ontology)}],
|
|
383
|
+
"http://www.w3.org/2000/01/rdf-schema#range": [{"@id": prop["propTypeUri"]}]
|
|
384
|
+
}
|
|
385
|
+
output_json.append(group)
|
|
386
|
+
|
|
387
|
+
for rel in con["relations"]:
|
|
388
|
+
group = {
|
|
389
|
+
"@id": rel["uri"].replace("AssetModel", top_level_ontology),
|
|
390
|
+
"@type": ["http://www.w3.org/2002/07/owl#ObjectProperty"],
|
|
391
|
+
"http://purl.org/dc/terms/title": [{"@value": rel["label"]}],
|
|
392
|
+
"http://www.w3.org/2000/01/rdf-schema#domain": [{"@id": con["uri"].replace("AssetModel", top_level_ontology)}],
|
|
393
|
+
"http://www.w3.org/2000/01/rdf-schema#range": [{"@id": rel["relationTypeUri"].replace("AssetModel", top_level_ontology)}]
|
|
394
|
+
}
|
|
395
|
+
output_json.append(group)
|
|
396
|
+
return output_json
|
|
397
|
+
|
|
398
|
+
def _post_model(tenant_json, top_level_ontology, mobi_config):
|
|
399
|
+
|
|
400
|
+
mp = MultipartEncoder(fields={
|
|
401
|
+
"title": top_level_ontology,
|
|
402
|
+
"description": "This model was exported from Kobai.",
|
|
403
|
+
"json": json.dumps(tenant_json)
|
|
404
|
+
})
|
|
405
|
+
h = {"Content-type": mp.content_type}
|
|
406
|
+
|
|
407
|
+
api_url = mobi_config.mobi_api_url + "/ontologies"
|
|
408
|
+
#response = requests.post(
|
|
409
|
+
response = special_post(
|
|
410
|
+
api_url,
|
|
411
|
+
mobi_config,
|
|
412
|
+
headers = h,
|
|
413
|
+
data = mp,
|
|
414
|
+
verify=False,
|
|
415
|
+
timeout=5000
|
|
416
|
+
#auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password)
|
|
417
|
+
)
|
|
418
|
+
print("Upload Status", response.status_code)
|
|
419
|
+
if response.status_code != 201:
|
|
420
|
+
print(response.text)
|
|
421
|
+
|
|
422
|
+
##############################
|
|
423
|
+
# Mobi Update
|
|
424
|
+
##############################
|
|
425
|
+
|
|
426
|
+
def update_tenant(kobai_tenant, top_level_ontology_name, mobi_config: MobiSettings):
|
|
427
|
+
record_id = _get_ont_record_by_name(top_level_ontology_name, mobi_config)
|
|
428
|
+
|
|
429
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(record_id) + "/branches"
|
|
430
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
431
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
432
|
+
|
|
433
|
+
classes = _get_classes_by_ont(record_id, mobi_config)
|
|
434
|
+
ontology_by_class = {}
|
|
435
|
+
for o in classes:
|
|
436
|
+
for c in o["classes"]:
|
|
437
|
+
ontology_by_class[c] = o["id"]
|
|
438
|
+
|
|
439
|
+
_, mobi_tenant = get_tenant(top_level_ontology_name, mobi_config)
|
|
440
|
+
|
|
441
|
+
change_json = _compare_tenants(kobai_tenant, mobi_tenant, classes, mobi_config)
|
|
442
|
+
|
|
443
|
+
#################################
|
|
444
|
+
# Apply changes with Mobi API calls
|
|
445
|
+
#################################
|
|
446
|
+
|
|
447
|
+
for o in change_json:
|
|
448
|
+
if not change_json[o]["changed"]:
|
|
449
|
+
continue
|
|
450
|
+
|
|
451
|
+
ont_record_id = _get_ont_record_by_url(o, mobi_config)
|
|
452
|
+
if ont_record_id == "":
|
|
453
|
+
continue
|
|
454
|
+
|
|
455
|
+
branch_id = _get_or_create_branch_by_record(ont_record_id, "kobai_dev", mobi_config)
|
|
456
|
+
master_branch_id = _get_or_create_branch_by_record(ont_record_id, "MASTER", mobi_config)
|
|
457
|
+
|
|
458
|
+
for change in change_json[o]["class"]:
|
|
459
|
+
_stage_changes([change["mobi"]], ont_record_id, mobi_config)
|
|
460
|
+
_commit_changes("Kobai added class " + change["mobi"]["http://purl.org/dc/terms/title"][0]["@value"], ont_record_id, branch_id, mobi_config)
|
|
461
|
+
for change in change_json[o]["property"]:
|
|
462
|
+
_stage_changes([change["mobi"]], ont_record_id, mobi_config)
|
|
463
|
+
_commit_changes("Kobai added property " + change["mobi"]["http://purl.org/dc/terms/title"][0]["@value"], ont_record_id, branch_id, mobi_config)
|
|
464
|
+
|
|
465
|
+
api_url = mobi_config.mobi_api_url + "/merge-requests"
|
|
466
|
+
pd = {"title": "Kobai Change from kobai-dev to master", "recordId": ont_record_id, "sourceBranchId": branch_id, "targetBranchId": master_branch_id, "assignees": ["admin"], "removeSource": "true"}
|
|
467
|
+
response = requests.post(api_url, verify=mobi_config.verify_ssl, timeout=5000, params=pd, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
468
|
+
#print(response.status_code)
|
|
469
|
+
|
|
470
|
+
def _compare_tenants(kobai_tenant, mobi_tenant, classes, mobi_config):
|
|
471
|
+
existing_concepts = []
|
|
472
|
+
existing_relations = {}
|
|
473
|
+
existing_properties = {}
|
|
474
|
+
|
|
475
|
+
for dom in mobi_tenant["domains"]:
|
|
476
|
+
conText = base64.b64decode(dom['concepts']).decode('UTF-8')
|
|
477
|
+
cons = json.loads(conText)
|
|
478
|
+
for con in cons:
|
|
479
|
+
existing_concepts.append(con["uri"])
|
|
480
|
+
existing_properties[con["uri"]] = []
|
|
481
|
+
for prop in con["properties"]:
|
|
482
|
+
existing_properties[con["uri"]].append(prop["uri"])
|
|
483
|
+
existing_relations[con["uri"]] = []
|
|
484
|
+
for rel in con["relations"]:
|
|
485
|
+
existing_relations[con["uri"]].append(rel["uri"])
|
|
486
|
+
|
|
487
|
+
new_concepts = []
|
|
488
|
+
new_relations = {}
|
|
489
|
+
new_properties = {}
|
|
490
|
+
|
|
491
|
+
tenantId = kobai_tenant["tenantId"]
|
|
492
|
+
for dom in kobai_tenant["domains"]:
|
|
493
|
+
conText = base64.b64decode(dom['concepts']).decode('UTF-8')
|
|
494
|
+
cons = json.loads(conText)
|
|
495
|
+
for con in cons:
|
|
496
|
+
con_d = con["uri"].replace(tenantId, mobi_config.default_tenant_id)
|
|
497
|
+
if con_d not in existing_concepts:
|
|
498
|
+
print("New Class Detected")
|
|
499
|
+
new_concepts.append(con)
|
|
500
|
+
for prop in con["properties"]:
|
|
501
|
+
prop_d = prop["uri"].replace(tenantId, mobi_config.default_tenant_id)
|
|
502
|
+
if con_d in existing_properties:
|
|
503
|
+
if prop_d not in existing_properties[con_d]:
|
|
504
|
+
print("New Prop Detected")
|
|
505
|
+
if con_d not in new_properties:
|
|
506
|
+
new_properties[con_d] = []
|
|
507
|
+
new_properties[con_d].append(prop)
|
|
508
|
+
print(prop)
|
|
509
|
+
else:
|
|
510
|
+
print("New Property due to New Concept")
|
|
511
|
+
for rel in con["relations"]:
|
|
512
|
+
rel_d = rel["uri"].replace(tenantId, mobi_config.default_tenant_id)
|
|
513
|
+
if con_d in existing_relations:
|
|
514
|
+
if rel_d not in existing_relations[con_d]:
|
|
515
|
+
print("New Rel Detected")
|
|
516
|
+
if con_d not in new_relations:
|
|
517
|
+
new_relations[con_d] = []
|
|
518
|
+
new_relations[con_d].append(rel)
|
|
519
|
+
else:
|
|
520
|
+
print("New Relation due to New Concept")
|
|
521
|
+
|
|
522
|
+
change_json = {}
|
|
523
|
+
for o in classes:
|
|
524
|
+
ont_exist = o["id"]
|
|
525
|
+
change_json[ont_exist] = {}
|
|
526
|
+
change_json[ont_exist]["exists"] = True
|
|
527
|
+
change_json[ont_exist]["changed"] = False
|
|
528
|
+
change_json[ont_exist]["class"] = []
|
|
529
|
+
change_json[ont_exist]["property"] = []
|
|
530
|
+
change_json[ont_exist]["relation"] = []
|
|
531
|
+
|
|
532
|
+
#################################
|
|
533
|
+
# Identify and capture changes associated to Mobi ontology
|
|
534
|
+
#################################
|
|
535
|
+
for c in new_concepts:
|
|
536
|
+
c_d = c["uri"].replace(tenantId, mobi_config.default_tenant_id)
|
|
537
|
+
ont_sig = c_d.replace("http://kobai/" + mobi_config.default_tenant_id + "/AssetModel/", "").replace("#", "/").replace("_", "/")
|
|
538
|
+
ont_sig = "/".join(ont_sig.split("/")[:-1])
|
|
539
|
+
ont = ""
|
|
540
|
+
for o in classes:
|
|
541
|
+
ont_exist = o["id"]
|
|
542
|
+
if ont_sig == _get_ont_sig_from_ont(ont_exist, len(ont_sig.split("/"))):
|
|
543
|
+
change_json[ont_exist]["class"].append({"type": "new", "kobai": c, "mobi": {}})
|
|
544
|
+
|
|
545
|
+
for c in new_properties:
|
|
546
|
+
for p in new_properties[c]:
|
|
547
|
+
ont_sig = _get_ont_sig_from_concept(tenantId, c, mobi_config)
|
|
548
|
+
for o in classes:
|
|
549
|
+
ont_exist = o["id"]
|
|
550
|
+
if ont_sig == _get_ont_sig_from_ont(ont_exist, len(ont_sig.split("/"))):
|
|
551
|
+
change_json[ont_exist]["property"].append({"type": "new", "kobai": p, "mobi": {}})
|
|
552
|
+
|
|
553
|
+
for c in new_relations:
|
|
554
|
+
for r in new_properties[c]:
|
|
555
|
+
ont_sig = _get_ont_sig_from_concept(tenantId, c, mobi_config)
|
|
556
|
+
for o in classes:
|
|
557
|
+
ont_exist = o["id"]
|
|
558
|
+
if ont_sig == _get_ont_sig_from_ont(ont_exist, len(ont_sig.split("/"))):
|
|
559
|
+
change_json[ont_exist]["relation"].append({"type": "new", "kobai": r, "mobi": {}})
|
|
560
|
+
|
|
561
|
+
#################################
|
|
562
|
+
# Generate Mobi json for every change
|
|
563
|
+
#################################
|
|
564
|
+
for ont in change_json:
|
|
565
|
+
changed = False
|
|
566
|
+
for i, change in enumerate(change_json[ont]["class"]):
|
|
567
|
+
c = change["kobai"]
|
|
568
|
+
c_json = {}
|
|
569
|
+
c_json["@id"] = ont + "/" + c["label"].split("_")[-1]
|
|
570
|
+
c_json["@type"] = [ "http://www.w3.org/2002/07/owl#Class" ]
|
|
571
|
+
c_json["http://www.w3.org/2000/01/rdf-schema#label"] = [{"@value": c["label"].split("_")[-1]}]
|
|
572
|
+
c_json["http://purl.org/dc/terms/title"] = [{"@value": c["label"].split("_")[-1]}]
|
|
573
|
+
c_json["http://www.w3.org/2000/01/rdf-schema#subClassOf"] = []
|
|
574
|
+
#c_json["http://www.w3.org/2002/07/owl#deprecated"] = [{"@value": "true", "@type": "http://www.w3.org/2001/XMLSchema#boolean"}]
|
|
575
|
+
for pc in c["inheritedConcepts"]:
|
|
576
|
+
c_json["http://www.w3.org/2000/01/rdf-schema#subClassOf"].append(pc)
|
|
577
|
+
change_json[ont]["class"][i]["mobi"] = c_json
|
|
578
|
+
changed = True
|
|
579
|
+
|
|
580
|
+
for i, change in enumerate(change_json[ont]["property"]):
|
|
581
|
+
p = change["kobai"]
|
|
582
|
+
p_json = {}
|
|
583
|
+
p_json["@id"] = ont + "/" + p["label"]
|
|
584
|
+
p_json["@type"] = [ "http://www.w3.org/2002/07/owl#Class" ]
|
|
585
|
+
p_json["http://www.w3.org/2000/01/rdf-schema#label"] = [{"@value": p["label"]}]
|
|
586
|
+
p_json["http://purl.org/dc/terms/title"] = [{"@value": p["label"]}]
|
|
587
|
+
p_json["http://www.w3.org/2000/01/rdf-schema#domain"] = [{"@id": ont + "/" + _get_concept_name_from_prop_uri(p["uri"])}]
|
|
588
|
+
p_json["http://www.w3.org/2000/01/rdf-schema#range"] = [{"@id": p["propTypeUri"]}]
|
|
589
|
+
change_json[ont]["property"][i]["mobi"] = p_json
|
|
590
|
+
changed = True
|
|
591
|
+
|
|
592
|
+
if changed is True:
|
|
593
|
+
change_json[ont]["changed"] = True
|
|
594
|
+
|
|
595
|
+
return change_json
|
|
596
|
+
|
|
597
|
+
def _stage_changes(changes, ont_record_id, mobi_config):
|
|
598
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(ont_record_id) + "/in-progress-commit"
|
|
599
|
+
#response = requests.delete(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
600
|
+
response = special_delete(api_url, mobi_config, verify=False, timeout=5000)
|
|
601
|
+
|
|
602
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(ont_record_id) + "/in-progress-commit"
|
|
603
|
+
m = MultipartEncoder(fields={"additions": json.dumps(changes), "deletions": "[]"})
|
|
604
|
+
h = {"Content-type": m.content_type}
|
|
605
|
+
#response = requests.put(api_url, verify=False, timeout=5000, data=m, headers=h, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
606
|
+
response = special_put(api_url, mobi_config, verify=False, timeout=5000, data=m, headers=h)
|
|
607
|
+
|
|
608
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(ont_record_id) + "/in-progress-commit"
|
|
609
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
610
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
611
|
+
|
|
612
|
+
def _commit_changes(message, ont_record_id, branch_id, mobi_config):
|
|
613
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(ont_record_id) + "/branches/" + urllib.parse.quote_plus(branch_id) + "/commits"
|
|
614
|
+
pd = {"message": message}
|
|
615
|
+
#response = requests.post(api_url, verify=False, timeout=5000, params=pd, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
616
|
+
response = special_post(api_url, mobi_config, verify=False, timeout=5000, params=pd)
|
|
617
|
+
|
|
618
|
+
##############################
|
|
619
|
+
# Mobi Branch
|
|
620
|
+
##############################
|
|
621
|
+
|
|
622
|
+
#def jprint(data):
|
|
623
|
+
# json_str = json.dumps(data, indent=4)
|
|
624
|
+
|
|
625
|
+
def _get_branches_by_record(id, mobi_config):
|
|
626
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(id) + "/branches"
|
|
627
|
+
#response = requests.get(api_url, verify=False, timeout=5000, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
628
|
+
response = special_request(api_url, mobi_config, verify=False, timeout=5000)
|
|
629
|
+
return response.json()
|
|
630
|
+
|
|
631
|
+
def _get_or_create_branch_by_record(id, name, mobi_config):
|
|
632
|
+
branches = _get_branches_by_record(id, mobi_config)
|
|
633
|
+
for b in branches:
|
|
634
|
+
if b["http://purl.org/dc/terms/title"][0]["@value"] == name:
|
|
635
|
+
return b["@id"]
|
|
636
|
+
|
|
637
|
+
commit = ""
|
|
638
|
+
for b in branches:
|
|
639
|
+
if b["http://purl.org/dc/terms/title"][0]["@value"] == "MASTER":
|
|
640
|
+
commit = b["http://mobi.com/ontologies/catalog#head"][0]["@id"]
|
|
641
|
+
|
|
642
|
+
api_url = mobi_config.mobi_api_url + "/catalogs/" + urllib.parse.quote_plus(mobi_config.catalog_name) + "/records/" + urllib.parse.quote_plus(id) + "/branches"
|
|
643
|
+
pd = {"type": "http://mobi.com/ontologies/catalog#Branch", "title": name, "commitId": commit}
|
|
644
|
+
#requests.post(api_url, verify=False, timeout=5000, params=pd, auth=requests.auth.HTTPBasicAuth(mobi_config.mobi_username, mobi_config.mobi_password))
|
|
645
|
+
special_post(api_url, mobi_config, verify=False, timeout=5000, params=pd)
|
|
646
|
+
|
|
647
|
+
branches = _get_branches_by_record(id, mobi_config)
|
|
648
|
+
for b in branches:
|
|
649
|
+
if b["http://purl.org/dc/terms/title"][0]["@value"] == name:
|
|
650
|
+
return b["@id"]
|
|
651
|
+
return ""
|
|
652
|
+
|
|
653
|
+
##############################
|
|
654
|
+
# Mobi Parse
|
|
655
|
+
##############################
|
|
656
|
+
def _get_domain_range(url, mobi_config):
|
|
657
|
+
for d, r in mobi_config.domain_extraction.items():
|
|
658
|
+
if d in url:
|
|
659
|
+
return r
|
|
660
|
+
return {"min": 0, "max": 0}
|
|
661
|
+
|
|
662
|
+
def _parent_uri_from_uri(uri):
|
|
663
|
+
#return "/".join(uri.split("/")[:-1])
|
|
664
|
+
return "/".join(_uri_split(uri)[:-1])
|
|
665
|
+
|
|
666
|
+
def _trim_trailing_slash(uri):
|
|
667
|
+
if uri[-1] == "/":
|
|
668
|
+
return uri[:-1]
|
|
669
|
+
else:
|
|
670
|
+
return uri
|
|
671
|
+
|
|
672
|
+
def _uri_split(uri):
|
|
673
|
+
uri = uri.replace("#", "/")
|
|
674
|
+
return uri.split("/")
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
################################
|
|
678
|
+
# Transform from Kobai to Mobi
|
|
679
|
+
################################
|
|
680
|
+
|
|
681
|
+
def _get_ont_sig_from_concept(tenantId, uri, mobi_config):
|
|
682
|
+
uri = uri.replace(tenantId, mobi_config.default_tenant_id)
|
|
683
|
+
ont_sig = uri.replace("http://kobai/" + mobi_config.default_tenant_id + "/AssetModel/", "").replace("#", "/").replace("_", "/")
|
|
684
|
+
ont_sig = "/".join(ont_sig.split("/")[:-1])
|
|
685
|
+
return ont_sig
|
|
686
|
+
|
|
687
|
+
def _get_ont_sig_from_ont(uri, length):
|
|
688
|
+
return "/".join(uri.split("/")[-length:])
|
|
689
|
+
|
|
690
|
+
def _get_concept_name_from_prop_uri(uri):
|
|
691
|
+
return uri.split("#")[0].split("/")[-1]
|
|
692
|
+
#return uri.split("#")[0].split("_")[-1]
|
|
693
|
+
|
|
694
|
+
################################
|
|
695
|
+
# Transform from Mobi to Kobai
|
|
696
|
+
################################
|
|
697
|
+
|
|
698
|
+
def _domain_from_uri(uri, mobi_config):
|
|
699
|
+
#domain = "_".join(uri.split("/")[_get_domain_range(uri, mobi_config)['min']:_get_domain_range(uri, mobi_config)['max']+1])
|
|
700
|
+
domain = "_".join(_uri_split(uri)[_get_domain_range(uri, mobi_config)['min']:_get_domain_range(uri, mobi_config)['max']+1])
|
|
701
|
+
return domain
|
|
702
|
+
|
|
703
|
+
def _name_from_uri(uri, mobi_config):
|
|
704
|
+
#name = "_".join(uri.split("/")[_get_domain_range(uri, mobi_config)['max']+1:])
|
|
705
|
+
name = "_".join(_uri_split(uri)[_get_domain_range(uri, mobi_config)['max']+1:])
|
|
706
|
+
if name == "":
|
|
707
|
+
#name = "_" + "_".join(uri.split("/")[_get_domain_range(uri, mobi_config)['max']:])
|
|
708
|
+
name = "_" + "_".join(_uri_split(uri)[_get_domain_range(uri, mobi_config)['max']:])
|
|
709
|
+
return name
|
|
710
|
+
|
|
711
|
+
def _label_from_uri(uri):
|
|
712
|
+
#return uri.split("/")[-1]
|
|
713
|
+
return _uri_split(uri)[-1]
|
|
714
|
+
|
|
715
|
+
def _fix_uri(uri, type, mobi_config):
|
|
716
|
+
domain = _domain_from_uri(uri, mobi_config)
|
|
717
|
+
name = _name_from_uri(uri, mobi_config)
|
|
718
|
+
|
|
719
|
+
top = "/".join(uri.split("/")[0:_get_domain_range(uri, mobi_config)['min']])
|
|
720
|
+
|
|
721
|
+
if type == "concept":
|
|
722
|
+
uri = domain + "#" + name
|
|
723
|
+
elif type == "prop":
|
|
724
|
+
uri = domain + "/" + "_".join(name.split("_")[0:-1]) + "#" + name.split("_")[-1]
|
|
725
|
+
|
|
726
|
+
for d in mobi_config.domain_extraction:
|
|
727
|
+
|
|
728
|
+
if d in top:
|
|
729
|
+
uri = "http://kobai/" + mobi_config.default_tenant_id + "/AssetModel/" + uri
|
|
730
|
+
|
|
731
|
+
return uri
|
|
732
|
+
|
|
733
|
+
|
kobai/mobi_config.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings
|
|
2
|
+
|
|
3
|
+
class MobiSettings(BaseSettings):
|
|
4
|
+
|
|
5
|
+
#Application Specific Settings
|
|
6
|
+
domain_extraction: dict = {}
|
|
7
|
+
|
|
8
|
+
#Mobi Server Settings
|
|
9
|
+
mobi_api_url: str = "https://localhost:8443/mobirest"
|
|
10
|
+
mobi_username: str = "admin"
|
|
11
|
+
mobi_password: str = "admin"
|
|
12
|
+
cookies: str = ""
|
|
13
|
+
use_cookies: bool = False
|
|
14
|
+
verify_ssl = False
|
|
15
|
+
|
|
16
|
+
catalog_name: str = "http://mobi.com/catalog-local"
|
|
17
|
+
default_tenant_id: str = "00000000-0000-0000-0000-000000000000"
|
|
18
|
+
|
|
19
|
+
#settings = MobiSettings()
|
kobai/ms_authenticate.py
CHANGED
|
@@ -20,7 +20,7 @@ def get_scope(client_id: str = None, target_client_id: str = None, scope: str =
|
|
|
20
20
|
|
|
21
21
|
return f"openid profile offline_access api://{target_client_id}/Kobai.Access"
|
|
22
22
|
|
|
23
|
-
def device_code(tenant_id: str, client_id: str, target_client_id: str = None, scope: str = None):
|
|
23
|
+
def device_code(tenant_id: str, client_id: str, target_client_id: str = None, scope: str = None, authority: str = None ):
|
|
24
24
|
|
|
25
25
|
"""
|
|
26
26
|
Authenticate using the device code flow and get the access token
|
|
@@ -31,7 +31,7 @@ def device_code(tenant_id: str, client_id: str, target_client_id: str = None, sc
|
|
|
31
31
|
target_client_id (str): Kobai IDM client ID.
|
|
32
32
|
scope (str): Scope to be passed
|
|
33
33
|
"""
|
|
34
|
-
credential = DeviceCodeCredential(client_id=client_id, tenant_id=tenant_id)
|
|
34
|
+
credential = DeviceCodeCredential(client_id=client_id, tenant_id=tenant_id, authority=authority)
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
37
|
token = credential.get_token(get_scope(client_id, target_client_id, scope))
|
kobai/tenant_client.py
CHANGED
|
@@ -8,7 +8,8 @@ from pyspark.sql import SparkSession
|
|
|
8
8
|
from langchain_core.language_models.chat_models import BaseChatModel
|
|
9
9
|
from langchain_core.embeddings import Embeddings
|
|
10
10
|
|
|
11
|
-
from . import spark_client, databricks_client, ai_query, tenant_api, ai_rag
|
|
11
|
+
from . import spark_client, databricks_client, ai_query, tenant_api, ai_rag, mobi
|
|
12
|
+
from .mobi_config import MobiSettings
|
|
12
13
|
from .genie import get_genie_descriptions
|
|
13
14
|
|
|
14
15
|
class TenantClient:
|
|
@@ -56,6 +57,7 @@ class TenantClient:
|
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
|
|
59
61
|
########################################
|
|
60
62
|
# MS Entra Auth
|
|
61
63
|
########################################
|
|
@@ -1012,3 +1014,123 @@ class TenantClient:
|
|
|
1012
1014
|
'/data-svcs/solution/snapshot/import/upload',
|
|
1013
1015
|
{'file': EMPTY_TENANT_JSON}
|
|
1014
1016
|
)
|
|
1017
|
+
|
|
1018
|
+
########################################
|
|
1019
|
+
# Mobi
|
|
1020
|
+
########################################
|
|
1021
|
+
|
|
1022
|
+
def pull_mobi_to_tenant(self, ontology_name, mobi_config: MobiSettings):
|
|
1023
|
+
|
|
1024
|
+
"""
|
|
1025
|
+
Export an ontology from Mobi and import it into a Kobai tenant, replacing the contents of the tenant.
|
|
1026
|
+
|
|
1027
|
+
Requires that the SDK be authenticated against the target Kobai tenant.
|
|
1028
|
+
|
|
1029
|
+
Parameters:
|
|
1030
|
+
ontology_name (str): The name of the ontology to access in Mobi.
|
|
1031
|
+
mobi_config (MobiSettings): Configuration required to access the Mobi service.
|
|
1032
|
+
"""
|
|
1033
|
+
|
|
1034
|
+
tenant_json, tenant_json_enc = mobi.get_tenant(ontology_name, mobi_config)
|
|
1035
|
+
#for d in tenant_json["domains"]:
|
|
1036
|
+
#for c in d["concepts"]:
|
|
1037
|
+
# print(c)
|
|
1038
|
+
self.__set_tenant_import(tenant_json_enc)
|
|
1039
|
+
|
|
1040
|
+
def pull_mobi_to_file(self, ontology_name, mobi_config: MobiSettings, file_name, human_readable=False):
|
|
1041
|
+
|
|
1042
|
+
"""
|
|
1043
|
+
Export an ontology from Mobi and save it in a Kobai json import file.
|
|
1044
|
+
|
|
1045
|
+
Requires that the SDK be authenticated against the target Kobai tenant.
|
|
1046
|
+
|
|
1047
|
+
Parameters:
|
|
1048
|
+
ontology_name (str): The name of the ontology to access in Mobi.
|
|
1049
|
+
mobi_config (MobiSettings): Configuration required to access the Mobi service.
|
|
1050
|
+
file_name (str): File name to give the output (no extension)
|
|
1051
|
+
human_readable (bool) OPTIONAL: generate a second, decoded Kobai file.
|
|
1052
|
+
"""
|
|
1053
|
+
|
|
1054
|
+
tenant_json, tenant_json_enc = mobi.get_tenant(ontology_name, mobi_config)
|
|
1055
|
+
|
|
1056
|
+
if ".json" in file_name:
|
|
1057
|
+
file_name = file_name.split(".json")[0]
|
|
1058
|
+
|
|
1059
|
+
with open(f"{file_name}.json", "w") as out_file:
|
|
1060
|
+
json.dump(tenant_json_enc, out_file)
|
|
1061
|
+
|
|
1062
|
+
if human_readable:
|
|
1063
|
+
with open(f"{file_name}_decoded.json", "w") as out_file:
|
|
1064
|
+
json.dump(tenant_json, out_file)
|
|
1065
|
+
|
|
1066
|
+
def push_tenant_update_to_mobi(self, ontology_name, mobi_config: MobiSettings):
|
|
1067
|
+
|
|
1068
|
+
"""
|
|
1069
|
+
Compare a (modified) Kobai tenant to a Mobi ontology, and generate a Merge Request for the changes.
|
|
1070
|
+
|
|
1071
|
+
Requires that the SDK be authenticated against the target Kobai tenant.
|
|
1072
|
+
|
|
1073
|
+
Parameters:
|
|
1074
|
+
ontology_name (str): The name of the ontology to access in Mobi.
|
|
1075
|
+
mobi_config (MobiSettings): Configuration required to access the Mobi service.
|
|
1076
|
+
"""
|
|
1077
|
+
|
|
1078
|
+
tenant_json_enc = self.__get_tenant_export()
|
|
1079
|
+
mobi.update_tenant(tenant_json_enc, ontology_name, mobi_config)
|
|
1080
|
+
|
|
1081
|
+
def push_whole_tenant_to_mobi(self, ontology_name, mobi_config: MobiSettings):
|
|
1082
|
+
|
|
1083
|
+
"""
|
|
1084
|
+
Export a tenant from Kobai, and create an ontology in Mobi.
|
|
1085
|
+
|
|
1086
|
+
Requires that the SDK be authenticated against the target Kobai tenant.
|
|
1087
|
+
Requires that an ontology with the same name does not already exist in Mobi.
|
|
1088
|
+
|
|
1089
|
+
Parameters:
|
|
1090
|
+
ontology_name (str): The name of the ontology to create in Mobi.
|
|
1091
|
+
mobi_config (MobiSettings): Configuration required to access the Mobi service.
|
|
1092
|
+
"""
|
|
1093
|
+
|
|
1094
|
+
tenant_json = self.get_tenant_config()
|
|
1095
|
+
mobi.replace_tenant_to_mobi(tenant_json, ontology_name, mobi_config)
|
|
1096
|
+
|
|
1097
|
+
def push_whole_tenant_to_jsonld_file(self, ontology_name, file_name):
|
|
1098
|
+
|
|
1099
|
+
"""
|
|
1100
|
+
Export a tenant from Kobai, and create an ontology in Mobi.
|
|
1101
|
+
|
|
1102
|
+
Requires that the SDK be authenticated against the target Kobai tenant.
|
|
1103
|
+
|
|
1104
|
+
Parameters:
|
|
1105
|
+
ontology_name (str): The name of the ontology to create in Mobi.
|
|
1106
|
+
file_name (str): File name to give the output (no extension)
|
|
1107
|
+
"""
|
|
1108
|
+
|
|
1109
|
+
tenant_json = self.get_tenant_config()
|
|
1110
|
+
tenant_jsonld = mobi.replace_tenant_to_file(tenant_json, ontology_name)
|
|
1111
|
+
|
|
1112
|
+
if ".json" in file_name:
|
|
1113
|
+
file_name = file_name.split(".json")[0]
|
|
1114
|
+
|
|
1115
|
+
with open(f"{file_name}.json", "w") as out_file:
|
|
1116
|
+
json.dump(tenant_jsonld, out_file)
|
|
1117
|
+
|
|
1118
|
+
def get_default_mobi_config(self):
|
|
1119
|
+
|
|
1120
|
+
"""
|
|
1121
|
+
Returns a default MobiSettings configuration object.
|
|
1122
|
+
|
|
1123
|
+
Available Fields to Set:
|
|
1124
|
+
domain_extraction: Mapping of ontology url structures to Kobai domain names.
|
|
1125
|
+
mobi_api_url: url for Mobi service. (ex: https://localhost:8443/mobirest)
|
|
1126
|
+
mobi_username: User name for Mobi service.
|
|
1127
|
+
mobi_password: Password for Mobi service.
|
|
1128
|
+
"""
|
|
1129
|
+
|
|
1130
|
+
return MobiSettings()
|
|
1131
|
+
|
|
1132
|
+
def __set_tenant_import(self, tenant_json_enc):
|
|
1133
|
+
self.api_client._TenantAPI__run_post_files(
|
|
1134
|
+
'/data-svcs/solution/snapshot/import/upload',
|
|
1135
|
+
{'file': json.dumps(tenant_json_enc)}
|
|
1136
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kobai-sdk
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5rc1
|
|
4
4
|
Summary: A package that enables interaction with a Kobai tenant.
|
|
5
5
|
Author-email: Ryan Oattes <ryan@kobai.io>
|
|
6
6
|
License: Apache License
|
|
@@ -222,6 +222,7 @@ Requires-Dist: azure-storage-blob
|
|
|
222
222
|
Requires-Dist: langchain-core
|
|
223
223
|
Requires-Dist: langchain-community
|
|
224
224
|
Requires-Dist: langchain-classic
|
|
225
|
+
Requires-Dist: delta-spark
|
|
225
226
|
Provides-Extra: dev
|
|
226
227
|
Requires-Dist: black; extra == "dev"
|
|
227
228
|
Requires-Dist: bumpver; extra == "dev"
|
|
@@ -4,12 +4,14 @@ kobai/ai_rag.py,sha256=ZBUlpjbQC73yTXNV91uG_Tw-PvtWR5bSAg2NlwJQZwM,14635
|
|
|
4
4
|
kobai/databricks_client.py,sha256=fyqqMly2Qm0r1AHWsQjkYeNsDdH0G1JSgTkF9KJ55qA,2118
|
|
5
5
|
kobai/demo_tenant_client.py,sha256=wlNc-bdI2wotRXo8ppUOalv4hYdBlek_WzJNARZV-AE,9293
|
|
6
6
|
kobai/genie.py,sha256=-EbEYpu9xj_3zIXaPdwbNJEAmoeM7nb9qK-h1f_STtM,8061
|
|
7
|
-
kobai/
|
|
7
|
+
kobai/mobi.py,sha256=1pIAdn9qKmg2zm_DnH1qAsgKr-YgH6SJJWXEzSZch38,32787
|
|
8
|
+
kobai/mobi_config.py,sha256=AkrGtwDNMPuwt-udbp_8KsLnLGze4S8-at2UH-zN9YQ,525
|
|
9
|
+
kobai/ms_authenticate.py,sha256=f7t_BaxK8e-SUWoB6JliBEngF2iduF-COcCZq83H5t0,2297
|
|
8
10
|
kobai/spark_client.py,sha256=opM_F-4Ut5Hq5zZjWMuLvUps9sDULvyPNZHXGL8dW1k,776
|
|
9
11
|
kobai/tenant_api.py,sha256=Q5yuFd9_V4lo3LWzvYEEO3LpDRWFgQD4TlRPXDTGbiE,4368
|
|
10
|
-
kobai/tenant_client.py,sha256=
|
|
11
|
-
kobai_sdk-0.3.
|
|
12
|
-
kobai_sdk-0.3.
|
|
13
|
-
kobai_sdk-0.3.
|
|
14
|
-
kobai_sdk-0.3.
|
|
15
|
-
kobai_sdk-0.3.
|
|
12
|
+
kobai/tenant_client.py,sha256=EeJhjdg1iFKZm0L7iSwbg4FEXbJjh6iWmdGLdKb0yC4,43470
|
|
13
|
+
kobai_sdk-0.3.5rc1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
14
|
+
kobai_sdk-0.3.5rc1.dist-info/METADATA,sha256=0QRyijyJKePuaBrZ1vBYO4yjoC6NVxuUy7Uq9rS1CAU,19864
|
|
15
|
+
kobai_sdk-0.3.5rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
kobai_sdk-0.3.5rc1.dist-info/top_level.txt,sha256=ns1El3BrTTHKvoAgU1XtiSaVIudYeCXbEEUVY8HFDZ4,6
|
|
17
|
+
kobai_sdk-0.3.5rc1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|