pyMetaModel 0.6.6__tar.gz → 0.6.9__tar.gz

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.
Files changed (108) hide show
  1. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/PKG-INFO +1 -1
  2. pymetamodel-0.6.9/meta/__init__.py +1 -0
  3. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/metamodel.py +68 -75
  4. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/version.py +1 -1
  5. pymetamodel-0.6.9/tests/test_metamodel.py +81 -0
  6. pymetamodel-0.6.6/meta/__init__.py +0 -1
  7. pymetamodel-0.6.6/tests/test_metamodel.py +0 -36
  8. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/.github/workflows/build.yml +0 -0
  9. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/.github/workflows/upload-to-pypi.yml +0 -0
  10. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/.gitignore +0 -0
  11. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/.project +0 -0
  12. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/.pydevproject +0 -0
  13. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/LICENSE +0 -0
  14. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/README.md +0 -0
  15. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/docs/index.md +0 -0
  16. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.mermaid +0 -0
  17. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.puml +0 -0
  18. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.py +0 -0
  19. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.sidif +0 -0
  20. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.xlsx +0 -0
  21. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.yaml +0 -0
  22. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws_puml.txt +0 -0
  23. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.mermaid +0 -0
  24. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.puml +0 -0
  25. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.py +0 -0
  26. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.sidif +0 -0
  27. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.xlsx +0 -0
  28. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city.yaml +0 -0
  29. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/city/city_puml.txt +0 -0
  30. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.mermaid +0 -0
  31. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.puml +0 -0
  32. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.py +0 -0
  33. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.sidif +0 -0
  34. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.xlsx +0 -0
  35. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext.yaml +0 -0
  36. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/family/FamilyContext_puml.txt +0 -0
  37. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.mermaid +0 -0
  38. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.puml +0 -0
  39. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.py +0 -0
  40. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.sidif +0 -0
  41. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.xlsx +0 -0
  42. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.yaml +0 -0
  43. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure_puml.txt +0 -0
  44. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.mermaid +0 -0
  45. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.puml +0 -0
  46. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.py +0 -0
  47. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.sidif +0 -0
  48. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.xlsx +0 -0
  49. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel.yaml +0 -0
  50. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/metamodel/metamodel_puml.txt +0 -0
  51. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.mermaid +0 -0
  52. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.puml +0 -0
  53. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.py +0 -0
  54. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.sidif +0 -0
  55. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.xlsx +0 -0
  56. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.yaml +0 -0
  57. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events_puml.txt +0 -0
  58. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.mermaid +0 -0
  59. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.puml +0 -0
  60. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.py +0 -0
  61. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.sidif +0 -0
  62. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.yaml +0 -0
  63. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM_puml.txt +0 -0
  64. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.mermaid +0 -0
  65. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.puml +0 -0
  66. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.py +0 -0
  67. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.sidif +0 -0
  68. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.xlsx +0 -0
  69. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon.yaml +0 -0
  70. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/smwcon/smwcon_puml.txt +0 -0
  71. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.mermaid +0 -0
  72. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.puml +0 -0
  73. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.py +0 -0
  74. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.sidif +0 -0
  75. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.xlsx +0 -0
  76. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.yaml +0 -0
  77. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema_puml.txt +0 -0
  78. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.mermaid +0 -0
  79. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.puml +0 -0
  80. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.py +0 -0
  81. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.sidif +0 -0
  82. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.xlsx +0 -0
  83. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki.yaml +0 -0
  84. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/examples/wiki/wiki_puml.txt +0 -0
  85. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/metamodel_cmd.py +0 -0
  86. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/mw.py +0 -0
  87. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/profiler.py +0 -0
  88. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/sidif2linkml.py +0 -0
  89. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/smw_type.py +0 -0
  90. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/meta/uml.py +0 -0
  91. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/metamodel.puml +0 -0
  92. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/mkdocs.yml +0 -0
  93. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/pyproject.toml +0 -0
  94. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/blackisort +0 -0
  95. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/doc +0 -0
  96. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/genexamples +0 -0
  97. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/install +0 -0
  98. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/release +0 -0
  99. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/scripts/test +0 -0
  100. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/__init__.py +0 -0
  101. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/basemwtest.py +0 -0
  102. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/basesmwtest.py +0 -0
  103. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/basetest.py +0 -0
  104. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/test_generators.py +0 -0
  105. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/test_linkml.py +0 -0
  106. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/test_mw.py +0 -0
  107. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/test_plantuml.py +0 -0
  108. {pymetamodel-0.6.6 → pymetamodel-0.6.9}/tests/test_sidif.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyMetaModel
