deriva-cli 0.8.1__tar.gz → 0.8.3__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 (202) hide show
  1. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/PKG-INFO +6 -7
  2. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/manager.py +29 -0
  3. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/models.py +28 -14
  4. deriva_cli-0.8.3/deriva/adapters/archimate/views.py +154 -0
  5. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/xml_export.py +107 -10
  6. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/__init__.py +7 -0
  7. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/data/derivation_config.json +126 -86
  8. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/data/extraction_config.json +106 -4
  9. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/data/file_types.json +8 -0
  10. deriva_cli-0.8.3/deriva/adapters/database/data/function_models/pcf-8.0-cross-industry.csv +90 -0
  11. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/manager.py +19 -1
  12. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/__init__.py +20 -0
  13. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/manager.py +58 -2
  14. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/models.py +340 -5
  15. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/__init__.py +0 -4
  16. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/manager.py +21 -10
  17. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/model_registry.py +11 -5
  18. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/models.py +4 -0
  19. deriva_cli-0.8.3/deriva/adapters/llm/retry.py +147 -0
  20. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/schemas.py +34 -0
  21. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/repository/manager.py +5 -20
  22. deriva_cli-0.8.3/deriva/adapters/treesitter/cdk.py +312 -0
  23. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/__init__.py +1 -0
  24. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/javascript.py +21 -15
  25. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/python.py +16 -15
  26. deriva_cli-0.8.3/deriva/adapters/treesitter/languages/sql.py +98 -0
  27. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/manager.py +4 -1
  28. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/cli.py +16 -1
  29. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/commands/config.py +56 -17
  30. deriva_cli-0.8.3/deriva/cli/commands/graph.py +27 -0
  31. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/commands/repo.py +1 -1
  32. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/commands/run.py +10 -0
  33. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/cache_utils.py +201 -36
  34. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/logging.py +6 -63
  35. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/settings.py +4 -3
  36. deriva_cli-0.8.3/deriva/modules/analysis/conventions.py +302 -0
  37. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/semantic_matching.py +29 -0
  38. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/application_service.py +3 -21
  39. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/base.py +318 -39
  40. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/business_actor.py +3 -21
  41. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/business_object.py +3 -23
  42. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/business_process.py +3 -22
  43. deriva_cli-0.8.3/deriva/modules/derivation/component_services.py +835 -0
  44. deriva_cli-0.8.3/deriva/modules/derivation/detail_level.py +85 -0
  45. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/element_base.py +245 -26
  46. deriva_cli-0.8.3/deriva/modules/derivation/function_model.py +415 -0
  47. deriva_cli-0.8.3/deriva/modules/derivation/levels.py +224 -0
  48. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/__init__.py +6 -8
  49. deriva_cli-0.8.3/deriva/modules/derivation/refine/abstraction_levels.py +261 -0
  50. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/base.py +2 -0
  51. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/duplicate_elements.py +27 -107
  52. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/duplicate_relationships.py +18 -38
  53. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/joint.py +111 -45
  54. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/joint_consistency.py +27 -52
  55. deriva_cli-0.8.3/deriva/modules/extraction/build_modules.py +254 -0
  56. deriva_cli-0.8.3/deriva/modules/extraction/cooperation.py +242 -0
  57. deriva_cli-0.8.3/deriva/modules/extraction/data_domains.py +359 -0
  58. deriva_cli-0.8.3/deriva/modules/extraction/data_entities.py +277 -0
  59. deriva_cli-0.8.3/deriva/modules/extraction/deployable_units.py +508 -0
  60. deriva_cli-0.8.3/deriva/modules/extraction/deployments.py +671 -0
  61. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/edges.py +137 -45
  62. deriva_cli-0.8.3/deriva/modules/extraction/lifecycles.py +395 -0
  63. deriva_cli-0.8.3/deriva/modules/extraction/roles.py +91 -0
  64. deriva_cli-0.8.3/deriva/modules/extraction/step_tags.py +139 -0
  65. deriva_cli-0.8.3/deriva/modules/extraction/technologies.py +63 -0
  66. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/type_definition.py +2 -0
  67. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/__init__.py +3 -0
  68. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/about.py +10 -0
  69. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/config.py +48 -22
  70. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/config_models.py +2 -2
  71. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/derivation.py +568 -63
  72. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/export_bundle.py +5 -0
  73. deriva_cli-0.8.3/deriva/services/export_levels.py +117 -0
  74. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/extraction.py +1070 -18
  75. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/llm_log.py +2 -0
  76. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/model_configs.py +12 -1
  77. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/session.py +61 -9
  78. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/pyproject.toml +11 -9
  79. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/pyproject.toml.orig +10 -11
  80. deriva_cli-0.8.1/deriva/adapters/llm/retry.py +0 -421
  81. deriva_cli-0.8.1/deriva/modules/derivation/component_services.py +0 -265
  82. deriva_cli-0.8.1/deriva/modules/derivation/refine/circular_relationships.py +0 -251
  83. deriva_cli-0.8.1/deriva/modules/derivation/refine/relationship_consolidation.py +0 -299
  84. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/LICENSE +0 -0
  85. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/README.md +0 -0
  86. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/__init__.py +0 -0
  87. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/documentation/Diagram.html +0 -0
  88. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/documentation/View.html +0 -0
  89. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/documentation/model.html +0 -0
  90. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Diagram.xsd +0 -0
  91. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Model.xsd +0 -0
  92. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_View.xsd +0 -0
  93. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Diagram.xsd +0 -0
  94. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Model.xsd +0 -0
  95. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_View.xsd +0 -0
  96. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-1.xml +0 -0
  97. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-10.xml +0 -0
  98. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-2.xml +0 -0
  99. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-3.xml +0 -0
  100. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-4.xml +0 -0
  101. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-5.xml +0 -0
  102. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-7.xml +0 -0
  103. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-8.xml +0 -0
  104. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-9.xml +0 -0
  105. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/archimate/validation.py +0 -0
  106. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/README.md +0 -0
  107. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/data/derivation_patterns.json +0 -0
  108. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/data/system_settings.json +0 -0
  109. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/db_tool.py +0 -0
  110. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/scripts/7_extraction_params.sql +0 -0
  111. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/database/scripts/schema.sql +0 -0
  112. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/grafeo/__init__.py +0 -0
  113. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/grafeo/manager.py +0 -0
  114. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/README.md +0 -0
  115. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/cache.py +0 -0
  116. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/graph/metamodel.py +0 -0
  117. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/README.md +0 -0
  118. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/cache.py +0 -0
  119. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/llm/rate_limiter.py +0 -0
  120. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/__init__.py +0 -0
  121. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/extract.py +0 -0
  122. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/language.py +0 -0
  123. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/manager.py +0 -0
  124. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/models.py +0 -0
  125. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/phrases.py +0 -0
  126. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/settings.py +0 -0
  127. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/text.py +0 -0
  128. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/nlp/translate.py +0 -0
  129. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/repository/README.md +0 -0
  130. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/repository/__init__.py +0 -0
  131. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/repository/models.py +0 -0
  132. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/__init__.py +0 -0
  133. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/base.py +0 -0
  134. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/bms.py +0 -0
  135. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/cobol.py +0 -0
  136. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/csharp.py +0 -0
  137. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/java.py +0 -0
  138. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/jcl.py +0 -0
  139. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/languages/structural.py +0 -0
  140. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/adapters/treesitter/models.py +0 -0
  141. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/README.md +0 -0
  142. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/__init__.py +0 -0
  143. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/commands/__init__.py +0 -0
  144. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/cli/progress.py +0 -0
  145. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/__init__.py +0 -0
  146. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/chunking.py +0 -0
  147. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/document_reader.py +0 -0
  148. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/exceptions.py +0 -0
  149. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/file_utils.py +0 -0
  150. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/json_utils.py +0 -0
  151. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/llm_utils.py +0 -0
  152. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/naming.py +0 -0
  153. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/ocel.py +0 -0
  154. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/paths.py +0 -0
  155. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/schema_utils.py +0 -0
  156. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/time_utils.py +0 -0
  157. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/timing.py +0 -0
  158. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/common/types.py +0 -0
  159. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/__init__.py +0 -0
  160. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/__init__.py +0 -0
  161. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/consistency.py +0 -0
  162. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/cross_repo_analysis.py +0 -0
  163. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/deviation.py +0 -0
  164. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/fit_analysis.py +0 -0
  165. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/model_quality.py +0 -0
  166. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/run_consistency.py +0 -0
  167. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/stability_analysis.py +0 -0
  168. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/analysis/types.py +0 -0
  169. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/__init__.py +0 -0
  170. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/application_component.py +0 -0
  171. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/application_interface.py +0 -0
  172. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/business_event.py +0 -0
  173. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/business_function.py +0 -0
  174. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/data_object.py +0 -0
  175. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/device.py +0 -0
  176. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/node.py +0 -0
  177. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/prep.py +0 -0
  178. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/cross_layer.py +0 -0
  179. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/graph_relationships.py +0 -0
  180. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/normalization.py +0 -0
  181. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/orphan_elements.py +0 -0
  182. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/refine/structural_consistency.py +0 -0
  183. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/system_software.py +0 -0
  184. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/derivation/technology_service.py +0 -0
  185. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/__init__.py +0 -0
  186. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/base.py +0 -0
  187. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/business_concept.py +0 -0
  188. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/classification.py +0 -0
  189. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/concept_candidates.py +0 -0
  190. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/directory.py +0 -0
  191. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/directory_classification.py +0 -0
  192. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/external_dependency.py +0 -0
  193. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/file.py +0 -0
  194. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/method.py +0 -0
  195. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/repository.py +0 -0
  196. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/technology_candidates.py +0 -0
  197. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/modules/extraction/test.py +0 -0
  198. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/ontology.py +0 -0
  199. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/overfit.py +0 -0
  200. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/pipeline.py +0 -0
  201. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/deriva/services/trace.py +0 -0
  202. {deriva_cli-0.8.1 → deriva_cli-0.8.3}/packages/README-deriva-cli.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deriva-cli
