personal_knowledge_library 3.0.0__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 personal_knowledge_library might be problematic. Click here for more details.

Files changed (42) hide show
  1. knowledge/__init__.py +91 -0
  2. knowledge/base/__init__.py +22 -0
  3. knowledge/base/access.py +167 -0
  4. knowledge/base/entity.py +267 -0
  5. knowledge/base/language.py +27 -0
  6. knowledge/base/ontology.py +2734 -0
  7. knowledge/base/search.py +473 -0
  8. knowledge/base/tenant.py +192 -0
  9. knowledge/nel/__init__.py +11 -0
  10. knowledge/nel/base.py +495 -0
  11. knowledge/nel/engine.py +123 -0
  12. knowledge/ontomapping/__init__.py +667 -0
  13. knowledge/ontomapping/manager.py +320 -0
  14. knowledge/public/__init__.py +27 -0
  15. knowledge/public/cache.py +115 -0
  16. knowledge/public/helper.py +373 -0
  17. knowledge/public/relations.py +128 -0
  18. knowledge/public/wikidata.py +1324 -0
  19. knowledge/services/__init__.py +128 -0
  20. knowledge/services/asyncio/__init__.py +7 -0
  21. knowledge/services/asyncio/base.py +458 -0
  22. knowledge/services/asyncio/graph.py +1420 -0
  23. knowledge/services/asyncio/group.py +450 -0
  24. knowledge/services/asyncio/search.py +439 -0
  25. knowledge/services/asyncio/users.py +270 -0
  26. knowledge/services/base.py +533 -0
  27. knowledge/services/graph.py +1897 -0
  28. knowledge/services/group.py +819 -0
  29. knowledge/services/helper.py +142 -0
  30. knowledge/services/ontology.py +1234 -0
  31. knowledge/services/search.py +488 -0
  32. knowledge/services/session.py +444 -0
  33. knowledge/services/tenant.py +281 -0
  34. knowledge/services/users.py +445 -0
  35. knowledge/utils/__init__.py +10 -0
  36. knowledge/utils/graph.py +417 -0
  37. knowledge/utils/wikidata.py +197 -0
  38. knowledge/utils/wikipedia.py +175 -0
  39. personal_knowledge_library-3.0.0.dist-info/LICENSE +201 -0
  40. personal_knowledge_library-3.0.0.dist-info/METADATA +1163 -0
  41. personal_knowledge_library-3.0.0.dist-info/RECORD +42 -0
  42. personal_knowledge_library-3.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,2734 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright © 2021-present Wacom. All rights reserved.