3
- Version: 0.6.6
3
+ Version: 0.6.9
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/pyMetaModel
6
6
  Project-URL: Documentation, https://wiki.bitplan.com/index.php/PyMetaModel
@@ -0,0 +1 @@
1
+ __version__ = "0.6.9"
@@ -26,18 +26,19 @@ class Context:
26
26
  A Context groups some topics like a Namespace/Package.
27
27
  This class provides helper functions and constants to render a Context to corresponding wiki pages
28
28
  """
29
+
29
30
  name: Optional[str] = None
30
- since: Optional[str] = None # isodate string
31
- updated: Optional[str] = None # isodate string
31
+ since: Optional[str] = None # isodate string
32
+ updated: Optional[str] = None # isodate string
32
33
  copyright: Optional[str] = None
33
34
  master: Optional[str] = None
34
35
  demo: Optional[str] = None
35
- topics: Dict[str, 'Topic'] = field(default_factory=dict)
36
- types: Dict[str, 'Topic'] = field(default_factory=dict)
36
+ topics: Dict[str, "Topic"] = field(default_factory=dict)
37
+ types: Dict[str, "Topic"] = field(default_factory=dict)
37
38
  errors: List[str] = field(default_factory=list)
38
39
 
39
40
  def __post_init__(self):
40
- self.log=Log()
41
+ self.log = Log()
41
42
  pass
42
43
 
43
44
  @classmethod
@@ -49,17 +50,17 @@ class Context:
49
50
  "updated": datetime.strptime("2024-08-07", "%Y-%m-%d"),
50
51
  "copyright": "2015-2025 BITPlan GmbH",
51
52
  "master": "https://contexts.bitplan.com",
52
- "demo": "https://wiki.bitplan.com/index.php/List_of_Contexts"
53
+ "demo": "https://wiki.bitplan.com/index.php/List_of_Contexts",
53
54
  }
54
55
  ]
55
56
  return samples
56
57
 
57
- def sanitize(self,key:str):
58
+ def sanitize(self, key: str):
58
59
  """
59
60
  make sure my needed properties exist
60
61
  """
61
- if not hasattr(self,"name"):
62
- self.name=key
62
+ if not hasattr(self, "name"):
63
+ self.name = key
63
64
  pass
64
65
  pass
65
66
 
@@ -82,7 +83,7 @@ class Context:
82
83
  return self.topics[topic_name]
83
84
  else:
84
85
  self.error(
85
- f"""topic {topic_name} not found in context {getattr(self,"name","?")} for {purpose}"""
86
+ f"""topic {topic_name} not found in context {getattr(self, "name", "?")} for {purpose}"""
86
87
  )
87
88
  return None
88
89
 
@@ -186,6 +187,7 @@ class Context:
186
187
  prop.topicLink = tl
187
188
  prop.isLink = True
188
189
  prop.showInGrid = False
190
+ prop.separator = tl.separator
189
191
  return prop
190
192
 
191
193
  @classmethod
@@ -225,8 +227,8 @@ class Context:
225
227
  prop.sanitize()
226
228
  context.addProperty(prop)
227
229
  elif isA == "SMW_Type":
228
- smw_type=SMW_Type.from_dict2(record) # @UndefinedVariable
229
- context.types[smw_type.type]=smw_type
230
+ smw_type = SMW_Type.from_dict2(record) # @UndefinedVariable
231
+ context.types[smw_type.type] = smw_type
230
232
  else: # isA == Topic or in declared topics
231
233
  topic = Topic.from_dict2(record)
232
234
  topic.sanitize()
@@ -238,14 +240,16 @@ class Context:
238
240
  context.error(f"topic {topic.name} has no defined context")
239
241
  if hasattr(topic, "name") and topic.name:
240
242
  context.topics[topic.name] = topic
241
- topic.context_obj=context
243
+ topic.context_obj = context
242
244
  else:
243
245
  # potential foreign or extends declaration
244
- context.error(f"missing name for topic {topic} {key} - foreign declaration?")
246
+ context.error(
247
+ f"missing name for topic {topic} {key} - foreign declaration?"
248
+ )
245
249
  except Exception as ex:
246
250
  if context:
247
- msg=f"invalid dict {record}: {str(ex)}"
248
- context.log.log("❌","dict",msg)
251
+ msg = f"invalid dict {record}: {str(ex)}"
252
+ context.log.log("❌", "dict", msg)
249
253
  else:
250
254
  raise ex
251
255
  # link topic to concepts and add topicLinks
@@ -258,7 +262,9 @@ class Context:
258
262
  return context
259
263
 
260
264
  @classmethod
261
- def fromSiDIF_input(cls, sidif_input: str, debug: bool = False)->Tuple['Context',str,str]:
265
+ def fromSiDIF_input(
266
+ cls, sidif_input: str, debug: bool = False
267
+ ) -> Tuple["Context", str, str]:
262
268
  """