3
- Version: 0.8.1
3
+ Version: 0.8.3
4
4
  Summary: Deriva is a research project aimed at enabling digital architects by automating the derivation of enterprise architecture models. Deriva uses dual knowledge graphs, heuristics and LLM's.
5
5
  Keywords: archimate,enterprise-architecture,code-analysis,llm,grafeo,fastapi
6
6
  Author: S. T. Grond
@@ -35,15 +35,14 @@ Requires-Dist: tree-sitter-c-sharp>=0.23
35
35
  Requires-Dist: pypdf>=6
36
36
  Requires-Dist: python-docx>=1.2
37
37
  Requires-Dist: spacy==3.8.16
38
- Requires-Dist: ctranslate2==4.8.2
38
+ Requires-Dist: ctranslate2==4.8.3
39
39
  Requires-Dist: sentencepiece==0.2.2
40
40
  Requires-Dist: solvor>=0.6.3
41
- Requires-Dist: backoff>=2.2.1
42
- Requires-Dist: pydantic-ai-slim[openai,anthropic,mistral]>=2.55
43
- Requires-Dist: diskcache>=5.6.3
44
- Requires-Dist: structlog>=26.1.0
41
+ Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.55
42
+ Requires-Dist: httpx2>=2.13
43
+ Requires-Dist: lmdb>=1.6
45
44
  Requires-Dist: typer>=0.27