3
+ import abc
4
+ import enum
5
+ from datetime import datetime
6
+ from json import JSONEncoder
7
+ from typing import Union, Optional, Any, List, Dict, Tuple, Set
8
+
9
+ import loguru
10
+ from rdflib import Literal, RDFS, OWL, URIRef, RDF, Graph
11
+
12
+ from knowledge.base.access import TenantAccessRight
13
+ from knowledge.base.entity import (
14
+ EntityStatus,
15
+ Label,
16
+ Description,
17
+ URI_TAG,
18
+ IMAGE_TAG,
19
+ DESCRIPTIONS_TAG,
20
+ LABELS_TAG,
21
+ TYPE_TAG,
22
+ STATUS_FLAG_TAG,
23
+ DATA_PROPERTIES_TAG,
24
+ OBJECT_PROPERTIES_TAG,
25
+ GROUP_IDS,
26
+ OWNER_TAG,
27
+ OWNER_ID_TAG,
28
+ SOURCE_REFERENCE_ID_TAG,
29
+ SOURCE_SYSTEM_TAG,
30
+ TENANT_RIGHTS_TAG,
31
+ LOCALE_TAG,
32
+ IS_MAIN_TAG,
33
+ USE_NEL_TAG,
34
+ VALUE_TAG,
35
+ DATA_PROPERTY_TAG,
36
+ VISIBILITY_TAG,
37
+ INFLECTION_CASE_SENSITIVE,
38
+ INFLECTION_SETTING,
39
+ INFLECTION_CONCEPT_CLASS,
40
+ LANGUAGE_TAG,
41
+ CONTENT_TAG,
42
+ DATA_TYPE_TAG,
43
+ RELATION_TAG,
44
+ INCOMING_TAG,
45
+ OUTGOING_TAG,
46
+ COMMENT_TAG,
47
+ LocalizedContent,
48
+ COMMENTS_TAG,
49
+ USE_VECTOR_INDEX_TAG,
50
+ SEND_VECTOR_INDEX_TAG,
51
+ USE_FULLTEXT_TAG,
52
+ TARGETS_TAG,
53
+ INDEXING_NEL_TARGET,
54
+ INDEXING_VECTOR_SEARCH_TARGET,
55
+ INDEXING_FULLTEXT_TARGET,
56
+ USE_VECTOR_DOCUMENT_INDEX_TAG,
57
+ INDEXING_VECTOR_SEARCH_DOCUMENT_TARGET,
58
+ EXTERNAL_USER_ID_TAG,
59
+ DESCRIPTION_TAG,
60
+ )
61
+ from knowledge.base.language import EN_US, SUPPORTED_LOCALES, EN, LanguageCode, LocaleCode
62
+
63
+ # ---------------------------------------------- Vocabulary base URI ---------------------------------------------------
64
+ PREFIX: str = "xsd"
65
+ BASE_URI: str = "http://www.w3.org/2001/XMLSchema#"
66
+ RESOURCE: str = "http://www.w3.org/2000/01/rdf-schema#Resource"
67
+ # ---------------------------------------------------- Constants -------------------------------------------------------
68
+ SUB_CLASS_OF_TAG: str = "subClassOf"
69
+ TENANT_ID: str = "tenantId"
70
+ NAME_TAG: str = "name"
71
+ SEND_TO_NEL: str = "sendToNEL"
72
+ # ---------------------------------------------------- RDFLib ----------------------------------------------------------
73
+ PREFERRED_LABEL: URIRef = URIRef("wacom:core#prefLabel")
74
+ logger = loguru.logger
75
+
76
+
77
+ # ---------------------------------------------------- Ontology --------------------------------------------------------
78
+ class PropertyType(enum.Enum):
79
+ """
80
+ PropertyType
81
+ -----------
82
+ Within the ontology two different property types are defined. A data- and an object property.
83
+ """
84
+
85
+ OBJECT_PROPERTY = "Relation"
86
+ DATA_PROPERTY = "Literal"
87
+
88
+
89
+ INVERSE_PROPERTY_TYPE: Dict[str, PropertyType] = {pt.value: pt for pt in PropertyType}
90
+
91
+
92
+ class DataPropertyType(enum.Enum):
93
+ """
94
+ DataPropertyType.
95
+ -----------------
96
+ Data types that are used by Datatype properties.
97
+ """
98
+
99
+ STRING = BASE_URI + "string"
100
+ """Character strings (but not all Unicode character strings) """
101
+ BOOLEAN = BASE_URI + "boolean"
102
+ """boolean: true, false"""
103
+ DECIMAL = BASE_URI + "decimal"
104
+ """Arbitrary-precision decimal numbers"""
105
+ INTEGER = BASE_URI + "integer"
106
+ """Arbitrary-size integer numbers"""
107
+ DOUBLE = BASE_URI + "double"
108
+ """64-bit floating point numbers incl. ±Inf, ±0, NaN"""
109
+ FLOAT = BASE_URI + "float"
110
+ """32-bit floating point numbers incl. ±Inf, ±0, NaN"""
111
+ DATE = BASE_URI + "date"
112
+ """Dates (yyyy-mm-dd) with or without timezone"""
113
+ TIME = BASE_URI + "time"
114
+ """Times (hh:mm:ss.sss…) with or without timezone"""
115
+ DATE_TIME = BASE_URI + "dateTime"
116
+ """Date and time with or without timezone"""
117
+ DATE_TIMESTAMP = BASE_URI + "dateTimeStamp"
118
+ """Date and time with required timezone """
119
+ G_YEAR = BASE_URI + "gYear"
120
+ """Gregorian calendar year"""
121
+ G_MONTH = BASE_URI + "gMonth"
122
+ """Gregorian calendar month"""
123
+ G_DAY = BASE_URI + "gDay"
124
+ """Gregorian calendar day of the month"""
125
+ G_YEAR_MONTH = BASE_URI + "gYearMonth"
126
+ """Gregorian calendar year and month"""
127
+ G_MONTH_DAY = BASE_URI + "gMonthDay"
128
+ """Gregorian calendar month and day"""
129
+ DURATION = BASE_URI + "duration"
130
+ """Duration of time"""
131
+ YEAR_MONTH_DURATION = BASE_URI + "yearMonthDuration"
132
+ """Duration of time (months and years only)"""
133
+ DAYTIME_DURATION = BASE_URI + "dayTimeDuration"
134
+ """Duration of time (days, hours, minutes, seconds only)"""
135
+ BYTES = BASE_URI + "byte"
136
+ """-128…+127 (8 bit)"""
137
+ SHORT = BASE_URI + "short"
138
+ """-32768… + 32767 (16 bit)"""
139
+ INT = BASE_URI + "int"
140
+ """-2147483648…+2147483647 (32 bit)"""
141
+ LONG = BASE_URI + "long"
142
+ """-9223372036854775808…+9223372036854775807 (64 bit)"""
143
+ UNSIGNED_BYTE = BASE_URI + "unsignedByte"
144
+ """0 … 255 (8 bit)"""
145
+ UNSIGNED_SHORT = BASE_URI + "unsignedShort"
146
+ """0 … 65535 (16 bit)"""
147
+ UNSIGNED_INT = BASE_URI + "unsignedInt"
148
+ """ 0 … 4294967295 (32 bit)"""
149
+ UNSIGNED_LONG = BASE_URI + "unsignedLong"
150
+ """ 0 … 18446744073709551615 (64 bit)"""
151
+ POSITIVE_INTEGER = BASE_URI + "positiveInteger"
152
+ """Integer numbers > 0 """
153
+ NON_NEGATIVE_INTEGER = BASE_URI + "nonNegativeInteger"
154
+ """Integer numbers ≥ 0"""
155
+ NEGATIVE_INTEGER = BASE_URI + "negativeInteger"
156
+ """Integer numbers ≤ 0"""
157
+ NON_POSITIVE_INTEGER = BASE_URI + "nonPositiveInteger"
158
+ """Integer numbers ≤ 0"""
159
+ HEX_BINARY = BASE_URI + "hexBinary"
160
+ """Hex-encoded binary data"""
161
+ BASE64_BINARY = BASE_URI + "base64Binary"
162
+ """Base64-encoded binary data"""
163
+ ANY_URI = BASE_URI + "anyURI"
164
+ """Absolute or relative URIs and IRIs"""
165
+ LANGUAGE = BASE_URI + "language_code"
166
+ """Language tags per http://tools.ietf.org/html/bcp47"""
167
+ NORMALIZED = BASE_URI + "normalizedString"
168
+ """Whitespace-normalized strings"""
169
+ TOKEN = BASE_URI + "token"
170
+ """Tokenized strings"""
171
+ NM_TOKEN = BASE_URI + "NMTOKEN"
172
+ """XML NMTOKENs"""
173
+ NAME = BASE_URI + "Name"
174
+ """XML Names"""
175
+ NC_NAME = BASE_URI + "NCName"
176
+ """XML NCNames"""
177
+
178
+
179
+ INVERSE_DATA_PROPERTY_TYPE_MAPPING: Dict[str, DataPropertyType] = {
180
+ str(lit_type.value): lit_type for lit_type in DataPropertyType
181
+ }
182
+ """Maps the string representation of the XSD data types to the data types enum constants."""
183
+
184
+
185
+ # ------------------------------------------ Ontology References -------------------------------------------------------
186
+
187
+
188
+ class OntologyLabel(LocalizedContent):
189
+ """
190
+ Ontology Label
191
+ --------------
192
+ Label that is multilingual.
193
+
194
+ Parameters
195
+ ----------
196
+ content: str
197
+ Content value
198
+ language_code: LanguageCode (default:= 'en')
199
+ Language code of content
200
+ main: bool (default:=False)
201
+ Main content
202
+ """
203
+
204
+ def __init__(self, content: str, language_code: LanguageCode = EN, main: bool = False):
205
+ self.__main: bool = main
206
+ super().__init__(content, language_code)
207
+
208
+ @property
209
+ def main(self) -> bool:
210
+ """Flag if the content is the main content or an alias."""
211
+ return self.__main
212
+
213
+ @staticmethod
214
+ def create_from_dict(
215
+ dict_label: Dict[str, Any], tag_name: str = CONTENT_TAG, locale_name: str = LOCALE_TAG
216
+ ) -> "OntologyLabel":
217
+ """
218
+ Create a label from a dictionary.
219
+
220
+ Parameters
221
+ ----------
222
+ dict_label: Dict[str, Any]
223
+ Dictionary with the label information
224
+ tag_name: str
225
+ Tag name of the content
226
+ locale_name: str
227
+ Tag name of the language code
228
+
229
+ Returns
230
+ -------
231
+ instance: OntologyLabel
232
+ Instance of the label
233
+ """
234
+ if tag_name not in dict_label:
235
+ raise ValueError("Dict is does not contain a localized label.")
236
+ if locale_name not in dict_label:
237
+ raise ValueError("Dict is does not contain a language code")
238
+ if IS_MAIN_TAG in dict_label:
239
+ return OntologyLabel(dict_label[tag_name], LanguageCode(dict_label[locale_name]), dict_label[IS_MAIN_TAG])
240
+ return OntologyLabel(dict_label[tag_name], LanguageCode(dict_label[locale_name]))
241
+
242
+ @staticmethod
243
+ def create_from_list(param: List[dict]) -> List["OntologyLabel"]:
244
+ """
245
+ Create a list of labels from a list of dictionaries.
246
+ Parameters
247
+ ----------
248
+ param: List[dict]
249
+ List of dictionaries with the label information
250
+
251
+ Returns
252
+ -------
253
+ instances: List[OntologyLabel]
254
+ List of label instances
255
+ """
256
+ return [OntologyLabel.create_from_dict(p) for p in param]
257
+
258
+ def __dict__(self):
259
+ return {CONTENT_TAG: self.content, LOCALE_TAG: self.language_code, IS_MAIN_TAG: self.main}
260
+
261
+
262
+ class OntologyObjectReference(abc.ABC):
263
+ """
264
+ Ontology class type
265
+ ------------------
266
+ Associated to an entity to link the type of the entity.
267
+
268
+ Parameters
269
+ ----------
270
+ scheme: str
271
+ Scheme or owner of the ontology object
272
+ context: str
273
+ Context of ontology object
274
+ name: str
275
+ Ontology object reference name
276
+ """
277
+
278
+ def __init__(self, scheme: str, context: str, name: str):
279
+ self.__scheme: str = scheme
280
+ self.__context: str = context
281
+ self.__name: str = name
282
+
283
+ @property
284
+ def scheme(self):
285
+ """Scheme."""
286
+ return self.__scheme
287
+
288
+ @property
289
+ def context(self):
290
+ """Context."""
291
+ return self.__context
292
+
293
+ @property
294
+ def name(self):
295
+ """Name."""
296
+ return self.__name
297
+
298
+ @property
299
+ def iri(self):
300
+ """Internationalized Resource Identifier (IRI) encoded ontology class name."""
301
+ return f"{self.scheme}:{self.context}#{self.name}"
302
+
303
+ def __repr__(self):
304
+ return self.iri
305
+
306
+ @classmethod
307
+ def parse_iri(cls, iri: str) -> Tuple[str, str, str]:
308
+ """Parse an IRI into its components.
309
+
310
+ Parameters
311
+ ----------
312
+ iri: str
313
+ IRI to parse
314
+
315
+ Returns
316
+ -------
317
+ Tuple[str, str, str]
318
+ Scheme, context and name of the IRI
319
+ """
320
+ if iri is None:
321
+ raise ValueError("IRI cannot be None")
322
+ if ":" not in iri or "#" not in iri:
323
+ raise ValueError(f"Invalid IRI: {iri}")
324
+ colon_idx: int = iri.index(":")
325
+ hash_idx: int = iri.index("#")
326
+ scheme: str = iri[:colon_idx]
327
+ context: str = iri[colon_idx + 1 : hash_idx]
328
+ name: str = iri[hash_idx + 1 :]
329
+ return scheme, context, name
330
+
331
+
332
+ class OntologyClassReference(OntologyObjectReference):
333
+ """
334
+ Ontology class type
335
+ -------------------
336
+ Associated to an ontology class.
337
+
338
+ Parameters
339
+ ----------
340
+ scheme: str
341
+ Scheme or owner
342
+ context: str
343
+ Context of class
344
+ class_name: str
345
+ Class name
346
+ """
347
+
348
+ def __init__(self, scheme: str, context: str, class_name: str):
349
+ super().__init__(scheme, context, class_name)
350
+
351
+ @property
352
+ def class_name(self):
353
+ """Class name."""
354
+ return self.name
355
+
356
+ @classmethod
357
+ def parse(cls, iri: str) -> "OntologyClassReference":
358
+ """Parse IRI to create an ontology class reference.
359
+
360
+ Parameters
361
+ ----------
362
+ iri: str
363
+ IRI of ontology class reference
364
+
365
+ Returns
366
+ -------
367
+ instance: OntologyClassReference
368
+ Instance of ontology class reference
369
+ """
370
+ scheme, context, name = OntologyObjectReference.parse_iri(iri)
371
+ return OntologyClassReference(scheme, context, name)
372
+
373
+ def __eq__(self, other):
374
+ if not isinstance(other, OntologyClassReference):
375
+ return False
376
+ return self.iri == other.iri
377
+
378
+ def __hash__(self):
379
+ return hash(self.iri)
380
+
381
+
382
+ class OntologyPropertyReference(OntologyObjectReference):
383
+ """
384
+ Property reference
385
+ ------------------
386
+ Associated to an ontology property.
387
+
388
+ Parameters
389
+ ----------
390
+ scheme: str
391
+ Scheme or owner
392
+ context: str
393
+ Context of class
394
+ property_name: str
395
+ Property name
396
+ """
397
+
398
+ def __init__(self, scheme: str, context: str, property_name: str):
399
+ super().__init__(scheme, context, property_name)
400
+
401
+ @property
402
+ def property_name(self):
403
+ """Property name."""
404
+ return self.name
405
+
406
+ @classmethod
407
+ def parse(cls, iri: str) -> "OntologyPropertyReference":
408
+ """Parses an IRI into an OntologyPropertyReference.
409
+
410
+ Parameters
411
+ ----------
412
+ iri: str
413
+ IRI to parse
414
+
415
+ Returns
416
+ -------
417
+ instance: OntologyPropertyReference
418
+ Instance of OntologyPropertyReference
419
+ """
420
+ scheme, context, name = OntologyObjectReference.parse_iri(iri)
421
+ return OntologyPropertyReference(scheme, context, name)
422
+
423
+ def __eq__(self, other):
424
+ if not isinstance(other, OntologyPropertyReference):
425
+ return False
426
+ return self.iri == other.iri
427
+
428
+ def __hash__(self):
429
+ return hash(self.iri)
430
+
431
+
432
+ # ---------------------------------------------------- Classes Constants -----------------------------------------------
433
+ THING_CLASS: OntologyClassReference = OntologyClassReference("wacom", "core", "Thing")
434
+ # ---------------------------------------------------- Property Constants ----------------------------------------------
435
+ SYSTEM_SOURCE_SYSTEM: OntologyPropertyReference = OntologyPropertyReference("wacom", "core", "sourceSystem")
436
+ SYSTEM_SOURCE_REFERENCE_ID: OntologyPropertyReference = OntologyPropertyReference("wacom", "core", "sourceReferenceId")
437
+ CREATION_DATE: OntologyPropertyReference = OntologyPropertyReference.parse("wacom:core#creationDate")
438
+ LAST_UPDATE_DATE: OntologyPropertyReference = OntologyPropertyReference.parse("wacom:core#lastUpdate")
439
+
440
+
441
+ class Comment(LocalizedContent):
442
+ """
443
+ Comment
444
+ -------
445
+ Comment that is multi-lingual.
446
+
447
+ Parameters
448
+ ----------
449
+ text: str
450
+ Text value
451
+ language_code: LanguageCode (default:= 'en')
452
+ Language code of content
453
+ """
454
+
455
+ def __init__(self, text: str, language_code: LanguageCode = "en"):
456
+ super().__init__(text, language_code)
457
+
458
+ @staticmethod
459
+ def create_from_dict(dict_description: Dict[str, Any]) -> "Comment":
460
+ """
461
+ Create a comment from a dictionary.
462
+ Parameters
463
+ ----------
464
+ dict_description: Dict[str, Any]
465
+ Dictionary containing the comment
466
+
467
+ Returns
468
+ -------
469
+ instance: Comment
470
+ Instance of comment
471
+ """
472
+ if VALUE_TAG not in dict_description or LANGUAGE_TAG not in dict_description:
473
+ raise ValueError("Dict is does not contain a localized comment.")
474
+ return Comment(dict_description[VALUE_TAG], dict_description[LANGUAGE_TAG])
475
+
476
+ @staticmethod
477
+ def create_from_list(param: List[Dict[str, Any]]) -> List["Comment"]:
478
+ """
479
+ Create a list of comments from a list of dictionaries.
480
+ Parameters
481
+ ----------
482
+ param: List[Dict[str, Any]]
483
+ List of dictionaries containing the comments
484
+
485
+ Returns
486
+ -------
487
+ instances: List[Comment]
488
+ List of instances of comments
489
+ """
490
+ return [Comment.create_from_dict(p) for p in param]
491
+
492
+ def __dict__(self):
493
+ return {
494
+ COMMENT_TAG: self.content,
495
+ LOCALE_TAG: self.language_code,
496
+ }
497
+
498
+
499
+ class OntologyObject(abc.ABC):
500
+ """
501
+ Generic ontology object
502
+ -----------------------
503
+
504
+ Parameters
505
+ ----------
506
+ tenant_id: str
507
+ Reference id for tenant
508
+ iri: str
509
+ IRI of the ontology object
510
+ icon: str
511
+ Icon assigned to object, visually representing it
512
+ labels: List[Label]
513
+ List of multi-language_code labels
514
+ comments: List[Label]
515
+ List of multi-language_code comments
516
+ context: str
517
+ Context
518
+ """
519
+
520
+ def __init__(
521
+ self, tenant_id: str, iri: str, icon: str, labels: List[OntologyLabel], comments: List[Comment], context: str
522
+ ):
523
+ self.__tenant_id: str = tenant_id
524
+ self.__labels: List[OntologyLabel] = labels
525
+ self.__comments: List[Comment] = comments
526
+ self.__iri: str = iri
527
+ self.__icon: str = icon
528
+ self.__context: str = context
529
+
530
+ @property
531
+ def tenant_id(self) -> str:
532
+ """Tenant id."""
533
+ return self.__tenant_id
534
+
535
+ @property
536
+ def iri(self) -> str:
537
+ """IRI"""
538
+ return self.__iri
539
+
540
+ @property
541
+ def context(self) -> str:
542
+ """Context."""
543
+ return self.__context
544
+
545
+ @property
546
+ def icon(self) -> str:
547
+ """Icon."""
548
+ return self.__icon
549
+
550
+ @icon.setter
551
+ def icon(self, value: str):
552
+ self.__icon = value
553
+
554
+ @property
555
+ def labels(self) -> List[OntologyLabel]:
556
+ """Labels related to ontology object."""
557
+ return self.__labels
558
+
559
+ def label_for_lang(self, language_code: LanguageCode) -> Optional[OntologyLabel]:
560
+ """
561
+ Get label for language_code.
562
+ Parameters
563
+ ----------
564
+ language_code: LanguageCode
565
+ Language code
566
+
567
+ Returns
568
+ -------
569
+ label: Optional[OntologyLabel]
570
+ Label for language_code
571
+ """
572
+ for label in self.labels:
573
+ if label.language_code == language_code:
574
+ return label
575
+ return None
576
+
577
+ @property
578
+ def comments(self) -> List[Comment]:
579
+ """Comment related to ontology object."""
580
+ return self.__comments
581
+
582
+ def comment_for_lang(self, language_code: LanguageCode) -> Optional[Comment]:
583
+ """
584
+ Get comment for language_code.
585
+ Parameters
586
+ ----------
587
+ language_code: LanguageCode
588
+ Language code
589
+
590
+ Returns
591
+ -------
592
+ comment: Optional[Comment]
593
+ Comment for language_code
594
+ """
595
+ for comment in self.comments:
596
+ if comment.language_code == language_code:
597
+ return comment
598
+ return None
599
+
600
+
601
+ class OntologyContextSettings:
602
+ """
603
+ OntologyContextSettings
604
+ -----------------------
605
+ Describes the settings of the context, such as:
606
+ - prefixes for RDF, RDFS and OWL
607
+ - Base literal URI
608
+ - Base class URI
609
+ - Description literal name
610
+ - depth
611
+ """
612
+
613
+ def __init__(
614
+ self,
615
+ rdf_prefix: str,
616
+ rdfs_prefix: str,
617
+ owl_prefix: str,
618
+ base_literal_uri: str,
619
+ base_class_uri: str,
620
+ description_literal_name: str,
621
+ depth: int,
622
+ ):
623
+ self.__rdf_prefix: str = rdf_prefix
624
+ self.__rdfs_prefix: str = rdfs_prefix
625
+ self.__owl_prefix: str = owl_prefix
626
+ self.__base_literal_uri: str = base_literal_uri
627
+ self.__base_class_uri: str = base_class_uri
628
+ self.__description_literal_name: str = description_literal_name
629
+ self.__depth: int = depth
630
+
631
+ @property
632
+ def rdf_prefix(self):
633
+ """RDF prefix"""
634
+ return self.__rdf_prefix
635
+
636
+ @property
637
+ def rdfs_prefix(self):
638
+ """RDFS prefix"""
639
+ return self.__rdfs_prefix
640
+
641
+ @property
642
+ def owl_prefix(self):
643
+ """OWL prefix"""
644
+ return self.__owl_prefix
645
+
646
+ @property
647
+ def base_literal_uri(self):
648
+ """Base literal URI."""
649
+ return self.__base_literal_uri
650
+
651
+ @property
652
+ def base_class_uri(self):
653
+ """Base class URI."""
654
+ return self.__base_class_uri
655
+
656
+ @property
657
+ def description_literal_name(self) -> str:
658
+ """Literal name of the description."""
659
+ return self.__description_literal_name
660
+
661
+ @property
662
+ def depth(self) -> int:
663
+ """Depth."""
664
+ return self.__depth
665
+
666
+
667
+ class OntologyContext(OntologyObject):
668
+ """
669
+ OntologyContext
670
+ ----------------
671
+ Ontology context representation.
672
+
673
+ Parameters
674
+ ----------
675
+ cid: str
676
+ Context id
677
+ tenant_id: str
678
+ Tenant id.
679
+ name: str
680
+ Name of the ontology context
681
+ icon: str
682
+ Icon or Base64 encoded
683
+ labels: List[Label]
684
+ List of labels
685
+ comments: List[Comment]
686
+ List of comments
687
+ context: str
688
+ context name
689
+ base_uri: str
690
+ Base URI
691
+ concepts: List[str]
692
+ List of classes / concepts
693
+ properties: List[str]
694
+ List of properties (data and object properties)
695
+ """
696
+
697
+ def __init__(
698
+ self,
699
+ cid: str,
700
+ tenant_id: str,
701
+ name: str,
702
+ icon: str,
703
+ labels: List[OntologyLabel],
704
+ comments: List[Comment],
705
+ date_added: datetime,
706
+ date_modified: datetime,
707
+ context: str,
708
+ base_uri: str,
709
+ version: int,
710
+ orphaned: bool,
711
+ concepts: List[str],
712
+ properties: List[str],
713
+ ):
714
+ self.__id = cid
715
+ self.__base_uri: str = base_uri
716
+ self.__version: int = version
717
+ self.__date_added: datetime = date_added
718
+ self.__date_modified: datetime = date_modified
719
+ self.__orphaned: bool = orphaned
720
+ self.__concepts: List[str] = concepts
721
+ self.__properties: List[str] = properties
722
+ super().__init__(tenant_id, name, icon, labels, comments, context)
723
+
724
+ @property
725
+ def id(self) -> str:
726
+ """Context id."""
727
+ return self.__id
728
+
729
+ @property
730
+ def base_uri(self) -> str:
731
+ """Base URI."""
732
+ return self.__base_uri
733
+
734
+ @property
735
+ def orphaned(self) -> bool:
736
+ """Orphaned."""
737
+ return self.__orphaned
738
+
739
+ @property
740
+ def version(self) -> int:
741
+ """Version."""
742
+ return self.__version
743
+
744
+ @property
745
+ def date_added(self) -> datetime:
746
+ """Date added."""
747
+ return self.__date_added
748
+
749
+ @property
750
+ def date_modified(self) -> datetime:
751
+ """Date modified."""
752
+ return self.__date_modified
753
+
754
+ @property
755
+ def concepts(self) -> List[str]:
756
+ """List of concepts."""
757
+ return self.__concepts
758
+
759
+ @property
760
+ def properties(self) -> List[str]:
761
+ """List of properties."""
762
+ return self.__properties
763
+
764
+ @classmethod
765
+ def from_dict(cls, context_dict: Dict[str, Any]):
766
+ """
767
+ Create OntologyContext from dictionary.
768
+
769
+ Parameters
770
+ ----------
771
+ context_dict: Dict[str, Any]
772
+ Dictionary containing the context data.
773
+
774
+ Returns
775
+ -------
776
+ instance: OntologyContext
777
+ Instance of OntologyContext object.
778
+ """
779
+ context_data: Dict[str, Any] = context_dict["context"]
780
+ labels: List[OntologyLabel] = (
781
+ []
782
+ if context_data["labels"] is None
783
+ else [Label(content=la[VALUE_TAG], language_code=la[LANGUAGE_TAG]) for la in context_data["labels"]]
784
+ )
785
+ comments: List[Comment] = (
786
+ []
787
+ if context_data["comments"] is None
788
+ else [Comment(text=la[VALUE_TAG], language_code=la[LANGUAGE_TAG]) for la in context_data["comments"]]
789
+ )
790
+ added: datetime = datetime.fromisoformat(context_data["dateAdded"])
791
+ modified: datetime = datetime.fromisoformat(context_data["dateModified"])
792
+ return OntologyContext(
793
+ context_data["id"],
794
+ context_data["tenantId"],
795
+ context_data["name"],
796
+ context_data["icon"],
797
+ labels,
798
+ comments,
799
+ added,
800
+ modified,
801
+ context_data["context"],
802
+ context_data["baseURI"],
803
+ context_dict["version"],
804
+ context_data["orphaned"],
805
+ context_dict.get("concepts"),
806
+ context_dict.get("properties"),
807
+ )
808
+
809
+ def __repr__(self):
810
+ return f"<OntologyContext> - [id:={self.id}, iri:={self.iri}]"
811
+
812
+
813
+ class OntologyClass(OntologyObject):
814
+ """
815
+ OntologyClass
816
+ ----------------
817
+ Concept for ontology.
818
+
819
+ Parameters
820
+ ----------
821
+ tenant_id: str
822
+ Tenant id for ontology
823
+ context: str
824
+ Context
825
+ reference: OntologyClassReference
826
+ Reference for ontology class
827
+ icon: str
828
+ Icon representing concept
829
+ labels: List[Label]
830
+ List of labels
831
+ comments: List[Comment]
832
+ List of comments
833
+
834
+ subclass_of: str (default: None)
835
+ Subclass of ontology class
836
+ """
837
+
838
+ def __init__(
839
+ self,
840
+ tenant_id: str,
841
+ context: str,
842
+ reference: OntologyClassReference,
843
+ subclass_of: OntologyClassReference = None,
844
+ icon: Optional[str] = None,
845
+ labels: Optional[List[OntologyLabel]] = None,
846
+ comments: Optional[List[Comment]] = None,
847
+ ):
848
+ self.__subclass_of: OntologyClassReference = subclass_of
849
+ self.__reference: OntologyClassReference = reference
850
+ super().__init__(tenant_id, reference.iri, icon, labels, comments, context)
851
+
852
+ @property
853
+ def subclass_of(self) -> Optional[OntologyClassReference]:
854
+ """
855
+ Superclass of the class.
856
+ """
857
+ return self.__subclass_of
858
+
859
+ @property
860
+ def reference(self) -> OntologyClassReference:
861
+ """
862
+ Reference of ontology class.
863
+ """
864
+ return self.__reference
865
+
866
+ def __repr__(self):
867
+ return f"<OntologyClass> - [reference:={self.reference}, subclass_of:={self.subclass_of}]"
868
+
869
+ @classmethod
870
+ def from_dict(cls, concept_dict: Dict[str, Any]):
871
+ """Create OntologyClass from dictionary.
872
+
873
+ Parameters
874
+ ----------
875
+ concept_dict: Dict[str, Any]
876
+ Dictionary containing the concept data.
877
+
878
+ Returns
879
+ -------
880
+ instance: OntologyClass
881
+ Instance of OntologyClass object.
882
+ """
883
+ labels: List[OntologyLabel] = (
884
+ []
885
+ if concept_dict[LABELS_TAG] is None
886
+ else [
887
+ OntologyLabel(content=la[VALUE_TAG], language_code=la[LANGUAGE_TAG]) for la in concept_dict[LABELS_TAG]
888
+ ]
889
+ )
890
+ comments: List[Comment] = (
891
+ []
892
+ if concept_dict[COMMENTS_TAG] is None
893
+ else [Comment(text=la[VALUE_TAG], language_code=la[LANGUAGE_TAG]) for la in concept_dict[COMMENTS_TAG]]
894
+ )
895
+ return OntologyClass(
896
+ tenant_id=concept_dict[TENANT_ID],
897
+ context=concept_dict["context"],
898
+ reference=OntologyClassReference.parse(concept_dict[NAME_TAG]),
899
+ subclass_of=(
900
+ OntologyClassReference.parse(concept_dict[SUB_CLASS_OF_TAG])
901
+ if SUB_CLASS_OF_TAG in concept_dict
902
+ else None
903
+ ),
904
+ icon=concept_dict["icon"],
905
+ labels=labels,
906
+ comments=comments,
907
+ )
908
+
909
+ @classmethod
910
+ def new(cls) -> "OntologyClass":
911
+ """
912
+ Create new ontology class.
913
+
914
+ Returns
915
+ -------
916
+ instance: OntologyClass
917
+ New ontology class.
918
+ """
919
+ return OntologyClass("", "", THING_CLASS)
920
+
921
+
922
+ class OntologyProperty(OntologyObject):
923
+ """
924
+ Ontology Property
925
+ -----------------
926
+ Property ontology object.
927
+
928
+ Parameters
929
+ ----------
930
+ kind: str
931
+ Kind of relation
932
+ tenant_id: str
933
+ Tenant id
934
+ context: str
935
+ Context
936
+ name: OntologyPropertyReference
937
+ Name of property object
938
+ icon: str
939
+ Icon describing the property
940
+ property_domain: OntologyClassReference
941
+ Domain for the property
942
+ property_range: OntologyClassReference
943
+ Range for the property
944
+ labels: List[Label]
945
+ List of labels (localized)
946
+ comments: List[Comment],
947
+ List of comments
948
+ sub_property_of: str (default: = None)
949
+ Sub property of.
950
+ inverse_property_of: str (optional)
951
+ Inverse property
952
+ """
953
+
954
+ def __init__(
955
+ self,
956
+ kind: PropertyType,
957
+ tenant_id: str,
958
+ context: str,
959
+ name: OntologyPropertyReference,
960
+ icon: str = None,
961
+ property_domain: Optional[List[OntologyClassReference]] = None,
962
+ property_range: Optional[List[Union[OntologyClassReference, DataPropertyType]]] = None,
963
+ labels: Optional[List[OntologyLabel]] = None,
964
+ comments: Optional[List[Comment]] = None,
965
+ sub_property_of: Optional[OntologyPropertyReference] = None,
966
+ inverse_property_of: Optional[OntologyPropertyReference] = None,
967
+ ):
968
+ self.__kind: PropertyType = kind
969
+ self.__subproperty_of: OntologyPropertyReference = sub_property_of
970
+ self.__inverse_property_of: OntologyPropertyReference = inverse_property_of
971
+ self.__domains: List[OntologyClassReference] = property_domain if property_domain else []
972
+ self.__ranges: List[Optional[Union[OntologyClassReference, DataPropertyType]]] = (
973
+ property_range if property_range else []
974
+ )
975
+ self.__reference: OntologyPropertyReference = name
976
+ super().__init__(tenant_id, name.iri, icon, labels, comments, context)
977
+
978
+ @property
979
+ def is_data_property(self) -> bool:
980
+ """Check if property is data property.
981
+
982
+ Returns
983
+ -------
984
+ is_data_property: bool
985
+ True if property is data property, False otherwise.
986
+ """
987
+ return self.kind != PropertyType.OBJECT_PROPERTY
988
+
989
+ @property
990
+ def kind(self) -> PropertyType:
991
+ """Kind of the property."""
992
+ return self.__kind
993
+
994
+ @property
995
+ def reference(self) -> OntologyPropertyReference:
996
+ """Reference to property"""
997
+ return self.__reference
998
+
999
+ @property
1000
+ def subproperty_of(self) -> OntologyPropertyReference:
1001
+ """Reference to the super property"""
1002
+ return self.__subproperty_of
1003
+
1004
+ @property
1005
+ def inverse_property_of(self) -> OntologyPropertyReference:
1006
+ """Reference to the inverse property"""
1007
+ return self.__inverse_property_of
1008
+
1009
+ @property
1010
+ def domains(self) -> List[OntologyClassReference]:
1011
+ """Domain of the property."""
1012
+ return self.__domains
1013
+
1014
+ @property
1015
+ def ranges(self) -> List[Union[OntologyClassReference, DataPropertyType]]:
1016
+ """Ranges of the property."""
1017
+ return self.__ranges
1018
+
1019
+ def __repr__(self):
1020
+ return (
1021
+ f"<OntologyProperty> - [name:= {self.iri} domain:={self.domains}, range:={self.ranges}, "
1022
+ f"sub-property_of:={self.subproperty_of}, type:={self.kind}]"
1023
+ )
1024
+
1025
+ @classmethod
1026
+ def from_dict(cls, property_dict: Dict[str, Any]):
1027
+ """
1028
+ Create ontology property from dictionary.
1029
+ Parameters
1030
+ ----------
1031
+ property_dict: Dict[str, Any]
1032
+ Dictionary containing property information.
1033
+
1034
+ Returns
1035
+ -------
1036
+ instance: OntologyProperty
1037
+ Ontology property instance.
1038
+ """
1039
+ labels: List[OntologyLabel] = (
1040
+ []
1041
+ if property_dict[LABELS_TAG] is None
1042
+ else [OntologyLabel.create_from_dict(la, locale_name=LANGUAGE_TAG) for la in property_dict[LABELS_TAG]]
1043
+ )
1044
+ comments: List[Comment] = (
1045
+ []
1046
+ if property_dict["comments"] is None
1047
+ else [Comment.create_from_dict(co) for co in property_dict["comments"]]
1048
+ )
1049
+ return OntologyProperty(
1050
+ INVERSE_PROPERTY_TYPE[property_dict["kind"]],
1051
+ property_dict["tenantId"],
1052
+ property_dict["context"],
1053
+ OntologyPropertyReference.parse(property_dict["name"]),
1054
+ property_dict["icon"],
1055
+ [OntologyClassReference.parse(domain) for domain in property_dict["domains"]],
1056
+ [OntologyClassReference.parse(domain) for domain in property_dict["ranges"]],
1057
+ labels,
1058
+ comments,
1059
+ (
1060
+ OntologyPropertyReference.parse(property_dict["subPropertyOf"])
1061
+ if property_dict.get("subPropertyOf") not in ["", None]
1062
+ else None
1063
+ ),
1064
+ (
1065
+ OntologyPropertyReference.parse(property_dict["inverseOf"])
1066
+ if property_dict.get("inverseOf") not in ["", None]
1067
+ else None
1068
+ ),
1069
+ )
1070
+
1071
+ @classmethod
1072
+ def new(cls, kind: PropertyType) -> "OntologyProperty":
1073
+ """
1074
+ Create new ontology property.
1075
+ Parameters
1076
+ ----------
1077
+ kind: PropertyType
1078
+ Kind of property.
1079
+
1080
+ Returns
1081
+ -------
1082
+ instance: OntologyProperty
1083
+ New ontology property.
1084
+ """
1085
+ return OntologyProperty(
1086
+ kind, "", "", OntologyPropertyReference.parse("http://www.w3.org/2002/07/owl#topObjectProperty")
1087
+ )
1088
+
1089
+
1090
+ class EntityProperty(abc.ABC):
1091
+ """
1092
+ EntityProperty
1093
+ --------------
1094
+ Abstract class for the different types of properties.
1095
+ """
1096
+
1097
+
1098
+ class DataProperty(EntityProperty):
1099
+ """
1100
+ DataProperty
1101
+ ------------
1102
+ Data property for entities.
1103
+
1104
+ Parameter
1105
+ ---------
1106
+ content: Any
1107
+ Content
1108
+ literal_type: LiteralProperty
1109
+ OntologyPropertyReference type
1110
+ language_code: str
1111
+ Language code
1112
+ data_type: str
1113
+ Data type
1114
+ """
1115
+
1116
+ def __init__(
1117
+ self,
1118
+ content: Any,
1119
+ property_ref: OntologyPropertyReference,
1120
+ language_code: LocaleCode = EN_US,
1121
+ data_type: DataPropertyType = None,
1122
+ ):
1123
+ self.__content: Any = content
1124
+ self.__language_code: LocaleCode = language_code
1125
+ self.__type: OntologyPropertyReference = property_ref
1126
+ self.__data_type: Optional[DataPropertyType] = data_type
1127
+
1128
+ @property
1129
+ def data_property_type(self) -> OntologyPropertyReference:
1130
+ """Ontology type."""
1131
+ return self.__type
1132
+
1133
+ @property
1134
+ def data_type(self) -> Optional[DataPropertyType]:
1135
+ """Data type (optional)."""
1136
+ return self.__data_type
1137
+
1138
+ @property
1139
+ def value(self) -> Any:
1140
+ """Content of the data property."""
1141
+ return self.__content
1142
+
1143
+ @property
1144
+ def language_code(self) -> LocaleCode:
1145
+ """Language code of the content."""
1146
+ return self.__language_code
1147
+
1148
+ @staticmethod
1149
+ def create_from_dict(data_property_struct: dict):
1150
+ """
1151
+ Create data property from dictionary.
1152
+
1153
+ Parameters
1154
+ ----------
1155
+ data_property_struct: dict
1156
+ Dictionary containing data property information.
1157
+
1158
+ Returns
1159
+ -------
1160
+ instance: DataProperty
1161
+ Data property instance.
1162
+ """
1163
+ if (
1164
+ CONTENT_TAG not in data_property_struct
1165
+ or LOCALE_TAG not in data_property_struct
1166
+ and DATA_PROPERTY_TAG not in data_property_struct
1167
+ ):
1168
+ raise ValueError("Dict is does not contain a data_property structure.")
1169
+ data_property_type: str = data_property_struct[DATA_PROPERTY_TAG]
1170
+ data_type: DataPropertyType = DataPropertyType.STRING
1171
+ if DATA_TYPE_TAG in data_property_struct and data_property_struct[DATA_TYPE_TAG] is not None:
1172
+ if data_property_struct[DATA_TYPE_TAG] not in INVERSE_DATA_PROPERTY_TYPE_MAPPING:
1173
+ raise ValueError(f"DataProperty data type is not supported. Type: {data_type}")
1174
+ data_type = INVERSE_DATA_PROPERTY_TYPE_MAPPING[data_property_struct[DATA_TYPE_TAG]]
1175
+ return DataProperty(
1176
+ data_property_struct[CONTENT_TAG],
1177
+ OntologyPropertyReference.parse(data_property_type),
1178
+ data_property_struct[LOCALE_TAG],
1179
+ data_type,
1180
+ )
1181
+
1182
+ def __dict__(self):
1183
+ return {
1184
+ CONTENT_TAG: self.value,
1185
+ LOCALE_TAG: self.language_code,
1186
+ DATA_PROPERTY_TAG: self.data_property_type.iri,
1187
+ DATA_TYPE_TAG: None if self.data_type is None else self.data_type.value,
1188
+ }
1189
+
1190
+ def __repr__(self):
1191
+ return f"{self.value}@{self.language_code}<{self.data_property_type.name}>"
1192
+
1193
+ @staticmethod
1194
+ def create_from_list(param: List[dict]) -> List["DataProperty"]:
1195
+ """
1196
+ Create data property list from dictionary list.
1197
+
1198
+ Parameters
1199
+ ----------
1200
+ param: List[dict]
1201
+ List of dictionaries containing data property information.
1202
+
1203
+ Returns
1204
+ -------
1205
+ instances: List[DataProperty]
1206
+ List of data property instances.
1207
+ """
1208
+ return [DataProperty.create_from_dict(p) for p in param]
1209
+
1210
+
1211
+ class ObjectProperty(EntityProperty):
1212
+ """
1213
+ Object Property
1214
+ ---------------
1215
+ ObjectProperty for entities.
1216
+
1217
+ Parameter
1218
+ ---------
1219
+ relation: OntologyPropertyReference
1220
+ The OntologyPropertyReference type
1221
+ incoming: List[str] (default:= [])
1222
+ Incoming relations
1223
+ outgoing: List[str] (default:= [])
1224
+ Outgoing relations
1225
+ """
1226
+
1227
+ def __init__(
1228
+ self,
1229
+ relation: OntologyPropertyReference,
1230
+ incoming: Optional[List[Union[str, "ThingObject"]]] = None,
1231
+ outgoing: Optional[List[Union[str, "ThingObject"]]] = None,
1232
+ ):
1233
+ self.__relation: OntologyPropertyReference = relation
1234
+ self.__incoming: List[Union[str, "ThingObject"]] = incoming if incoming is not None else []
1235
+ self.__outgoing: List[Union[str, "ThingObject"]] = outgoing if outgoing is not None else []
1236
+
1237
+ @property
1238
+ def relation(self) -> OntologyPropertyReference:
1239
+ """Reference from the ontology."""
1240
+ return self.__relation
1241
+
1242
+ @property
1243
+ def incoming_relations(self) -> List[Union[str, "ThingObject"]]:
1244
+ """Incoming relation"""
1245
+ return self.__incoming
1246
+
1247
+ @property
1248
+ def outgoing_relations(self) -> List[Union[str, "ThingObject"]]:
1249
+ """Outgoing relation"""
1250
+ return self.__outgoing
1251
+
1252
+ @staticmethod
1253
+ def create_from_dict(relation_struct: Dict[str, Any]) -> Tuple[OntologyPropertyReference, "ObjectProperty"]:
1254
+ """
1255
+ Create object property from dictionary.
1256
+
1257
+ Parameters
1258
+ ----------
1259
+ relation_struct: Dict[str, Any]
1260
+ Dictionary containing object property information.
1261
+
1262
+ Returns
1263
+ -------
1264
+ relation_type: OntologyPropertyReference
1265
+ The OntologyPropertyReference type
1266
+ """
1267
+ relation_type: OntologyPropertyReference = OntologyPropertyReference.parse(relation_struct[RELATION_TAG])
1268
+ incoming: List[Union[str, ThingObject]] = []
1269
+
1270
+ for incoming_relation in relation_struct[INCOMING_TAG]:
1271
+ if isinstance(incoming_relation, dict):
1272
+ incoming.append(ThingObject.from_dict(incoming_relation))
1273
+ elif isinstance(incoming_relation, str):
1274
+ incoming.append(incoming_relation)
1275
+
1276
+ outgoing: List[Union[str, ThingObject]] = []
1277
+ for outgoing_relation in relation_struct[OUTGOING_TAG]:
1278
+ if isinstance(outgoing_relation, dict):
1279
+ outgoing.append(ThingObject.from_dict(outgoing_relation))
1280
+ elif isinstance(outgoing_relation, str):
1281
+ outgoing.append(outgoing_relation)
1282
+ return relation_type, ObjectProperty(relation_type, incoming, outgoing)
1283
+
1284
+ def __dict__(self):
1285
+ outgoing_relations: List[str] = []
1286
+ incoming_relations: List[str] = []
1287
+ for e in self.incoming_relations:
1288
+
1289
+ if isinstance(e, ThingObject):
1290
+ if e.uri is not None:
1291
+ incoming_relations.append(e.uri)
1292
+ else:
1293
+ incoming_relations.append(e.reference_id)
1294
+ else:
1295
+ incoming_relations.append(e)
1296
+ for e in self.outgoing_relations:
1297
+ if isinstance(e, ThingObject):
1298
+ if e.uri is not None:
1299
+ outgoing_relations.append(e.uri)
1300
+ else:
1301
+ outgoing_relations.append(e.reference_id)
1302
+ else:
1303
+ outgoing_relations.append(e)
1304
+ return {RELATION_TAG: self.relation.iri, INCOMING_TAG: incoming_relations, OUTGOING_TAG: outgoing_relations}
1305
+
1306
+ def __repr__(self):
1307
+ return f"{self.relation.iri}, in:={self.incoming_relations}, out:={self.outgoing_relations}"
1308
+
1309
+ @staticmethod
1310
+ def create_from_list(param: List[dict]) -> Dict[OntologyPropertyReference, "ObjectProperty"]:
1311
+ """
1312
+ Create object property list from dictionary list.
1313
+ Parameters
1314
+ ----------
1315
+ param: List[dict]
1316
+ List of dictionaries containing object property information.
1317
+
1318
+ Returns
1319
+ -------
1320
+ instances: Dict[OntologyPropertyReference, ObjectProperty]
1321
+ Dictionary of object property instances.
1322
+ """
1323
+ return dict([ObjectProperty.create_from_dict(p) for p in param])
1324
+
1325
+
1326
+ class Ontology:
1327
+ """
1328
+ Ontology
1329
+ --------
1330
+ The ontology consists of classes and properties.
1331
+ """
1332
+
1333
+ def __init__(self):
1334
+ self.__classes: Dict[OntologyClassReference, OntologyClass] = {}
1335
+ self.__data_properties: Dict[str, OntologyProperty] = {}
1336
+ self.__object_properties: Dict[str, OntologyProperty] = {}
1337
+
1338
+ def add_class(self, class_obj: OntologyClass):
1339
+ """
1340
+ Adding class object.
1341
+
1342
+ Parameters
1343
+ ----------
1344
+ class_obj: OntologyClass
1345
+ Class object
1346
+ """
1347
+ self.__classes[class_obj.reference] = class_obj
1348
+
1349
+ def add_properties(self, prop_obj: OntologyProperty):
1350
+ """
1351
+ Adding properties.
1352
+
1353
+ Parameters
1354
+ ----------
1355
+ prop_obj: OntologyProperty
1356
+ """
1357
+ if prop_obj.is_data_property:
1358
+ self.__data_properties[prop_obj.reference.iri] = prop_obj
1359
+ else:
1360
+ self.__object_properties[prop_obj.reference.iri] = prop_obj
1361
+
1362
+ @property
1363
+ def data_properties(self) -> List[OntologyProperty]:
1364
+ """All data properties."""
1365
+ return list(self.__data_properties.values())
1366
+
1367
+ @property
1368
+ def object_properties(self) -> List[OntologyProperty]:
1369
+ """All object properties."""
1370
+ return list(self.__object_properties.values())
1371
+
1372
+ @property
1373
+ def classes(self) -> List[OntologyClass]:
1374
+ """All classes."""
1375
+ return list(self.__classes.values())
1376
+
1377
+ def __check_hierarchy__(self, clz: OntologyClassReference, domain: OntologyClassReference) -> bool:
1378
+ """
1379
+ Check if class is in domain.
1380
+ Parameters
1381
+ ----------
1382
+ clz: OntologyClassReference
1383
+ Class reference
1384
+ domain: OntologyClassReference
1385
+ Domain reference
1386
+
1387
+ Returns
1388
+ -------
1389
+ result: bool
1390
+ True if class is in domain.
1391
+ """
1392
+ current_clz: Optional[OntologyClass] = self.get_class(clz)
1393
+ while current_clz is not None:
1394
+ if current_clz.reference == domain:
1395
+ return True
1396
+ current_clz = self.get_class(current_clz.subclass_of)
1397
+ return False
1398
+
1399
+ def get_class(self, class_reference: OntologyClassReference) -> Optional[OntologyClass]:
1400
+ """
1401
+ Get class instance by reference.
1402
+
1403
+ Parameters
1404
+ ----------
1405
+ class_reference: OntologyClassReference
1406
+ Class reference
1407
+
1408
+ Returns
1409
+ --------
1410
+ instance: Optional[OntologyClass]
1411
+ Instance of ontology class.
1412
+ """
1413
+ return self.__classes.get(class_reference, None)
1414
+
1415
+ def get_object_properties(self, property_reference: OntologyPropertyReference) -> Optional[OntologyProperty]:
1416
+ """
1417
+ Get object property instance by reference.
1418
+
1419
+ Parameters
1420
+ ----------
1421
+ property_reference: OntologyPropertyReference
1422
+ Property reference
1423
+
1424
+ Returns
1425
+ --------
1426
+ instance: Optional[OntologyProperty]
1427
+ Instance of ontology object property.
1428
+ """
1429
+ return self.__object_properties.get(property_reference.iri)
1430
+
1431
+ def get_data_properties(self, property_reference: OntologyPropertyReference) -> Optional[OntologyProperty]:
1432
+ """
1433
+ Get object property instance by reference.
1434
+
1435
+ Parameters
1436
+ ----------
1437
+ property_reference: OntologyPropertyReference
1438
+ Property reference
1439
+
1440
+ Returns
1441
+ --------
1442
+ instance: Optional[OntologyProperty]
1443
+ Instance of ontology object property.
1444
+ """
1445
+ return self.__data_properties.get(property_reference.iri)
1446
+
1447
+ def data_properties_for(self, cls_reference: OntologyClassReference) -> List[OntologyPropertyReference]:
1448
+ """
1449
+ Retrieve a list of data properties.
1450
+
1451
+ Parameters
1452
+ ----------
1453
+ cls_reference: OntologyClassReference
1454
+ Class of the ontology
1455
+
1456
+ Returns
1457
+ -------
1458
+ data_properties: List[OntologyPropertyReference]
1459
+ List of data properties, where domain fit for the class of one of its super classes.
1460
+ """
1461
+ data_properties: List[OntologyPropertyReference] = []
1462
+ clz: Optional[OntologyClass] = self.get_class(cls_reference)
1463
+ if clz is not None:
1464
+ for dp in self.data_properties:
1465
+ for domain in dp.domains:
1466
+ if self.__check_hierarchy__(clz.reference, domain):
1467
+ data_properties.append(dp.reference)
1468
+ return data_properties
1469
+
1470
+ def object_properties_for(self, cls_reference: OntologyClassReference) -> List[OntologyPropertyReference]:
1471
+ """
1472
+ Retrieve a list of object properties.
1473
+
1474
+ Parameters
1475
+ ----------
1476
+ cls_reference: OntologyClassReference
1477
+ Class of the ontology
1478
+
1479
+ Returns
1480
+ -------
1481
+ object_properties: List[OntologyPropertyReference]
1482
+ List of object properties, where domain fit for the class of one of its super classes.
1483
+ """
1484
+ object_properties: List[OntologyPropertyReference] = []
1485
+ clz: Optional[OntologyClass] = self.get_class(cls_reference)
1486
+ if clz is not None:
1487
+ for dp in self.object_properties:
1488
+ for domain in dp.domains:
1489
+ if self.__check_hierarchy__(clz.reference, domain):
1490
+ object_properties.append(dp.reference)
1491
+ return object_properties
1492
+
1493
+ def __repr__(self):
1494
+ return f"<Ontology> : classes:= {self.classes}"
1495
+
1496
+
1497
+ class ThingObject(abc.ABC):
1498
+ """
1499
+ ThingObject
1500
+ -----------
1501
+ Generic entity within knowledge graph.
1502
+
1503
+ Each entity is derived from this object, thus all entity shares:
1504
+ - **uri**: A unique resource identity to identify the entity and reference it in relations
1505
+ - **label**: Human understandable label
1506
+ - **icon**: Visual representation of the entity
1507
+ - **description**: Description of entity
1508
+ - **concept_type**: Type of the concept
1509
+ - **concept_type_info**: Information on the concept type
1510
+ - **visibility**: Visibility of the entity
1511
+ - **use_for_nel**: Use the entity for named entity linking
1512
+
1513
+ Parameters
1514
+ ----------
1515
+ label: List[Label]
1516
+ List of labels
1517
+ icon: str (optional)
1518
+ Icon
1519
+ description: List[Description] (optional)
1520
+ List of descriptions
1521
+ concept_type: OntologyClassReference
1522
+ Type of the concept
1523
+ uri: str
1524
+ URI for entity. For new entities the URI is None, as the knowledge graph backend assigns this.
1525
+ tenant_rights: TenantAccessRight
1526
+ Rights for tenants
1527
+ owner: bool
1528
+ Is the logged-in user the owner of the entity
1529
+ use_for_nel: bool
1530
+ Use the entity for named entity linking
1531
+ use_vector_index: bool
1532
+ Use vector index for labels
1533
+ use_vector_index_document: bool
1534
+ Use vector index for document
1535
+ use_full_text_index: bool
1536
+ Use full text index for entity
1537
+ """
1538
+
1539
+ def __init__(
1540
+ self,
1541
+ label: List[Label] = None,
1542
+ concept_type: OntologyClassReference = THING_CLASS,
1543
+ description: Optional[List[Description]] = None,
1544
+ uri: Optional[str] = None,
1545
+ icon: Optional[str] = None,
1546
+ tenant_rights: TenantAccessRight = TenantAccessRight(),
1547
+ owner: bool = True,
1548
+ use_for_nel: bool = True,
1549
+ use_vector_index: bool = False,
1550
+ use_vector_index_document: bool = False,
1551
+ use_full_text_index: bool = True,
1552
+ ):
1553
+ self.__uri: str = uri
1554
+ self.__icon: Optional[str] = icon
1555
+ self.__label: List[Label] = label if label else []
1556
+ self.__description: List[Description] = description if description else []
1557
+ self.__alias: List[Label] = []
1558
+ self.__concept_type: OntologyClassReference = concept_type
1559
+ self.__data_properties: Dict[OntologyPropertyReference, List[DataProperty]] = {}
1560
+ self.__object_properties: Dict[OntologyPropertyReference, ObjectProperty] = {}
1561
+ self.__tenants_rights: TenantAccessRight = tenant_rights
1562
+ self.__status_flag: EntityStatus = EntityStatus.UNKNOWN
1563
+ self.__ontology_types: Optional[Set[str]] = None
1564
+ self.__owner: bool = owner
1565
+ self.__owner_id: Optional[str] = None
1566
+ self.__owner_external_user_id: Optional[str] = None
1567
+ self.__group_ids: List[str] = []
1568
+ self.__use_for_nel: bool = use_for_nel
1569
+ self.__use_vector_index: bool = use_vector_index
1570
+ self.__use_vector_index_document: bool = use_vector_index_document
1571
+ self.__use_full_text_index: bool = use_full_text_index
1572
+ self.__visibility: Optional[str] = None
1573
+
1574
+ @property
1575
+ def uri(self) -> str:
1576
+ """Unique identifier for entity."""
1577
+ return self.__uri
1578
+
1579
+ @uri.setter
1580
+ def uri(self, uri: str):
1581
+ self.__uri = uri
1582
+
1583
+ @property
1584
+ def visibility(self) -> str:
1585
+ """Visibility."""
1586
+ return self.__visibility
1587
+
1588
+ @visibility.setter
1589
+ def visibility(self, vis: str):
1590
+ self.__visibility = vis
1591
+
1592
+ @property
1593
+ def use_for_nel(self) -> bool:
1594
+ """Use the entity for named entity linking."""
1595
+ return self.__use_for_nel
1596
+
1597
+ @use_for_nel.setter
1598
+ def use_for_nel(self, use_for_nel: bool):
1599
+ self.__use_for_nel = use_for_nel
1600
+
1601
+ @property
1602
+ def use_full_text_index(self) -> bool:
1603
+ """Use full text index for entity."""
1604
+ return self.__use_full_text_index
1605
+
1606
+ @use_full_text_index.setter
1607
+ def use_full_text_index(self, use_full_text_index: bool):
1608
+ self.__use_full_text_index = use_full_text_index
1609
+
1610
+ @property
1611
+ def use_vector_index(self) -> bool:
1612
+ """Use vector index for entity."""
1613
+ return self.__use_vector_index
1614
+
1615
+ @use_vector_index.setter
1616
+ def use_vector_index(self, use_vector_index: bool):
1617
+ self.__use_vector_index = use_vector_index
1618
+
1619
+ @property
1620
+ def use_vector_index_document(self) -> bool:
1621
+ """Use vector index for document."""
1622
+ return self.__use_vector_index_document
1623
+
1624
+ @use_vector_index_document.setter
1625
+ def use_vector_index_document(self, use_vector_index_document: bool):
1626
+ self.__use_vector_index_document = use_vector_index_document
1627
+
1628
+ @property
1629
+ def owner(self) -> bool:
1630
+ """Is current user the owner of the entity."""
1631
+ return self.__owner
1632
+
1633
+ @property
1634
+ def owner_id(self) -> str:
1635
+ """Internal id of the owner."""
1636
+ return self.__owner_id
1637
+
1638
+ @owner_id.setter
1639
+ def owner_id(self, value: str):
1640
+ self.__owner_id = value
1641
+
1642
+ @property
1643
+ def owner_external_user_id(self) -> Optional[str]:
1644
+ """External user id of the owner."""
1645
+ return self.__owner_external_user_id
1646
+
1647
+ @owner_external_user_id.setter
1648
+ def owner_external_user_id(self, value: str):
1649
+ self.__owner_external_user_id = value
1650
+
1651
+ @property
1652
+ def group_ids(self) -> List[str]:
1653
+ """List of group ids."""
1654
+ return self.__group_ids
1655
+
1656
+ @group_ids.setter
1657
+ def group_ids(self, value: List[str]):
1658
+ self.__group_ids = value
1659
+
1660
+ @property
1661
+ def status_flag(self) -> EntityStatus:
1662
+ """Status flag."""
1663
+ return self.__status_flag
1664
+
1665
+ @status_flag.setter
1666
+ def status_flag(self, flag: EntityStatus):
1667
+ self.__status_flag = flag
1668
+
1669
+ @property
1670
+ def label(self) -> List[Label]:
1671
+ """Labels of the entity."""
1672
+ return self.__label
1673
+
1674
+ @label.setter
1675
+ def label(self, value: List[Label]):
1676
+ self.__label = value
1677
+
1678
+ def add_label(self, label: str, language_code: LocaleCode):
1679
+ """Adding a label for entity.
1680
+
1681
+ Parameters
1682
+ ----------
1683
+ label: str
1684
+ Label
1685
+ language_code: LocaleCode
1686
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1687
+ """
1688
+ self.__label.append(Label(label, language_code, True))
1689
+
1690
+ def update_label(self, value: str, language_code: LocaleCode):
1691
+ """Update or creates a label for a specific language.
1692
+
1693
+ Parameters
1694
+ ----------
1695
+ value: str
1696
+ Value to be set
1697
+ language_code: LocaleCode
1698
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1699
+ """
1700
+ for label in self.label:
1701
+ if label.language_code == language_code:
1702
+ label.content = value
1703
+ return
1704
+ # Label with language does not exist, so create a new label
1705
+ self.add_label(value, language_code)
1706
+
1707
+ def remove_label(self, language_code: LocaleCode):
1708
+ """
1709
+ Remove label for entity if it exists for language.
1710
+
1711
+ Parameters
1712
+ ----------
1713
+ language_code: LocaleCode
1714
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1715
+ """
1716
+ for idx, label in enumerate(self.label):
1717
+ if label.language_code == language_code:
1718
+ del self.label[idx]
1719
+ break
1720
+
1721
+ def remove_alias(self, label: Label):
1722
+ """
1723
+ Remove alias for entity if it exists for language.
1724
+
1725
+ Parameters
1726
+ ----------
1727
+ label: Label
1728
+ Alias label
1729
+ """
1730
+ for idx, alias in enumerate(self.alias):
1731
+ if label.language_code == alias.language_code and label.content == alias.content:
1732
+ del self.alias[idx]
1733
+ break
1734
+
1735
+ def label_lang(self, language_code: LocaleCode) -> Optional[Label]:
1736
+ """
1737
+ Get label for language_code code.
1738
+
1739
+ Parameters
1740
+ ----------
1741
+ language_code: LocaleCode
1742
+ Requested language_code code
1743
+ Returns
1744
+ -------
1745
+ label: Optional[Label]
1746
+ Returns the label for a specific language code
1747
+ """
1748
+ for label in self.label:
1749
+ if label.language_code == language_code:
1750
+ return label
1751
+ return None
1752
+
1753
+ def add_source_system(self, value: DataProperty):
1754
+ """
1755
+ Adding the source system of the entity.
1756
+
1757
+ Parameters
1758
+ -----------
1759
+ value: DataProperty
1760
+ Adds the source system as a Data Property. **Remark:** The data property must have the property type
1761
+ 'wacom:core#sourceSystem'.
1762
+ """
1763
+ if value.data_property_type != SYSTEM_SOURCE_SYSTEM:
1764
+ raise ValueError(
1765
+ f"Data property {value.data_property_type.iri} not supported. " f"Expected:={SYSTEM_SOURCE_SYSTEM.iri}"
1766
+ )
1767
+ if SYSTEM_SOURCE_SYSTEM not in self.__data_properties:
1768
+ self.__data_properties[SYSTEM_SOURCE_SYSTEM] = []
1769
+ for idx in range(0, len(self.__data_properties[SYSTEM_SOURCE_SYSTEM])):
1770
+ if self.__data_properties[SYSTEM_SOURCE_SYSTEM][idx].language_code == value.language_code:
1771
+ del self.__data_properties[SYSTEM_SOURCE_SYSTEM][idx]
1772
+ self.__data_properties[SYSTEM_SOURCE_SYSTEM].append(value)
1773
+
1774
+ @property
1775
+ def source_reference_id(self) -> Optional[List[DataProperty]]:
1776
+ """Reference id for to the source."""
1777
+ if SYSTEM_SOURCE_REFERENCE_ID in self.__data_properties:
1778
+ return self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID]
1779
+ return None
1780
+
1781
+ def add_source_reference_id(self, value: DataProperty):
1782
+ """
1783
+ Adding the reference id from the source system of the entity.
1784
+
1785
+ Parameters
1786
+ -----------
1787
+ value: DataProperty
1788
+ Adds the source system reference id as a Data Property.
1789
+ **Remark:** The data property must have the property type 'wacom:core#sourceReferenceId'.
1790
+ """
1791
+ if value.data_property_type != SYSTEM_SOURCE_REFERENCE_ID:
1792
+ raise ValueError(
1793
+ f"Data property {value.data_property_type.iri} not supported. "
1794
+ f"Expected:={SYSTEM_SOURCE_REFERENCE_ID.iri}"
1795
+ )
1796
+ if SYSTEM_SOURCE_REFERENCE_ID not in self.__data_properties:
1797
+ self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID] = []
1798
+ len_props: int = len(self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID])
1799
+ idx: int = 0
1800
+ while idx < len_props:
1801
+ if self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID][idx].language_code == value.language_code:
1802
+ del self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID][idx]
1803
+ len_props -= 1
1804
+ idx += 1
1805
+ self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID].append(value)
1806
+
1807
+ @property
1808
+ def reference_id(self) -> Optional[str]:
1809
+ """Default reference id for the entity."""
1810
+ if SYSTEM_SOURCE_REFERENCE_ID in self.__data_properties:
1811
+ # The en_US is the default language for the source reference id
1812
+ for sr in self.data_properties[SYSTEM_SOURCE_REFERENCE_ID]:
1813
+ if sr.language_code == EN_US:
1814
+ return sr.value
1815
+ if len(self.data_properties[SYSTEM_SOURCE_REFERENCE_ID]) > 0:
1816
+ return self.data_properties[SYSTEM_SOURCE_REFERENCE_ID][0].value
1817
+ return None
1818
+
1819
+ @reference_id.setter
1820
+ def reference_id(self, value: str):
1821
+ """
1822
+ Setting the default reference id for the entity.
1823
+
1824
+ Parameters
1825
+ ----------
1826
+ value: str
1827
+ Reference id to be set
1828
+ """
1829
+ if SYSTEM_SOURCE_REFERENCE_ID not in self.__data_properties:
1830
+ self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID] = []
1831
+ self.__data_properties[SYSTEM_SOURCE_REFERENCE_ID].append(DataProperty(value, SYSTEM_SOURCE_REFERENCE_ID))
1832
+
1833
+ @property
1834
+ def source_system(self) -> Optional[str]:
1835
+ """Default reference system for the entity."""
1836
+ if SYSTEM_SOURCE_REFERENCE_ID in self.__data_properties:
1837
+ # The en_US is the default language for the source reference system
1838
+ for sr in self.data_properties[SYSTEM_SOURCE_SYSTEM]:
1839
+ if sr.language_code == EN_US:
1840
+ return sr.value
1841
+ if len(self.data_properties[SYSTEM_SOURCE_SYSTEM]) > 0:
1842
+ return self.data_properties[SYSTEM_SOURCE_SYSTEM][0].value
1843
+ return None
1844
+
1845
+ @source_system.setter
1846
+ def source_system(self, value: str):
1847
+ """
1848
+ Setting the default reference system for the entity.
1849
+
1850
+ Parameters
1851
+ ----------
1852
+ value: str
1853
+ Reference id to be set
1854
+ """
1855
+ if SYSTEM_SOURCE_SYSTEM not in self.__data_properties:
1856
+ self.__data_properties[SYSTEM_SOURCE_SYSTEM] = []
1857
+ self.__data_properties[SYSTEM_SOURCE_SYSTEM].append(DataProperty(value, SYSTEM_SOURCE_SYSTEM))
1858
+
1859
+ def default_source_reference_id(self, language_code: LocaleCode = EN_US) -> Optional[str]:
1860
+ """
1861
+ Getting the source reference id for a certain language code.
1862
+
1863
+ Parameters
1864
+ ----------
1865
+ language_code: LocaleCode
1866
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1867
+
1868
+ Returns
1869
+ -------
1870
+ id: str
1871
+ Source reference id.
1872
+ """
1873
+ if SYSTEM_SOURCE_REFERENCE_ID in self.__data_properties:
1874
+ for sr in self.data_properties[SYSTEM_SOURCE_REFERENCE_ID]:
1875
+ if sr.language_code == language_code:
1876
+ return sr.value
1877
+ return None
1878
+
1879
+ def default_source_system(self, language_code: LocaleCode = EN_US) -> Optional[str]:
1880
+ """
1881
+ Getting the source system for a certain language code.
1882
+
1883
+ Parameters
1884
+ ----------
1885
+ language_code: LocaleCode
1886
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1887
+
1888
+ Returns
1889
+ -------
1890
+ id: str
1891
+ Source system.
1892
+ """
1893
+ if SYSTEM_SOURCE_SYSTEM in self.__data_properties:
1894
+ for sr in self.data_properties[SYSTEM_SOURCE_SYSTEM]:
1895
+ if sr.language_code == language_code:
1896
+ return sr.value
1897
+ return None
1898
+
1899
+ @property
1900
+ def image(self) -> Optional[str]:
1901
+ """Image depicting the entities (optional)."""
1902
+ return self.__icon
1903
+
1904
+ @image.setter
1905
+ def image(self, value: str):
1906
+ self.__icon = value
1907
+
1908
+ @property
1909
+ def description(self) -> Optional[List[Description]]:
1910
+ """Description of the thing (optional)."""
1911
+ return self.__description
1912
+
1913
+ @description.setter
1914
+ def description(self, value: List[Description]):
1915
+ self.__description = value
1916
+
1917
+ def add_description(self, description: str, language_code: LocaleCode):
1918
+ """Adding the description for entity.
1919
+
1920
+ Parameters
1921
+ ----------
1922
+ description: str
1923
+ Description
1924
+ language_code: LocaleCode
1925
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1926
+ """
1927
+ self.__description.append(Description(description=description, language_code=language_code))
1928
+
1929
+ def update_description(self, value: str, language_code: LocaleCode):
1930
+ """Update or creates a description for a specific language.
1931
+
1932
+ Parameters
1933
+ ----------
1934
+ value: str
1935
+ Value to be set
1936
+ language_code: LocaleCode
1937
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1938
+ """
1939
+ for desc in self.description:
1940
+ if desc.language_code == language_code:
1941
+ desc.content = value
1942
+ return
1943
+ # Description with language does not exist, so create a new description
1944
+ self.add_description(value, language_code)
1945
+
1946
+ def description_lang(self, language_code: LocaleCode) -> Optional[Description]:
1947
+ """
1948
+ Get description for entity.
1949
+
1950
+ Parameters
1951
+ ----------
1952
+ language_code: LocaleCode
1953
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1954
+ Returns
1955
+ -------
1956
+ label: LocalizedContent
1957
+ Returns the label for a specific language_code code
1958
+ """
1959
+ for desc in self.description:
1960
+ if desc.language_code == language_code:
1961
+ return desc
1962
+ return None
1963
+
1964
+ def remove_description(self, language_code: LocaleCode):
1965
+ """
1966
+ Remove description for entity if it exists for language.
1967
+
1968
+ Parameters
1969
+ ----------
1970
+ language_code: LocaleCode
1971
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
1972
+ """
1973
+ for index, description in enumerate(self.description):
1974
+ if description.language_code == language_code:
1975
+ del self.__description[index]
1976
+ break
1977
+
1978
+ @property
1979
+ def concept_type(self) -> OntologyClassReference:
1980
+ """Concept type."""
1981
+ return self.__concept_type
1982
+
1983
+ @concept_type.setter
1984
+ def concept_type(self, value: OntologyClassReference):
1985
+ self.__concept_type = value
1986
+
1987
+ @property
1988
+ def ontology_types(self) -> Set[str]:
1989
+ """Ontology types. For public entities."""
1990
+ return self.__ontology_types
1991
+
1992
+ @ontology_types.setter
1993
+ def ontology_types(self, value: Set[str]):
1994
+ self.__ontology_types = value
1995
+
1996
+ @property
1997
+ def data_properties(self) -> Dict[OntologyPropertyReference, List[DataProperty]]:
1998
+ """Literals of the concept."""
1999
+ return self.__data_properties
2000
+
2001
+ @data_properties.setter
2002
+ def data_properties(self, data_properties: Dict[OntologyPropertyReference, List[DataProperty]]):
2003
+ """Literals of the concept."""
2004
+ self.__data_properties = data_properties
2005
+
2006
+ @property
2007
+ def object_properties(self) -> Dict[OntologyPropertyReference, ObjectProperty]:
2008
+ """Relations of the concept."""
2009
+ return self.__object_properties
2010
+
2011
+ @object_properties.setter
2012
+ def object_properties(self, relations: Dict[OntologyPropertyReference, ObjectProperty]):
2013
+ self.__object_properties = relations
2014
+
2015
+ def data_property_lang(
2016
+ self, data_property: OntologyPropertyReference, language_code: LocaleCode
2017
+ ) -> List[DataProperty]:
2018
+ """
2019
+ Get data property for language_code code.
2020
+
2021
+ Parameters
2022
+ ----------
2023
+ data_property: OntologyPropertyReference
2024
+ Data property.
2025
+ language_code: LocaleCode
2026
+ Requested language_code code
2027
+ Returns
2028
+ -------
2029
+ data_properties: List[DataProperty]
2030
+ Returns a list of data properties for a specific language code
2031
+ """
2032
+ return [d for d in self.data_properties.get(data_property, []) if d.language_code == language_code]
2033
+
2034
+ def remove_data_property(self, data_property: OntologyPropertyReference):
2035
+ """Remove data property.
2036
+
2037
+ Parameters
2038
+ ----------
2039
+ data_property: OntologyPropertyReference
2040
+ Data property to be removed.
2041
+ """
2042
+ self.__data_properties.pop(data_property, None)
2043
+
2044
+ @property
2045
+ def alias(self) -> List[Label]:
2046
+ """Alternative labels of the concept."""
2047
+ return self.__alias
2048
+
2049
+ @alias.setter
2050
+ def alias(self, alias: List[Label]):
2051
+ self.__alias = alias
2052
+
2053
+ def alias_lang(self, language_code: LocaleCode) -> List[Label]:
2054
+ """
2055
+ Get alias for language_code code.
2056
+
2057
+ Parameters
2058
+ ----------
2059
+ language_code: LocaleCode
2060
+ Requested language_code code
2061
+ Returns
2062
+ -------
2063
+ aliases: List[Label]
2064
+ Returns a list of aliases for a specific language code
2065
+ """
2066
+ aliases: List[Label] = []
2067
+ for alias in self.alias:
2068
+ if alias.language_code == language_code:
2069
+ aliases.append(alias)
2070
+ return aliases
2071
+
2072
+ def update_alias(self, value: str, language_code: LocaleCode):
2073
+ """Update or creates an alias for a specific language.
2074
+
2075
+ Parameters
2076
+ ----------
2077
+ value: str
2078
+ Value to be set
2079
+ language_code: LocaleCode
2080
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
2081
+ """
2082
+ for a in self.alias:
2083
+ if a.language_code == language_code:
2084
+ a.content = value
2085
+ return
2086
+ # Label with language does not exist, so create a new label
2087
+ self.add_alias(value, language_code=language_code)
2088
+
2089
+ def add_relation(self, prop: ObjectProperty):
2090
+ """Adding a relation to the entity.
2091
+
2092
+ Parameters
2093
+ ----------
2094
+ prop: ObjectProperty
2095
+ Object property that is added
2096
+ """
2097
+ if prop.relation in self.object_properties:
2098
+ self.__object_properties[prop.relation].incoming_relations.extend(prop.incoming_relations)
2099
+ self.__object_properties[prop.relation].outgoing_relations.extend(prop.outgoing_relations)
2100
+ else:
2101
+ self.__object_properties[prop.relation] = prop
2102
+
2103
+ def add_data_property(self, data_property: DataProperty):
2104
+ """Add data property to the entity.
2105
+
2106
+ Parameters
2107
+ ----------
2108
+ data_property: DataProperty
2109
+ Data property that is added
2110
+ """
2111
+ if data_property.data_property_type not in self.__data_properties:
2112
+ self.__data_properties[data_property.data_property_type] = []
2113
+ self.__data_properties[data_property.data_property_type].append(data_property)
2114
+
2115
+ def add_alias(self, alias: str, language_code: LocaleCode):
2116
+ """Adding an alias for entity.
2117
+
2118
+ Parameters
2119
+ ----------
2120
+ alias: str
2121
+ Alias
2122
+ language_code: LocaleCode
2123
+ ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., 'en_US'.
2124
+ """
2125
+ self.__alias.append(Label(alias, language_code, False))
2126
+
2127
+ @property
2128
+ def tenant_access_right(self) -> TenantAccessRight:
2129
+ """Access rights for tenant."""
2130
+ return self.__tenants_rights
2131
+
2132
+ @tenant_access_right.setter
2133
+ def tenant_access_right(self, rights: TenantAccessRight):
2134
+ self.__tenants_rights = rights
2135
+
2136
+ def __dict__(self):
2137
+ labels: List[Dict[str, Any]] = []
2138
+ labels.extend([la.__dict__() for la in self.label])
2139
+ labels.extend([la.__dict__() for la in self.alias])
2140
+ dict_object: Dict[str, Any] = {
2141
+ URI_TAG: self.uri,
2142
+ IMAGE_TAG: self.image,
2143
+ LABELS_TAG: labels,
2144
+ DESCRIPTIONS_TAG: [desc.__dict__() for desc in self.description],
2145
+ TYPE_TAG: self.concept_type.iri,
2146
+ STATUS_FLAG_TAG: self.status_flag.value,
2147
+ DATA_PROPERTIES_TAG: {},
2148
+ OBJECT_PROPERTIES_TAG: {},
2149
+ GROUP_IDS: self.group_ids,
2150
+ OWNER_TAG: self.owner,
2151
+ OWNER_ID_TAG: self.owner_id,
2152
+ }
2153
+ for literal_type, items in self.data_properties.items():
2154
+ dict_object[DATA_PROPERTIES_TAG][literal_type.iri] = [i.__dict__() for i in items]
2155
+ for relation_type, item in self.object_properties.items():
2156
+ dict_object[OBJECT_PROPERTIES_TAG][relation_type.iri] = item.__dict__()
2157
+ return dict_object
2158
+
2159
+ def __import_format_dict__(
2160
+ self, group_ids: List[str] = None, external_user_id: Optional[str] = None, reference_id: Optional[str] = None
2161
+ ) -> Dict[str, Any]:
2162
+ """
2163
+ Export the entity to a dictionary.
2164
+ Parameters
2165
+ ----------
2166
+ group_ids: List[str]
2167
+ List of group ids
2168
+ external_user_id: Optional[str]
2169
+ External user id
2170
+ reference_id: Optional[str]
2171
+ Override the reference id
2172
+
2173
+ Returns
2174
+ -------
2175
+ dict_object: Dict[str, Any]
2176
+ Dictionary of the entity in the import format
2177
+ """
2178
+ labels: List[Dict[str, Any]] = []
2179
+ labels.extend([la.__dict__() for la in self.label])
2180
+ labels.extend([la.__dict__() for la in self.alias])
2181
+ dict_object: Dict[str, Any] = {
2182
+ SOURCE_REFERENCE_ID_TAG: self.source_reference_id if self.source_reference_id is None else reference_id,
2183
+ SOURCE_SYSTEM_TAG: self.source_system,
2184
+ IMAGE_TAG: self.image,
2185
+ LABELS_TAG: labels,
2186
+ DESCRIPTIONS_TAG: [desc.__dict__() for desc in self.description],
2187
+ TYPE_TAG: self.concept_type.iri,
2188
+ DATA_PROPERTIES_TAG: [],
2189
+ OBJECT_PROPERTIES_TAG: [],
2190
+ TENANT_RIGHTS_TAG: self.tenant_access_right.to_list(),
2191
+ GROUP_IDS: group_ids if group_ids else [],
2192
+ TARGETS_TAG: [],
2193
+ }
2194
+ if self.use_for_nel:
2195
+ dict_object[TARGETS_TAG].append(INDEXING_NEL_TARGET)
2196
+ if self.use_vector_index:
2197
+ dict_object[TARGETS_TAG].append(INDEXING_VECTOR_SEARCH_TARGET)
2198
+ if self.use_vector_index_document:
2199
+ dict_object[TARGETS_TAG].append(INDEXING_VECTOR_SEARCH_DOCUMENT_TARGET)
2200
+ if not self.use_full_text_index:
2201
+ dict_object[TARGETS_TAG].append(INDEXING_FULLTEXT_TARGET)
2202
+ if external_user_id:
2203
+ dict_object[EXTERNAL_USER_ID_TAG] = external_user_id
2204
+ for _, items in self.data_properties.items():
2205
+ dict_object[DATA_PROPERTIES_TAG].extend([i.__dict__() for i in items])
2206
+ for _, item in self.object_properties.items():
2207
+ dict_object[OBJECT_PROPERTIES_TAG].append(item.__dict__())
2208
+ return dict_object
2209
+
2210
+ @staticmethod
2211
+ def from_import_dict(entity: Dict[str, Any]) -> "ThingObject":
2212
+ """Creates a ThingObject from a dict.
2213
+
2214
+ Parameters
2215
+ ----------
2216
+ entity: Dict[str, Any]
2217
+ Dictionary that contains the data of the entity
2218
+
2219
+ Returns
2220
+ -------
2221
+ instance: ThingObject
2222
+ The ThingObject that is created from the dict
2223
+ """
2224
+ labels: List[Label] = []
2225
+ alias: List[Label] = []
2226
+ descriptions: List[Description] = []
2227
+ main_labels: Dict[str, bool] = {}
2228
+ for label in entity[LABELS_TAG]:
2229
+ if label[LOCALE_TAG] in SUPPORTED_LOCALES:
2230
+ if label[IS_MAIN_TAG]:
2231
+ main_label: Label = Label.create_from_dict(label)
2232
+ main_labels[label[LOCALE_TAG]] = True
2233
+ labels.append(main_label)
2234
+ else:
2235
+ alias_label: Label = Label.create_from_dict(label)
2236
+ if alias_label.language_code not in main_labels:
2237
+ main_labels[alias_label.language_code] = False
2238
+ alias.append(alias_label)
2239
+ for lang_code, is_main in main_labels.items():
2240
+ if not is_main:
2241
+ la_idx: int = 0
2242
+ while la_idx < len(alias):
2243
+ # Fix things where there is no main label
2244
+ if alias[la_idx].language_code == lang_code:
2245
+ al: Label = alias[la_idx]
2246
+ labels.append(Label(al.content, al.language_code, main=True))
2247
+ del alias[la_idx]
2248
+ else:
2249
+ la_idx += 1
2250
+
2251
+ for desc in entity[DESCRIPTIONS_TAG]:
2252
+ if desc[LOCALE_TAG] in SUPPORTED_LOCALES:
2253
+ if desc[DESCRIPTION_TAG] is None:
2254
+ logger.warning(f"Description is None for {desc}")
2255
+ else:
2256
+ descriptions.append(Description.create_from_dict(desc))
2257
+ # Backwards-compatibility
2258
+ use_nel: bool = entity.get(USE_NEL_TAG, False)
2259
+ use_vector_index: bool = entity.get(USE_VECTOR_INDEX_TAG, False)
2260
+ use_vector_index_document: bool = entity.get(USE_VECTOR_DOCUMENT_INDEX_TAG, False)
2261
+ use_full_text_index: bool = entity.get(USE_FULLTEXT_TAG, True)
2262
+ # Vector index target
2263
+ if INDEXING_VECTOR_SEARCH_TARGET in entity.get(TARGETS_TAG, []):
2264
+ use_vector_index = True
2265
+ # Vector index document target
2266
+ if INDEXING_VECTOR_SEARCH_DOCUMENT_TARGET in entity.get(TARGETS_TAG, []):
2267
+ use_vector_index_document = True
2268
+ # Full text index target
2269
+ if INDEXING_FULLTEXT_TARGET in entity.get(TARGETS_TAG, []):
2270
+ use_full_text_index = True
2271
+ # Named entity linking target
2272
+ if INDEXING_NEL_TARGET in entity.get(TARGETS_TAG, []):
2273
+ use_nel = True
2274
+ thing: ThingObject = ThingObject(
2275
+ label=labels,
2276
+ icon=entity[IMAGE_TAG],
2277
+ description=descriptions,
2278
+ concept_type=OntologyClassReference.parse(entity[TYPE_TAG]),
2279
+ use_for_nel=use_nel,
2280
+ use_vector_index=use_vector_index,
2281
+ use_vector_index_document=use_vector_index_document,
2282
+ use_full_text_index=use_full_text_index,
2283
+ )
2284
+ if EXTERNAL_USER_ID_TAG in entity:
2285
+ thing.owner_external_user_id = entity[EXTERNAL_USER_ID_TAG]
2286
+ if DATA_PROPERTIES_TAG in entity:
2287
+ if isinstance(entity[DATA_PROPERTIES_TAG], dict):
2288
+ for data_property_type_str, data_properties in entity[DATA_PROPERTIES_TAG].items():
2289
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2290
+ data_property_type_str
2291
+ )
2292
+ for data_property in data_properties:
2293
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2294
+ value: str = data_property[VALUE_TAG]
2295
+ thing.add_data_property(DataProperty(value, data_property_type, language_code))
2296
+ elif isinstance(entity[DATA_PROPERTIES_TAG], list):
2297
+ for data_property in entity[DATA_PROPERTIES_TAG]:
2298
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2299
+ value: str = data_property[VALUE_TAG]
2300
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2301
+ data_property[DATA_PROPERTY_TAG]
2302
+ )
2303
+ thing.add_data_property(DataProperty(value, data_property_type, language_code))
2304
+ if OBJECT_PROPERTIES_TAG in entity:
2305
+ for object_property in entity[OBJECT_PROPERTIES_TAG]:
2306
+ _, obj = ObjectProperty.create_from_dict(object_property)
2307
+ thing.add_relation(obj)
2308
+ thing.alias = alias
2309
+ thing.group_ids = entity.get(GROUP_IDS, [])
2310
+ # Finally, retrieve rights
2311
+ if TENANT_RIGHTS_TAG in entity:
2312
+ thing.tenant_access_right = TenantAccessRight.parse(entity[TENANT_RIGHTS_TAG])
2313
+ return thing
2314
+
2315
+ @staticmethod
2316
+ def from_dict(entity: Dict[str, Any]) -> "ThingObject":
2317
+ """Creates a ThingObject from a dict.
2318
+
2319
+ Parameters
2320
+ ----------
2321
+ entity: Dict[str, Any]
2322
+ Dictionary that contains the data of the entity
2323
+
2324
+ Returns
2325
+ -------
2326
+ instance: ThingObject
2327
+ The ThingObject that is created from the dict
2328
+ """
2329
+ labels: List[Label] = []
2330
+ alias: List[Label] = []
2331
+ descriptions: List[Description] = []
2332
+
2333
+ for label in entity[LABELS_TAG]:
2334
+ if label[LOCALE_TAG] in SUPPORTED_LOCALES:
2335
+ if label[IS_MAIN_TAG]:
2336
+ labels.append(Label.create_from_dict(label))
2337
+ else:
2338
+ alias.append(Label.create_from_dict(label))
2339
+
2340
+ for desc in entity[DESCRIPTIONS_TAG]:
2341
+ descriptions.append(Description.create_from_dict(desc))
2342
+
2343
+ use_nel: bool = False
2344
+ use_vector_index: bool = False
2345
+ use_vector_index_document: bool = False
2346
+ if TARGETS_TAG in entity:
2347
+ use_nel = INDEXING_NEL_TARGET in entity[TARGETS_TAG]
2348
+ use_vector_index = INDEXING_VECTOR_SEARCH_TARGET in entity[TARGETS_TAG]
2349
+ use_vector_index_document = INDEXING_VECTOR_SEARCH_DOCUMENT_TARGET in entity[TARGETS_TAG]
2350
+ use_fulltext_index = INDEXING_FULLTEXT_TARGET in entity[TARGETS_TAG]
2351
+ else:
2352
+ if USE_NEL_TAG in entity:
2353
+ use_nel = entity[USE_NEL_TAG]
2354
+ elif SEND_TO_NEL in entity:
2355
+ use_nel = entity[SEND_TO_NEL]
2356
+ if USE_VECTOR_INDEX_TAG in entity:
2357
+ use_vector_index = entity[USE_VECTOR_INDEX_TAG]
2358
+ elif SEND_VECTOR_INDEX_TAG in entity:
2359
+ use_vector_index = entity[SEND_VECTOR_INDEX_TAG]
2360
+ use_fulltext_index = True
2361
+ visibility: Optional[str] = entity.get(VISIBILITY_TAG)
2362
+ thing: ThingObject = ThingObject(
2363
+ label=labels,
2364
+ icon=entity[IMAGE_TAG],
2365
+ description=descriptions,
2366
+ uri=entity[URI_TAG],
2367
+ concept_type=OntologyClassReference.parse(entity[TYPE_TAG]),
2368
+ owner=entity.get(OWNER_TAG, True),
2369
+ use_for_nel=use_nel,
2370
+ use_vector_index=use_vector_index,
2371
+ use_vector_index_document=use_vector_index_document,
2372
+ use_full_text_index=use_fulltext_index,
2373
+ )
2374
+ thing.visibility = visibility
2375
+ thing.owner_id = entity.get(OWNER_ID_TAG)
2376
+ thing.group_ids = entity.get(GROUP_IDS)
2377
+ if DATA_PROPERTIES_TAG in entity:
2378
+ if isinstance(entity[DATA_PROPERTIES_TAG], dict):
2379
+ for data_property_type_str, data_properties in entity[DATA_PROPERTIES_TAG].items():
2380
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2381
+ data_property_type_str
2382
+ )
2383
+ for data_property in data_properties:
2384
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2385
+ value: str = data_property[VALUE_TAG]
2386
+ thing.add_data_property(DataProperty(value, data_property_type, language_code))
2387
+ elif isinstance(entity[DATA_PROPERTIES_TAG], list):
2388
+ for data_property in entity[DATA_PROPERTIES_TAG]:
2389
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2390
+ value: str = data_property[VALUE_TAG]
2391
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2392
+ data_property[DATA_PROPERTY_TAG]
2393
+ )
2394
+ thing.add_data_property(DataProperty(value, data_property_type, language_code))
2395
+ if OBJECT_PROPERTIES_TAG in entity:
2396
+ for object_property in entity[OBJECT_PROPERTIES_TAG].values():
2397
+ _, obj = ObjectProperty.create_from_dict(object_property)
2398
+ thing.add_relation(obj)
2399
+ thing.alias = alias
2400
+ # Finally, retrieve rights
2401
+ if TENANT_RIGHTS_TAG in entity and entity[TENANT_RIGHTS_TAG]:
2402
+ thing.tenant_access_right = TenantAccessRight.parse(entity[TENANT_RIGHTS_TAG])
2403
+ return thing
2404
+
2405
+ def __getstate__(self) -> Dict[str, Any]:
2406
+ return self.__dict__()
2407
+
2408
+ def __setstate__(self, state: Dict[str, Any]):
2409
+ self.__label: List[Label] = []
2410
+ self.__description: List[Description] = []
2411
+ self.__alias: List[Label] = []
2412
+ self.__data_properties: Dict[OntologyPropertyReference, List[DataProperty]] = {}
2413
+ self.__object_properties: Dict[OntologyPropertyReference, ObjectProperty] = {}
2414
+ self.__status_flag: EntityStatus = EntityStatus.UNKNOWN
2415
+ self.__ontology_types: Optional[Set[str]] = None
2416
+ self.__owner_id: Optional[str] = None
2417
+ self.__group_ids: List[str] = []
2418
+ self.__visibility: Optional[str] = None
2419
+
2420
+ for label in state[LABELS_TAG]:
2421
+ if label[LOCALE_TAG] in SUPPORTED_LOCALES:
2422
+ if label[IS_MAIN_TAG]:
2423
+ self.__label.append(Label.create_from_dict(label))
2424
+ else:
2425
+ self.__alias.append(Label.create_from_dict(label))
2426
+
2427
+ for desc in state[DESCRIPTIONS_TAG]:
2428
+ self.__description.append(Description.create_from_dict(desc))
2429
+
2430
+ use_nel: bool = state.get(USE_NEL_TAG, True)
2431
+ visibility: Optional[str] = state.get(VISIBILITY_TAG)
2432
+ self.__icon = state[IMAGE_TAG]
2433
+ self.__uri = state[URI_TAG]
2434
+ self.__concept_type = OntologyClassReference.parse(state[TYPE_TAG])
2435
+ self.__owner = state.get(OWNER_TAG, True)
2436
+ self.__use_for_nel = use_nel
2437
+ self.__use_vector_index = state.get(USE_VECTOR_INDEX_TAG, False)
2438
+ self.__use_vector_index_document = state.get(USE_VECTOR_DOCUMENT_INDEX_TAG, False)
2439
+ self.__use_full_text_index = state.get(USE_FULLTEXT_TAG, True)
2440
+ self.__visibility = visibility
2441
+ self.__owner_id = state.get(OWNER_ID_TAG)
2442
+ self.__group_ids = state.get(GROUP_IDS)
2443
+ if DATA_PROPERTIES_TAG in state:
2444
+ if isinstance(state[DATA_PROPERTIES_TAG], dict):
2445
+ for data_property_type_str, data_properties in state[DATA_PROPERTIES_TAG].items():
2446
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2447
+ data_property_type_str
2448
+ )
2449
+ for data_property in data_properties:
2450
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2451
+ value: str = data_property[VALUE_TAG]
2452
+ self.add_data_property(DataProperty(value, data_property_type, language_code))
2453
+ elif isinstance(state[DATA_PROPERTIES_TAG], list):
2454
+ for data_property in state[DATA_PROPERTIES_TAG]:
2455
+ language_code: LocaleCode = LocaleCode(data_property[LOCALE_TAG])
2456
+ value: str = data_property[VALUE_TAG]
2457
+ data_property_type: OntologyPropertyReference = OntologyPropertyReference.parse(
2458
+ data_property[DATA_PROPERTY_TAG]
2459
+ )
2460
+ self.add_data_property(DataProperty(value, data_property_type, language_code))
2461
+ if OBJECT_PROPERTIES_TAG in state:
2462
+ for object_property in state[OBJECT_PROPERTIES_TAG].values():
2463
+ _, obj = ObjectProperty.create_from_dict(object_property)
2464
+ self.add_relation(obj)
2465
+ # Finally, retrieve rights
2466
+ if TENANT_RIGHTS_TAG in state:
2467
+ self.tenant_access_right = TenantAccessRight.parse(state[TENANT_RIGHTS_TAG])
2468
+ else:
2469
+ self.tenant_access_right = TenantAccessRight()
2470
+
2471
+ def __hash__(self):
2472
+ return 0
2473
+
2474
+ def __eq__(self, other: Any):
2475
+ # another object is equal to self, iff
2476
+ # it is an instance of MyClass
2477
+ if not isinstance(other, ThingObject):
2478
+ return False
2479
+ if self.uri != other.uri:
2480
+ return False
2481
+ other_thing: ThingObject = other
2482
+ # Check if the descriptions are different
2483
+ if len(self.description) != len(other_thing.description):
2484
+ return False
2485
+ for desc_file in self.description:
2486
+ kg_desc: Optional[Description] = other_thing.description_lang(desc_file.language_code)
2487
+ if kg_desc is None or desc_file.content != kg_desc.content:
2488
+ return False
2489
+ # Difference in vector index
2490
+ if self.use_vector_index != other_thing.use_vector_index:
2491
+ return False
2492
+ # Difference in NEL index
2493
+ if self.use_for_nel != other_thing.use_for_nel:
2494
+ return False
2495
+ if self.use_vector_index_document != other_thing.use_vector_index_document:
2496
+ return False
2497
+
2498
+ # Different number of labels
2499
+ if len(self.label) != len(other_thing.label):
2500
+ return False
2501
+ # Check if the labels are different
2502
+ for label_file in self.label:
2503
+ label_kg_lang: Optional[Label] = other_thing.label_lang(label_file.language_code)
2504
+ if label_kg_lang is None or label_file.content != label_kg_lang.content:
2505
+ return False
2506
+
2507
+ # Different number of aliases
2508
+ if len(self.alias) != len(other_thing.alias):
2509
+ return False
2510
+
2511
+ # Check if the aliases are different
2512
+ for alias_file in self.alias:
2513
+ alias_kg_lang = other_thing.alias_lang(alias_file.language_code)
2514
+ if alias_file.content not in [alias.content for alias in alias_kg_lang]:
2515
+ return False
2516
+
2517
+ # If the image is different
2518
+ if self.image != other_thing.image:
2519
+ return False
2520
+
2521
+ # If the data properties are different
2522
+ if len(self.data_properties) != len(other_thing.data_properties):
2523
+ return False
2524
+
2525
+ for prop, data_properties in self.data_properties.items():
2526
+ if prop not in other_thing.data_properties:
2527
+ return False
2528
+ if len(data_properties) != len(other_thing.data_properties.get(prop, [])):
2529
+ return False
2530
+
2531
+ for dp in data_properties:
2532
+ if prop not in other_thing.data_properties:
2533
+ return False
2534
+ if dp.value not in [d.value for d in other_thing.data_properties.get(prop)]:
2535
+ return False
2536
+ return True
2537
+
2538
+ def __repr__(self):
2539
+ return (
2540
+ f'<{self.concept_type.iri if self.__concept_type else "UNSET"}: uri:={self.uri}, labels:={self.label}, '
2541
+ f"tenant access right:={self.tenant_access_right}]>"
2542
+ )
2543
+
2544
+
2545
+ # --------------------------------------------- Inflection setting -----------------------------------------------------
2546
+ class InflectionSetting(abc.ABC):
2547
+ """
2548
+ Inflection settings
2549
+ --------------------
2550
+
2551
+ Parameters
2552
+ ----------
2553
+ concept: str
2554
+ Concept class
2555
+ inflection: str
2556
+ Inflection setting
2557
+ case_sensitive: bool
2558
+ Entity labels of the class treated case-sensitive
2559
+ """
2560
+
2561
+ def __init__(self, concept: str, inflection: str, case_sensitive: bool):
2562
+ self.__concept: OntologyClassReference = OntologyClassReference.parse(concept)
2563
+ self.__inflection: str = inflection
2564
+ self.__case_sensitive: bool = case_sensitive
2565
+
2566
+ @property
2567
+ def concept(self) -> OntologyClassReference:
2568
+ """Concept class."""
2569
+ return self.__concept
2570
+
2571
+ @property
2572
+ def inflection(self) -> str:
2573
+ """Inflection setting"""
2574
+ return self.__inflection
2575
+
2576
+ @property
2577
+ def case_sensitive(self) -> bool:
2578
+ """Are entity labels of the class treated case-sensitive."""
2579
+ return self.__case_sensitive
2580
+
2581
+ @staticmethod
2582
+ def from_dict(entity: Dict[str, Any]) -> "InflectionSetting":
2583
+ """
2584
+ Create inflection setting from dictionary.
2585
+ Parameters
2586
+ ----------
2587
+ entity: Dict[str, Any]
2588
+ Entity dictionary
2589
+
2590
+ Returns
2591
+ -------
2592
+ instance: InflectionSetting
2593
+ Inflection setting instance
2594
+ """
2595
+ concept_class: str = ""
2596
+ inflection_setting: str = ""
2597
+ case_sensitive: bool = False
2598
+ if INFLECTION_CONCEPT_CLASS in entity:
2599
+ concept_class = entity[INFLECTION_CONCEPT_CLASS]
2600
+ if INFLECTION_SETTING in entity:
2601
+ inflection_setting = entity[INFLECTION_SETTING]
2602
+ if INFLECTION_CASE_SENSITIVE in entity:
2603
+ case_sensitive = entity[INFLECTION_CASE_SENSITIVE]
2604
+ return InflectionSetting(concept=concept_class, inflection=inflection_setting, case_sensitive=case_sensitive)
2605
+
2606
+
2607
+ # -------------------------------------------------- Encoder -----------------------------------------------------------
2608
+ class ThingEncoder(JSONEncoder):
2609
+ """
2610
+ Thing encoder
2611
+ -------------
2612
+ Encoder for ThingObject, Label and Description objects.
2613
+ """
2614
+
2615
+ def default(self, o: Any):
2616
+ if isinstance(o, (Label, Description, ThingObject)):
2617
+ return o.__dict__()
2618
+ return str(o)
2619
+
2620
+
2621
+ def ontology_import(rdf_content: str, tenant_id: str = "", context: str = "") -> Ontology:
2622
+ """Import Ontology from RDF ontology file.
2623
+
2624
+ Parameters
2625
+ ----------
2626
+ rdf_content: str
2627
+ Content of the RDF content file.
2628
+ tenant_id: str (default:= '')
2629
+ Tenant ID.
2630
+ context: str (default:= '')
2631
+ Context file.
2632
+
2633
+ Returns
2634
+ -------
2635
+ ontology: Ontology
2636
+ Instance of ontology.
2637
+ """
2638
+ rdf_graph: Graph = Graph().parse(data=rdf_content, format="xml")
2639
+ ontology: Ontology = Ontology()
2640
+ # Parse classes
2641
+ for cls_iri in [s for s, p, o in list(rdf_graph.triples((None, RDF.type, OWL.Class)))]:
2642
+ subclass_of: Optional[OntologyClassReference] = None
2643
+ comments: List[Comment] = []
2644
+ labels: List[OntologyLabel] = []
2645
+ for _, _, o in list(rdf_graph.triples((cls_iri, RDFS.comment, None))):
2646
+ if isinstance(o, Literal):
2647
+ comments.append(Comment(str(o), LanguageCode(o.language)))
2648
+ for _, _, o in list(rdf_graph.triples((cls_iri, PREFERRED_LABEL, None))):
2649
+ if isinstance(o, Literal):
2650
+ labels.append(OntologyLabel(str(o), LanguageCode(o.language)))
2651
+ for _, _, o in list(rdf_graph.triples((cls_iri, RDFS.subClassOf, None))):
2652
+ subclass_of = OntologyClassReference.parse(str(o))
2653
+ ontology.add_class(
2654
+ OntologyClass(
2655
+ tenant_id=tenant_id,
2656
+ context=context,
2657
+ reference=OntologyClassReference.parse(str(cls_iri)),
2658
+ subclass_of=subclass_of,
2659
+ labels=labels,
2660
+ comments=comments,
2661
+ )
2662
+ )
2663
+
2664
+ # Parse data properties
2665
+ for data_property_iri in [s for s, p, o in list(rdf_graph.triples((None, RDF.type, OWL.DatatypeProperty)))]:
2666
+ subproperty_of: Optional[OntologyPropertyReference] = None
2667
+ range_prop: List[DataPropertyType] = []
2668
+ domain_prop: List[OntologyClassReference] = []
2669
+ comments: List[Comment] = []
2670
+ labels: List[OntologyLabel] = []
2671
+ inverse_prop: Optional[OntologyPropertyReference] = None
2672
+ for _, _, obj in list(rdf_graph.triples((data_property_iri, RDFS.range, None))):
2673
+ range_prop.append(INVERSE_DATA_PROPERTY_TYPE_MAPPING[str(obj)])
2674
+ for _, _, obj in list(rdf_graph.triples((data_property_iri, RDFS.domain, None))):
2675
+ domain_prop.append(OntologyClassReference.parse(str(obj)))
2676
+ for _, _, obj in list(rdf_graph.triples((data_property_iri, OWL.inverseOf, None))):
2677
+ inverse_prop = OntologyPropertyReference.parse(str(obj))
2678
+ for _, _, obj in list(rdf_graph.triples((data_property_iri, RDFS.subPropertyOf, None))):
2679
+ subproperty_of = OntologyPropertyReference.parse(str(obj))
2680
+ for _, _, o in list(rdf_graph.triples((data_property_iri, RDFS.comment, None))):
2681
+ if isinstance(o, Literal):
2682
+ comments.append(Comment(str(o), LanguageCode(o.language)))
2683
+ for _, _, o in list(rdf_graph.triples((data_property_iri, PREFERRED_LABEL, None))):
2684
+ if isinstance(o, Literal):
2685
+ labels.append(OntologyLabel(str(o), LanguageCode(o.language)))
2686
+ ontology.add_properties(
2687
+ OntologyProperty(
2688
+ kind=PropertyType.DATA_PROPERTY,
2689
+ tenant_id=tenant_id,
2690
+ context=context,
2691
+ name=OntologyPropertyReference.parse(str(data_property_iri)),
2692
+ property_range=range_prop,
2693
+ property_domain=domain_prop,
2694
+ sub_property_of=subproperty_of,
2695
+ inverse_property_of=inverse_prop,
2696
+ labels=labels,
2697
+ comments=comments,
2698
+ )
2699
+ )
2700
+ # Parse object properties
2701
+ for object_property_iri in [s for s, p, o in list(rdf_graph.triples((None, RDF.type, OWL.ObjectProperty)))]:
2702
+ subproperty_of: Optional[OntologyPropertyReference] = None
2703
+ obj_range_prop: List[OntologyClassReference] = []
2704
+ domain_prop: List[OntologyClassReference] = []
2705
+ inverse_prop: Optional[OntologyPropertyReference] = None
2706
+ comments: List[Comment] = []
2707
+ labels: List[OntologyLabel] = []
2708
+ for _, _, o_range in list(rdf_graph.triples((object_property_iri, RDFS.range, None))):
2709
+ obj_range_prop.append(OntologyClassReference.parse(str(o_range)))
2710
+ for _, _, o_domain in list(rdf_graph.triples((object_property_iri, RDFS.domain, None))):
2711
+ domain_prop.append(OntologyClassReference.parse(str(o_domain)))
2712
+ for _, _, o_inverse in list(rdf_graph.triples((object_property_iri, OWL.inverseOf, None))):
2713
+ inverse_prop = OntologyPropertyReference.parse(str(o_inverse))
2714
+ for _, _, o_sub in list(rdf_graph.triples((object_property_iri, RDFS.subPropertyOf, None))):
2715
+ subproperty_of = OntologyPropertyReference.parse(str(o_sub))
2716
+ for _, _, o in list(rdf_graph.triples((object_property_iri, RDFS.comment, None))):
2717
+ if isinstance(o, Literal):
2718
+ comments.append(Comment(str(o), LanguageCode(o.language)))
2719
+ for _, _, o in list(rdf_graph.triples((object_property_iri, PREFERRED_LABEL, None))):
2720
+ if isinstance(o, Literal):
2721
+ labels.append(OntologyLabel(str(o), LanguageCode(o.language)))
2722
+ ontology.add_properties(
2723
+ OntologyProperty(
2724
+ kind=PropertyType.OBJECT_PROPERTY,
2725
+ tenant_id=tenant_id,
2726
+ context=context,
2727
+ name=OntologyPropertyReference.parse(str(object_property_iri)),
2728
+ property_range=obj_range_prop,
2729
+ property_domain=domain_prop,
2730
+ sub_property_of=subproperty_of,
2731
+ inverse_property_of=inverse_prop,
2732
+ )
2733
+ )
2734
+ return ontology