263
269
  initialize me from the given SiDIF input which might be a file path
264
270
  or url
@@ -333,7 +339,7 @@ class Context:
333
339
  error = None
334
340
  errMsg = None
335
341
  sidif = None
336
- msg=f"reading sidif for {mw_context.context} from {mw_context.wikiId}"
342
+ msg = f"reading sidif for {mw_context.context} from {mw_context.wikiId}"
337
343
  if debug:
338
344
  print(msg)
339
345
  try:
@@ -346,17 +352,19 @@ class Context:
346
352
  sidif=sidif, title=mw_context.wikiId, depth=depth, debug=debug
347
353
  )
348
354
  else:
349
- errMsg=f"{msg} failed"
350
- error=ValueError(msg)
355
+ errMsg = f"{msg} failed"
356
+ error = ValueError(msg)
351
357
  return context, error, errMsg
352
358
 
359
+
353
360
  @lod_storable
354
361
  class Topic:
355
362
  """
356
363
  A Topic is a Concept/Class/Thing/Entity
357
364
  """
365
+
358
366
  name: Optional[str] = None
359
- _pluralName: Optional[str] = None
367
+ pluralName: Optional[str] = None
360
368
  icon: Optional[str] = None
361
369
  iconUrl: Optional[str] = None
362
370
  documentation: Optional[str] = None
@@ -364,18 +372,24 @@ class Topic:
364
372
  defaultstoremode: str = "property"
365
373
  extends: Optional[str] = None
366
374
  listLimit: int = 200
367
- cargo:bool=False
368
- headerTabs:bool=False
375
+ cargo: bool = False
376
+ headerTabs: bool = False
369
377
  context: Optional[str] = None
370
- properties: Dict[str, 'Property'] = field(default_factory=dict)
371
- sourceTopicLinks: Dict[str, 'TopicLink'] = field(default_factory=dict)
372
- targetTopicLinks: Dict[str, 'TopicLink'] = field(default_factory=dict)
378
+ properties: Dict[str, "Property"] = field(default_factory=dict)
379
+ sourceTopicLinks: Dict[str, "TopicLink"] = field(default_factory=dict)
380
+ targetTopicLinks: Dict[str, "TopicLink"] = field(default_factory=dict)
373
381
  # object references
374
382
  context_obj: Optional[Context] = None
375
- conceptProperty: Optional['Property'] = None
383
+ conceptProperty: Optional["Property"] = None
376
384
 
385
+ def get_primary_key_property(self) -> Optional["Property"]:
386
+ """Return the first property marked as primaryKey, or None."""
387
+ return next(
388
+ (p for p in self.properties.values() if getattr(p, "primaryKey", False)),
389
+ None,
390
+ )
377
391
 