46
- Requires-Dist: httpx>=0.28.1
45
+ Requires-Dist: tree-sitter-typescript==0.23.2
47
46
  Requires-Python: >=3.14
48
47
  Project-URL: Homepage, https://deriva.dev
49
48
  Project-URL: Documentation, https://docs.deriva.dev
@@ -577,6 +577,35 @@ class ArchimateManager:
577
577
  key=lambda e: str(e["identifier"]),
578
578
  )
579
579
 
580
+ def update_element_properties(self, updates: dict[str, dict[str, Any]]) -> int:
581
+ """Merge keys into the properties of elements (identifier -> keys), leaving their other keys; returns how many changed."""
582
+ if self.db is None:
583
+ raise RuntimeError("Not connected to grafeo. Call connect() first.")
584
+ changed = 0
585
+ for identifier, keys in sorted(updates.items()):
586
+ rows = self.db.execute(f"MATCH (e:`{self.namespace}` {{identifier: $identifier}}) RETURN e.properties AS properties", {"identifier": identifier})
587
+ if not rows:
588
+ continue
589
+ merged = {**(rows[0].get("properties") or {}), **keys}
590
+ self.db.execute_write(f"MATCH (e:`{self.namespace}` {{identifier: $identifier}}) SET e.properties = $properties", {"identifier": identifier, "properties": merged})
591
+ changed += 1
592
+ return changed
593
+
594
+ def update_relationship_properties(self, updates: dict[str, dict[str, Any]]) -> int:
595
+ """Merge keys into the properties of relationships (identifier -> keys), in place; returns how many changed."""
596
+ if self.db is None:
597
+ raise RuntimeError("Not connected to grafeo. Call connect() first.")
598
+ changed = 0
599
+ for identifier, keys in sorted(updates.items()):
600
+ match = f"MATCH (:`{self.namespace}`)-[r {{identifier: $identifier}}]->(:`{self.namespace}`)"
601
+ rows = self.db.execute(f"{match} RETURN r.properties AS properties", {"identifier": identifier})
602
+ if not rows:
603
+ continue
604
+ merged = {**(rows[0].get("properties") or {}), **keys}
605
+ self.db.execute_write(f"{match} SET r.properties = $properties", {"identifier": identifier, "properties": merged})
606
+ changed += 1
607
+ return changed
608
+
580
609
  def delete_relationship(self, identifier: str) -> bool:
581
610
  """Delete a relationship by identifier.
582
611
 
@@ -122,6 +122,13 @@ ELEMENT_TYPES: dict[str, ElementType] = {
122
122
  aspect="Behavior",
123
123
  description="An explicitly defined exposed technology behavior",
124
124
  ),
125
+ # A detail-level element (the export's detail level): the deployed image or package of a component
126
+ "Artifact": ElementType(
127
+ name="Artifact",
128
+ layer="Technology",
129
+ aspect="Passive",
130
+ description="A piece of data that is used or produced in a software development process, or by deployment and operation of an IT system",
131
+ ),
125
132
  }
126
133
 
127
134
 
@@ -152,6 +159,7 @@ BEHAVIOR_ELEMENTS: set[str] = {
152
159
  PASSIVE_ELEMENTS: set[str] = {
153
160
  "DataObject",
154
161
  "BusinessObject",
162
+ "Artifact",
155
163
  }
156
164
 
157
165
  # All elements
@@ -178,6 +186,7 @@ TECHNOLOGY_LAYER: set[str] = {
178
186
  "Device",
179
187
  "SystemSoftware",
180
188
  "TechnologyService",
189
+ "Artifact",
181
190
  }
182
191
 
183
192
 
@@ -196,7 +205,7 @@ def layer_of(element_type: str) -> str:
196
205
  # ArchiMate 3.2 Relationship Table
197
206
  # =============================================================================
198
207
  # Source: ArchiMate 3.2 Specification, Appendix B.5 "Relationship Tables", restricted
199
- # to the 13 element types Deriva derives. The spec publishes the tables as images;
208
+ # to the 13 element types Deriva derives plus Artifact (the export's detail level). The spec publishes the tables as images;
200
209
  # letters were read from them and cross-checked (case-insensitively, all 169 cells)
201
210
  # against Archi's relationships.xml v3.2.
202
211
  #
@@ -233,24 +242,29 @@ _TABLE_COLUMNS = (
233
242
  "Device",
234
243
  "SystemSoftware",
235
244
  "TechnologyService",
245
+ "Artifact",
236
246
  )
237
247
 
248
+ # Artifact (row and column) from Archi's relationships.xml; uppercase where the spec's metamodel figures show the
249
+ # relationship: Node, Device and SystemSoftware are assigned to an artifact, an artifact realizes an application
250
+ # component, a data object or system software; artifacts compose, aggregate and specialize artifacts.
238
251
  # Row = source, columns in _TABLE_COLUMNS order = target.
239
252
  # fmt: off
240
253
  _TABLE_ROWS: dict[str, tuple[str, ...]] = {
241
- "BusinessActor": ("SCGvtfO", "IvtfO", "IvtfO", "IvtfO", "aO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO"),
242
- "BusinessProcess": ("vtfO", "SCGvTFO", "CGvTFO", "vTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO"),
243
- "BusinessFunction": ("vtfO", "CGvTFO", "SCGvTFO", "vTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO"),
244
- "BusinessEvent": ("vtfO", "vTFO", "vTFO", "SCGvTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO"),
245
- "BusinessObject": ("O", "O", "O", "O", "SCGO", "O", "O", "O", "O", "O", "O", "O", "O"),
246
- "ApplicationComponent": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "SCGRvtfO", "CgrvtfO", "irvtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO"),
247
- "ApplicationInterface": ("VtfO", "vtfO", "vtfO", "vtfO", "aO", "VtfO", "SCGvtfO", "IvtfO", "aO", "VtfO", "VtfO", "VtfO", "vtfO"),
248
- "ApplicationService": ("VtfO", "VtfO", "VtfO", "vtfO", "aO", "VtfO", "vtfO", "SCGvTFO", "AO", "VtfO", "VtfO", "VtfO", "vtfO"),
249
- "DataObject": ("O", "O", "O", "O", "RO", "O", "O", "O", "SCGO", "O", "O", "O", "O"),
250
- "Node": ("ivtfO", "irvtfO", "irvtfO", "irvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "SCGivtfO", "CGirvtfO", "CGirvtfO", "irvtfO"),
251
- "Device": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "vtfO", "SCGvtfO", "CGIrvtfO", "irvtfO"),
252
- "SystemSoftware": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "vtfO", "vtfO", "SCGIrvtfO", "irvtfO"),
253
- "TechnologyService": ("VtfO", "VtfO", "VtfO", "vtfO", "aO", "VtfO", "vtfO", "RvtfO", "aO", "VtfO", "VtfO", "VtfO", "SCGvTFO"),
254
+ "BusinessActor": ("SCGvtfO", "IvtfO", "IvtfO", "IvtfO", "aO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO", "aO"),
255
+ "BusinessProcess": ("vtfO", "SCGvTFO", "CGvTFO", "vTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO", "aO"),
256
+ "BusinessFunction": ("vtfO", "CGvTFO", "SCGvTFO", "vTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO", "aO"),
257
+ "BusinessEvent": ("vtfO", "vTFO", "vTFO", "SCGvTFO", "AO", "vtfO", "vtfO", "vtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO", "aO"),
258
+ "BusinessObject": ("O", "O", "O", "O", "SCGO", "O", "O", "O", "O", "O", "O", "O", "O", "O"),
259
+ "ApplicationComponent": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "SCGRvtfO", "CgrvtfO", "irvtfO", "aO", "vtfO", "vtfO", "vtfO", "vtfO", "aO"),
260
+ "ApplicationInterface": ("VtfO", "vtfO", "vtfO", "vtfO", "aO", "VtfO", "SCGvtfO", "IvtfO", "aO", "VtfO", "VtfO", "VtfO", "vtfO", "aO"),
261
+ "ApplicationService": ("VtfO", "VtfO", "VtfO", "vtfO", "aO", "VtfO", "vtfO", "SCGvTFO", "AO", "VtfO", "VtfO", "VtfO", "vtfO", "aO"),
262
+ "DataObject": ("O", "O", "O", "O", "RO", "O", "O", "O", "SCGO", "O", "O", "O", "O", "O"),
263
+ "Node": ("ivtfO", "irvtfO", "irvtfO", "irvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "SCGivtfO", "CGirvtfO", "CGirvtfO", "irvtfO", "aIO"),
264
+ "Device": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "vtfO", "SCGvtfO", "CGIrvtfO", "irvtfO", "aIO"),
265
+ "SystemSoftware": ("vtfO", "rvtfO", "rvtfO", "rvtfO", "aO", "rvtfO", "rvtfO", "rvtfO", "aO", "vtfO", "vtfO", "SCGIrvtfO", "irvtfO", "aIO"),
266
+ "TechnologyService": ("VtfO", "VtfO", "VtfO", "vtfO", "aO", "VtfO", "vtfO", "RvtfO", "aO", "VtfO", "VtfO", "VtfO", "SCGvTFO", "aO"),
267
+ "Artifact": ("O", "rO", "rO", "rO", "rO", "RO", "rO", "rO", "RO", "O", "O", "RO", "rO", "SCGrO"),
254
268
  }
255
269
  # fmt: on
256
270
 
@@ -0,0 +1,154 @@
1
+ """Generated views (pure): ArchiMate viewpoints per level with a deterministic band layout.
2
+
3
+ Each viewpoint (ArchiMate 3.2 Specification, chapter 13) shows the elements of its types on its level (enterprise or
4
+ solution, from the element's ``levels``) and the relationships on that level; a cross-level link is in no view. A
5
+ detail variant shows the solution elements of its types with their detail elements (property ``level`` = detail). The layout puts the
6
+ layers in horizontal bands (business, application, technology) and the elements of a band in a grid, ordered by
7
+ type and name, each detail element right after its parent, so the same model gives the same view in every run.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+
14
+ from .models import Element, Relationship, layer_of
15
+
16
+ ENTERPRISE, SOLUTION, DETAIL = "enterprise", "solution", "detail"
17
+ DEPLOYMENT = frozenset({"ApplicationComponent", "Node", "Device", "SystemSoftware", "TechnologyService"})
18
+ # (view name, viewpoint, element types, level), in export order
19
+ VIEWPOINTS: tuple[tuple[str, str, frozenset[str] | None, str], ...] = (
20
+ ("Layered (enterprise)", "Layered", None, ENTERPRISE),
21
+ ("Application Cooperation (enterprise)", "Application Cooperation", frozenset({"ApplicationComponent", "ApplicationInterface", "ApplicationService"}), ENTERPRISE),
22
+ ("Information Structure (enterprise)", "Information Structure", frozenset({"BusinessObject", "DataObject"}), ENTERPRISE),
23
+ ("Implementation and Deployment (enterprise)", "Implementation and Deployment", DEPLOYMENT, ENTERPRISE),
24
+ (
25
+ "Business (enterprise)",
26
+ "Business Process Cooperation",
27
+ frozenset({"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent", "ApplicationService"}),
28
+ ENTERPRISE,
29
+ ),
30
+ ("Application Structure (solution)", "Application Structure", frozenset({"ApplicationComponent"}), SOLUTION),
31
+ ("Application Cooperation (solution)", "Application Cooperation", frozenset({"ApplicationComponent", "ApplicationInterface"}), SOLUTION),
32
+ ("Information Structure (solution)", "Information Structure", frozenset({"DataObject"}), SOLUTION),
33
+ ("Implementation and Deployment (solution)", "Implementation and Deployment", DEPLOYMENT, SOLUTION),
34
+ ("Information Structure (detail)", "Information Structure", frozenset({"DataObject"}), DETAIL),
35
+ ("Implementation and Deployment (detail)", "Implementation and Deployment", DEPLOYMENT | {"Artifact"}, DETAIL),
36
+ # The interfaces with their members (endpoints, pages, jobs); components are left out, their members are above
37
+ ("Application Interfaces (detail)", "Application Cooperation", frozenset({"ApplicationInterface"}), DETAIL),
38
+ )
39
+
40
+ # A view needs an element of its anchor types (the Business view: a business element, not application services alone)
41
+ ANCHORS = {"Business (enterprise)": frozenset({"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent"})}
42
+ BANDS = ("Business", "Application", "Technology", "Other")
43
+ TYPE_ORDER = (
44
+ "BusinessActor", "BusinessFunction", "BusinessProcess", "BusinessEvent", "BusinessObject",
45
+ "ApplicationComponent", "ApplicationInterface", "ApplicationService", "DataObject",
46
+ "Node", "Device", "SystemSoftware", "TechnologyService", "Artifact",
47
+ ) # fmt: skip
48
+ COLUMNS, WIDTH, HEIGHT, GAP, BAND_GAP, MARGIN = 6, 160, 60, 20, 60, 20
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class ViewNode:
53
+ identifier: str
54
+ element: str
55
+ x: int
56
+ y: int
57
+ w: int = WIDTH
58
+ h: int = HEIGHT
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class ViewConnection:
63
+ identifier: str
64
+ relationship: str
65
+ source: str # node identifiers
66
+ target: str
67
+
68
+
69
+ @dataclass
70
+ class View:
71
+ identifier: str
72
+ name: str
73
+ viewpoint: str
74
+ level: str = ENTERPRISE # enterprise, solution or detail (a detail view shows the detail elements too)
75
+ nodes: list[ViewNode] = field(default_factory=list)
76
+ connections: list[ViewConnection] = field(default_factory=list)
77
+
78
+
79
+ def _level(e: Element) -> str:
80
+ return str((e.properties or {}).get("level") or "architect")
81
+
82
+
83
+ def _levels(properties: dict | None) -> frozenset[str]:
84
+ """The levels of an element or relationship: ``level`` detail, else ``levels`` (empty: a cross-level link), else the
85
+ enterprise level (a model from before the levels)."""
86
+ properties = properties or {}
87
+ if properties.get("level") == DETAIL:
88
+ return frozenset({DETAIL})
89
+ levels = properties.get("levels")
90
+ if isinstance(levels, list):
91
+ return frozenset(str(v) for v in levels)
92
+ return frozenset() if properties.get("cross_level") else frozenset({ENTERPRISE})
93
+
94
+
95
+ def _on(level: str, levels: frozenset[str]) -> bool:
96
+ """Whether something on ``levels`` belongs in a view of ``level`` (a detail view: the solution level and the detail)."""
97
+ return bool(levels & ({SOLUTION, DETAIL} if level == DETAIL else {level}))
98
+
99
+
100
+ def _order(e: Element) -> tuple[int, str, str]:
101
+ return (TYPE_ORDER.index(e.element_type) if e.element_type in TYPE_ORDER else len(TYPE_ORDER), e.name.casefold(), e.identifier)
102
+
103
+
104
+ def _layout(view: View, shown: list[Element]) -> None:
105
+ by_parent: dict[str, list[Element]] = {}
106
+ for e in shown:
107
+ if _level(e) == "detail":
108
+ by_parent.setdefault(str((e.properties or {}).get("parent") or ""), []).append(e)
109
+ shown_ids = {e.identifier for e in shown}
110
+ y = MARGIN
111
+ for band in BANDS:
112
+ # One group per element type (each starts a new row), every element followed by its detail members
113
+ groups: dict[str, list[Element]] = {}
114
+ for e in sorted((e for e in shown if _level(e) != "detail" and layer_of(e.element_type) == band), key=_order):
115
+ groups.setdefault(e.element_type, []).append(e)
116
+ groups[e.element_type] += sorted(by_parent.get(e.identifier, []), key=_order)
117
+ # Detail elements whose parent is not in the view still get a place, after the band's own
118
+ orphans = (e for e in shown if _level(e) == "detail" and layer_of(e.element_type) == band and str((e.properties or {}).get("parent")) not in shown_ids)
119
+ if rest := sorted(orphans, key=_order):
120
+ groups[""] = rest
121
+ if not groups:
122
+ continue
123
+ for group in groups.values():
124
+ for i, e in enumerate(group):
125
+ row, column = divmod(i, COLUMNS)
126
+ node_id = f"{view.identifier}-n-{e.identifier}"
127
+ view.nodes.append(ViewNode(identifier=node_id, element=e.identifier, x=MARGIN + column * (WIDTH + GAP), y=y + row * (HEIGHT + GAP)))
128
+ y += (len(group) + COLUMNS - 1) // COLUMNS * (HEIGHT + GAP)
129
+ y += BAND_GAP
130
+
131
+
132
+ def build_views(elements: list[Element], relationships: list[Relationship], relationship_levels: dict[str, frozenset[str]] | None = None) -> list[View]:
133
+ """The views of the model, one per viewpoint that has elements on its level, in VIEWPOINTS order.
134
+
135
+ Args:
136
+ relationship_levels: Relationship id -> its levels, where the relationship's own properties do not say (the
137
+ export's detail relationships); else from the properties
138
+ """
139
+ views: list[View] = []
140
+ for number, (name, viewpoint, types, level) in enumerate(VIEWPOINTS, 1):
141
+ shown = [e for e in elements if (types is None or e.element_type in types) and _on(level, _levels(e.properties))]
142
+ anchors = ANCHORS.get(name)
143
+ if not shown or (anchors and not any(e.element_type in anchors for e in shown)) or (level == DETAIL and not any(_level(e) == DETAIL for e in shown)):
144
+ continue
145
+ view = View(identifier=f"view-{number}", name=name, viewpoint=viewpoint, level=level)
146
+ _layout(view, shown)
147
+ node_of = {n.element: n.identifier for n in view.nodes}
148
+ for r in sorted(relationships, key=lambda r: r.identifier):
149
+ on = (relationship_levels or {}).get(r.identifier) or _levels(r.properties)
150
+ if r.source in node_of and r.target in node_of and _on(level, on):
151
+ connection = ViewConnection(identifier=f"{view.identifier}-c-{r.identifier}", relationship=r.identifier, source=node_of[r.source], target=node_of[r.target])
152
+ view.connections.append(connection)
153
+ views.append(view)
154
+ return views
@@ -8,12 +8,14 @@ Reference: https://www.opengroup.org/archimate-forum/archimate-exchange-format
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ from collections.abc import Mapping, Sequence
11
12
  from datetime import datetime
12
13
  from pathlib import Path
13
14
 
14
15
  from lxml import etree
15
16
 
16
17
  from .models import Element, Relationship
18
+ from .views import View
17
19
 
18
20
 
19
21
  class ArchiMateXMLExporter:
@@ -47,6 +49,9 @@ class ArchiMateXMLExporter:
47
49
  output_path: str,
48
50
  model_name: str = "ArchiMate Model",
49
51
  model_id: str = "model-001",
52
+ properties: Mapping[str, Mapping[str, str]] | None = None,
53
+ views: Sequence[View] | None = None,
54
+ relationship_properties: Mapping[str, Mapping[str, str]] | None = None,
50
55
  ) -> None:
51
56
  """
52
57
  Export ArchiMate model to XML file.
@@ -57,6 +62,9 @@ class ArchiMateXMLExporter:
57
62
  output_path: Path to output XML file
58
63
  model_name: Name of the model
59
64
  model_id: Unique identifier for the model
65
+ properties: Exported properties per element id (e.g. ``level``), declared as propertyDefinitions
66
+ views: Diagrams to export (see ``views.build_views``)
67
+ relationship_properties: Exported properties per relationship id, declared with the element ones
60
68
  """
61
69
  # Create root model element
62
70
  root = etree.Element(
@@ -76,17 +84,31 @@ class ArchiMateXMLExporter:
76
84
  # Add metadata
77
85
  self._add_metadata(root)
78
86
 
87
+ properties = properties or {}
88
+ relationship_properties = relationship_properties or {}
89
+ keys = sorted({key for values in [*properties.values(), *relationship_properties.values()] for key, value in values.items() if value})
90
+ definition_of = {key: f"propid-{key}" for key in keys}
91
+
79
92
  # Add elements section
80
93
  if elements:
81
94
  elements_section = etree.SubElement(root, "elements")
82
95
  for element in elements:
83
- self._add_element(elements_section, element)
96
+ self._add_element(elements_section, element, properties.get(element.identifier, {}), definition_of)
84
97
 
85
98
  # Add relationships section
86
99
  if relationships:
87
100
  rels_section = etree.SubElement(root, "relationships")
88
101
  for relationship in relationships:
89
- self._add_relationship(rels_section, relationship)
102
+ self._add_relationship(rels_section, relationship, relationship_properties.get(relationship.identifier, {}), definition_of)
103
+
104
+ # The exchange format's order: elements, relationships, organizations, propertyDefinitions, views
105
+ if keys:
106
+ definitions = etree.SubElement(root, "propertyDefinitions")
107
+ for key in keys:
108
+ definition = etree.SubElement(definitions, "propertyDefinition", attrib={"identifier": definition_of[key], "type": "string"})
109
+ etree.SubElement(definition, "name").text = key
110
+ if views:
111
+ self._add_views(root, views, definition_of.get("level"))
90
112
 
91
113
  # Write to file
92
114
  tree = etree.ElementTree(root)
@@ -124,13 +146,21 @@ class ArchiMateXMLExporter:
124
146
  description = etree.SubElement(metadata, f"{{{self.DC_NS}}}description")
125
147
  description.text = "ArchiMate model generated by Deriva from source code analysis"
126
148
 
127
- def _add_element(self, parent: etree.Element, element: Element) -> None:
149
+ def _add_element(
150
+ self,
151
+ parent: etree.Element,
152
+ element: Element,
153
+ properties: Mapping[str, str] | None = None,
154
+ definition_of: Mapping[str, str] | None = None,
155
+ ) -> None:
128
156
  """
129
157
  Add an element to the XML tree.
130
158
 
131
159
  Args:
132
160
  parent: Parent XML element
133
161
  element: ArchiMate element to add
162
+ properties: The element's exported properties (key -> value)
163
+ definition_of: propertyDefinition identifier per key
134
164
  """
135
165
  elem = etree.SubElement(
136
166
  parent,
@@ -152,17 +182,77 @@ class ArchiMateXMLExporter:
152
182
  doc_elem.set(f"{{{self.XML_NS}}}lang", "en")
153
183
  doc_elem.text = element.documentation
154
184
 
155
- # Note: Properties are not exported as they require propertyDefinitions
156
- # at the model level per ArchiMate Exchange Format XSD.
157
- # Internal metadata (source, confidence, derived_at) is omitted.
158
-
159
- def _add_relationship(self, parent: etree.Element, relationship: Relationship) -> None:
185
+ # Only the properties passed in are exported (each needs a model-level propertyDefinition);
186
+ # internal metadata (source, derived_at) on the element is omitted.
187
+ shown = sorted((key, value) for key, value in (properties or {}).items() if value and key in (definition_of or {}))
188
+ if shown and definition_of:
189
+ props = etree.SubElement(elem, "properties")
190
+ for key, value in shown:
191
+ prop = etree.SubElement(props, "property", attrib={"propertyDefinitionRef": definition_of[key]})
192
+ value_elem = etree.SubElement(prop, "value")
193
+ value_elem.set(f"{{{self.XML_NS}}}lang", "en")
194
+ value_elem.text = value
195
+
196
+ def _add_views(self, root: etree.Element, views: Sequence[View], level_definition: str | None = None) -> None:
197
+ """Add the diagrams: element nodes with their bounds, relationship connections between them, and each view's
198
+ level as a property where the model declares one."""
199
+ diagrams = etree.SubElement(etree.SubElement(root, "views"), "diagrams")
200
+ for view in views:
201
+ diagram = etree.SubElement(
202
+ diagrams,
203
+ "view",
204
+ attrib={"identifier": view.identifier, f"{{{self.XSI_NS}}}type": "Diagram", "viewpoint": view.viewpoint},
205
+ )
206
+ name_elem = etree.SubElement(diagram, "name")
207
+ name_elem.set(f"{{{self.XML_NS}}}lang", "en")
208
+ name_elem.text = view.name
209
+ if level_definition:
210
+ prop = etree.SubElement(etree.SubElement(diagram, "properties"), "property", attrib={"propertyDefinitionRef": level_definition})
211
+ value_elem = etree.SubElement(prop, "value")
212
+ value_elem.set(f"{{{self.XML_NS}}}lang", "en")
213
+ value_elem.text = view.level
214
+ for node in view.nodes:
215
+ etree.SubElement(
216
+ diagram,
217
+ "node",
218
+ attrib={
219
+ "identifier": node.identifier,
220
+ "elementRef": node.element,
221
+ f"{{{self.XSI_NS}}}type": "Element",
222
+ "x": str(node.x),
223
+ "y": str(node.y),
224
+ "w": str(node.w),
225
+ "h": str(node.h),
226
+ },
227
+ )
228
+ for connection in view.connections:
229
+ etree.SubElement(
230
+ diagram,
231
+ "connection",
232
+ attrib={
233
+ "identifier": connection.identifier,
234
+ "relationshipRef": connection.relationship,
235
+ f"{{{self.XSI_NS}}}type": "Relationship",
236
+ "source": connection.source,
237
+ "target": connection.target,
238
+ },
239
+ )
240
+
241
+ def _add_relationship(
242
+ self,
243
+ parent: etree.Element,
244
+ relationship: Relationship,
245
+ properties: Mapping[str, str] | None = None,
246
+ definition_of: Mapping[str, str] | None = None,
247
+ ) -> None:
160
248
  """
161
249
  Add a relationship to the XML tree.
162
250
 
163
251
  Args:
164
252
  parent: Parent XML element
165
253
  relationship: ArchiMate relationship to add
254
+ properties: The relationship's exported properties (key -> value)
255
+ definition_of: propertyDefinition identifier per key
166
256
  """
167
257
  rel = etree.SubElement(
168
258
  parent,
@@ -185,8 +275,15 @@ class ArchiMateXMLExporter:
185
275
  doc_elem = etree.SubElement(rel, "documentation")
186
276
  doc_elem.text = relationship.documentation
187
277
 
188
- # Note: Properties are not exported as they require propertyDefinitions
189
- # at the model level per ArchiMate Exchange Format XSD.
278
+ # Only the properties passed in are exported (each needs a model-level propertyDefinition), after name and documentation
279
+ shown = sorted((key, value) for key, value in (properties or {}).items() if value and key in (definition_of or {}))
280
+ if shown and definition_of:
281
+ props = etree.SubElement(rel, "properties")
282
+ for key, value in shown:
283
+ prop = etree.SubElement(props, "property", attrib={"propertyDefinitionRef": definition_of[key]})
284
+ value_elem = etree.SubElement(prop, "value")
285
+ value_elem.set(f"{{{self.XML_NS}}}lang", "en")
286
+ value_elem.text = str(value)
190
287
 
191
288
  def export_to_string(
192
289
  self,
@@ -25,10 +25,12 @@ Usage:
25
25
 
26
26
  from __future__ import annotations
27
27
 
28
+ from .db_tool import DATA_DIR
28
29
  from .db_tool import export_all as export_database
29
30
  from .db_tool import import_all as import_database
30
31
  from .manager import (
31
32
  DB_PATH,
33
+ ConfigDatabaseBusy,
32
34
  ensure_database,
33
35
  get_connection,
34
36
  init_database,
@@ -37,7 +39,12 @@ from .manager import (
37
39
  seed_database,
38
40
  )
39
41
 
42
+ # Bundled business function models (a config path names one of them, or a user-supplied file)
43
+ FUNCTION_MODELS_DIR = DATA_DIR / "function_models"
44
+
40
45
  __all__ = [
46
+ "FUNCTION_MODELS_DIR",
47
+ "ConfigDatabaseBusy",
41
48
  "ensure_database",
42
49
  "get_connection",
43
50
  "init_database",