378
- def get_extends_topics(self, l: List['Topic'] = None) -> List['Topic']:
392
+ def get_extends_topics(self, l: List["Topic"] = None) -> List["Topic"]:
379
393
  """
380
394
  Get the topics this topic is extending.
381
395
 
@@ -399,7 +413,7 @@ class Topic:
399
413
 
400
414
  return l
401
415
 
402
- def get_all_properties(self) -> List['Property']:
416
+ def get_all_properties(self) -> List["Property"]:
403
417
  """
404
418
  Get all properties of the topic, including those inherited from extended topics.
405
419
 
@@ -419,18 +433,18 @@ class Topic:
419
433
 
420
434
  return all_properties
421
435
 
422
-
423
436
  def sanitize(self):
424
437
  """
425
438
  make sure my properties exist note that name is not handled here
426
439
  on purpose since it needs special treatment
427
440
  """
428
- doc=self.documentation if hasattr(self, "documentation") else "?"
441
+ doc = self.documentation if hasattr(self, "documentation") else "?"
429
442
  if not hasattr(self, "wikiDocumentation") or not self.wikiDocumentation:
430
- self.wikiDocumentation=doc
431
- if not hasattr(self,"defaultstoremode"):
432
- self.defaultstoremode="property"
433
- pass
443
+ self.wikiDocumentation = doc
444
+ if not hasattr(self, "defaultstoremode"):
445
+ self.defaultstoremode = "property"
446
+ if not self.pluralName:
447
+ self.pluralName = f"{self.name}s"
434
448
 
435
449
  @classmethod
436
450
  def getSamples(cls):
@@ -455,7 +469,7 @@ class Topic:
455
469
  set my concept property to any primary key or mandatory property
456
470
  """
457
471
  self.conceptProperty = None
458
- all_properties=self.propertiesByIndex(to_root=True)
472
+ all_properties = self.propertiesByIndex(to_root=True)
459
473
  for prop in all_properties:
460
474
  mandatory = False
461
475
  primaryKey = False
@@ -467,27 +481,6 @@ class Topic:
467
481
  self.conceptProperty = prop
468
482
  break
469
483
 
470
- @property
471
- def pluralName(self)->str:
472
- """
473
- Getter for pluralName.
474
-
475
- Returns:
476
- str: The plural name of the topic.
477
- """
478
- # Default behavior if _pluralName is not explicitly set
479
- return self._pluralName if self._pluralName is not None else f"{self.name}s"
480
-
481
- @pluralName.setter
482
- def pluralName(self, value):
483
- """
484
- Setter for pluralName.
485
-
486
- Args:
487
- value (str): The plural name to be set for the topic.
488
- """
489
- self._pluralName = value
490
-
491
484
  def getPluralName(self) -> str:
492
485
  """
493
486
  get the plural name for this topic
@@ -513,7 +506,7 @@ class Topic:
513
506
  list: a list of properties in sort order
514
507
  """
515
508
  prop_list = []
516
- all_properties=self.get_all_properties()
509
+ all_properties = self.get_all_properties()
517
510
  for prop in all_properties:
518
511
  if hasattr(prop, "sortPos"):
519
512
  sortPos = prop.sortPos
@@ -534,13 +527,12 @@ class Topic:
534
527
  inheritance_chain = self.get_extends_topics()
535
528
  inheritance_chain.append(self)
536
529
  for topic in inheritance_chain:
537
- sorted_props=topic.propertiesByIndex(to_root=False)
530
+ sorted_props = topic.propertiesByIndex(to_root=False)
538
531
  properties_dict[topic.name] = sorted_props
539
532
 
540
533
  return properties_dict
541
534
 
542
-
543
- def propertiesByIndex(self,to_root:bool=False) -> list:
535
+ def propertiesByIndex(self, to_root: bool = False) -> list:
544
536
  """
545
537
  Return the properties by index, considering inheritance chain.
546
538
 
@@ -564,7 +556,6 @@ class Topic:
564
556
 
565
557
  return sorted_props
566
558
 
567
-
568
559
  def askSort(self) -> str:
569
560
  """
570
561
  generate the sort clause for my SMW ask query
@@ -594,7 +585,7 @@ class Topic:
594
585
  mainlabel: str = None,
595
586
  filterShowInGrid: bool = True,
596
587
  listLimit: int = None,
597
- result_format: str="table"
588
+ result_format: str = "table",
598
589
  ) -> str:
599
590
  """
600
591
  Get the askQuery for the topic, considering inheritance.
@@ -617,7 +608,7 @@ class Topic:
617
608
  for topic_name, properties in properties_dict.items():
618
609
  for prop in properties:
619
610
  if filterShowInGrid:
620
- show=getattr(prop, "showInGrid",False)
611
+ show = getattr(prop, "showInGrid", False)
621
612
  else:
622
613
  show = True
623
614
  if show:
@@ -628,11 +619,13 @@ class Topic:
628
619
  markup += f"""{self.askSort()}}}}}"""
629
620
  return markup
630
621
 
622
+
631
623
  @lod_storable
632
624
  class Property:
633
625
  """
634
626
  Provides helper functions and constants for properties
635
627
  """
628
+
636
629
  name: Optional[str] = None
637
630
  label: Optional[str] = None
638
631
  type: Optional[str] = None
@@ -645,18 +638,18 @@ class Property:
645
638
  uploadable: bool = False
646
639
  defaultValue: Optional[str] = None
647
640
  inputType: Optional[str] = None
648
- allowedValues: Optional[str]=None
641
+ allowedValues: Optional[str] = None
649
642
  documentation: Optional[str] = None
650
- values_from: Optional[str]=None
651
- formatterURI: Optional[str] = None # externalFormatterURI
643
+ values_from: Optional[str] = None
644
+ formatterURI: Optional[str] = None # externalFormatterURI
652
645
  showInGrid: bool = True
653
646
  isLink: bool = False
654
647
  nullable: bool = False
655
648
  sortAscending: bool = True
649
+ separator: Optional[str] = None
656
650
  # Links
657
651
  topic: Optional[str] = None
658
- topicLink: Optional['TopicLink'] = None
659
-
652
+ topicLink: Optional["TopicLink"] = None
660
653
 
661
654
  @classmethod
662
655
  def getSamples(cls):
@@ -698,23 +691,23 @@ class Property:
698
691
  ]
699
692
  return samples
700
693
 
701
-
702
694
  def sanitize(self):
703
695
  """
704
696
  make sure attributes such as namespace are set properly
705
697
  """
706
- self.namespace="Property"
707
- self.topicLink=None
698
+ self.namespace = "Property"
699
+ self.topicLink = None
708
700
  if hasattr(self, "scope"):
709
701
  pass
710
702
  if not hasattr(self, "showInGrid"):
711
- self.showInGrid=True
703
+ self.showInGrid = True
712
704
  if not hasattr(self, "label"):
713
- self.label=getattr(self,"name")
705
+ self.label = getattr(self, "name")
714
706
  if not hasattr(self, "type") or not self.type:
715
- self.type="Text"
707
+ self.type = "Text"
716
708
  pass
717
709
 
710
+
718
711
  @lod_storable
719
712
  class TopicLink:
720
713
  """
@@ -16,7 +16,7 @@ class Version(object):
16
16
  description = "pyMetaModel: MetaModel for Knowledge Graphs"
17
17
  version = meta.__version__
18
18
  date = "2022-11-30"
19
- updated = "2025-09-19"
19
+ updated = "2026-07-13"
20
20
  authors = "Wolfgang Fahl"
21
21
  doc_url = "https://wiki.bitplan.com/index.php/pyMetaModel"
22
22
  chat_url = "https://github.com/WolfgangFahl/pyMetaModel/discussions"
@@ -0,0 +1,81 @@
1
+ """
2
+ Created on 2023-02-25
3
+
4
+ @author: wf
5
+ """
6
+
7
+ from meta.smw_type import SMW_Type
8
+ from tests.basemwtest import BaseMediawikiTest
9
+ from meta.metamodel import Property, Topic
10
+ from tests.basetest import Basetest
11
+
12
+
13
+ class TestMetaModel(BaseMediawikiTest):
14
+ """
15
+ test round trip meta model handling
16
+ """
17
+
18
+ def setUp(self, debug=False, profile=True):
19
+ """
20
+ setUp
21
+ """
22
+ BaseMediawikiTest.setUp(self, debug=debug, profile=profile)
23
+ for wikiId in ["wiki"]:
24
+ self.getSMWAccess(wikiId, save=True)
25
+
26
+ def testSMW_Type(self):
27
+ """
28
+ test SMW_Type handling
29
+ """
30
+ debug = self.debug
31
+ smwAccess = self.smwAccessMap["wiki"]
32
+ ask_query = SMW_Type.askQuery()
33
+ did = smwAccess.smw.query(ask_query)
34
+ for record in did.values():
35
+ smw_type = SMW_Type.from_dict(record) # @UndefinedVariable
36
+ if debug:
37
+ print(smw_type)
38
+ self.assertIsInstance(smw_type, SMW_Type)
39
+
40
+
41
+ class TestTopic(Basetest):
42
+ """
43
+ test Topic methods - see https://github.com/WolfgangFahl/pyMetaModel/issues/36
44
+ """
45
+
46
+ def test_get_primary_key_property(self):
47
+ """test get_primary_key_property returns the primaryKey property or None"""
48
+ topic = Topic()
49
+ topic.properties = {
50
+ "name": Property(name="name", primaryKey=True),
51
+ "at": Property(name="at"),
52
+ }
53
+ pk = topic.get_primary_key_property()
54
+ self.assertIsNotNone(pk)
55
+ self.assertEqual(pk.name, "name")
56
+
57
+ def test_get_primary_key_property_none(self):
58
+ """test get_primary_key_property returns None when no primaryKey exists"""
59
+ topic = Topic()
60
+ topic.properties = {"at": Property(name="at")}
61
+ self.assertIsNone(topic.get_primary_key_property())
62
+
63
+ def test_pluralName_from_wiki(self):
64
+ """test that pluralName loaded from SiDIF is used, not the default fallback
65
+ see https://github.com/WolfgangFahl/pyMetaModel/issues/37
66
+ """
67
+ topic = Topic()
68
+ topic.name = "Property"
69
+ topic.pluralName = "Properties"
70
+ pluralName = topic.getPluralName()
71
+ self.assertEqual(pluralName, "Properties")
72
+
73
+ def test_pluralName_default_fallback(self):
74
+ """test that getPluralName falls back to name+s when pluralName not set
75
+ see https://github.com/WolfgangFahl/pyMetaModel/issues/37
76
+ """
77
+ topic = Topic()
78
+ topic.name = "Topic"
79
+ topic.sanitize()
80
+ pluralName = topic.getPluralName()
81
+ self.assertEqual(pluralName, "Topics")
@@ -1 +0,0 @@
1
- __version__ = "0.6.6"
@@ -1,36 +0,0 @@
1
- """
2
- Created on 2023-02-25
3
-
4
- @author: wf
5
- """
6
-
7
- from meta.smw_type import SMW_Type
8
- from tests.basemwtest import BaseMediawikiTest
9
-
10
-
11
- class TestMetaModel(BaseMediawikiTest):
12
- """
13
- test round trip meta model handling
14
- """
15
-
16
- def setUp(self, debug=False, profile=True):
17
- """
18
- setUp
19
- """
20
- BaseMediawikiTest.setUp(self, debug=debug, profile=profile)
21
- for wikiId in ["wiki"]:
22
- self.getSMWAccess(wikiId, save=True)
23
-
24
- def testSMW_Type(self):
25
- """
26
- test SMW_Type handling
27
- """
28
- debug = self.debug
29
- smwAccess = self.smwAccessMap["wiki"]
30
- ask_query = SMW_Type.askQuery()
31
- did = smwAccess.smw.query(ask_query)
32
- for record in did.values():
33
- smw_type = SMW_Type.from_dict(record) # @UndefinedVariable
34
- if debug:
35
- print(smw_type)
36
- self.assertIsInstance(smw_type, SMW_Type)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes