pyegeria 5.4.0.33__py3-none-any.whl → 5.4.0.34__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.
Files changed (132) hide show
  1. commands/.DS_Store +0 -0
  2. commands/cat/.DS_Store +0 -0
  3. commands/cat/.env +8 -0
  4. commands/cat/debug_log.2025-08-26_20-04-40_905576.log.zip +0 -0
  5. commands/cat/debug_log.2025-08-27_20-09-41_795022.log.zip +0 -0
  6. commands/cat/debug_log.log +898 -0
  7. commands/cat/list_format_set.py +4 -1
  8. commands/cat/logs/pyegeria.log +90 -0
  9. commands/cli/debug_log.log +0 -0
  10. commands/doc/.DS_Store +0 -0
  11. commands/ops/logs/pyegeria.log +0 -0
  12. md_processing/.DS_Store +0 -0
  13. md_processing/.idea/.gitignore +8 -0
  14. md_processing/.idea/inspectionProfiles/Project_Default.xml +59 -0
  15. md_processing/.idea/md_processing.iml +15 -0
  16. md_processing/.idea/modules.xml +8 -0
  17. md_processing/.idea/sonarlint/issuestore/index.pb +0 -0
  18. md_processing/.idea/sonarlint/securityhotspotstore/index.pb +0 -0
  19. md_processing/.idea/vcs.xml +6 -0
  20. md_processing/.idea/workspace.xml +107 -0
  21. md_processing/__init__.py +3 -2
  22. md_processing/data/commands.json +11496 -10345
  23. md_processing/dr_egeria.py +14 -6
  24. md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
  25. md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
  26. md_processing/dr_egeria_inbox/arch_test.md +57 -0
  27. md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
  28. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
  29. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
  30. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
  31. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
  32. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
  33. md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
  34. md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
  35. md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
  36. md_processing/dr_egeria_inbox/cat_test.md +100 -0
  37. md_processing/dr_egeria_inbox/collections.md +39 -0
  38. md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
  39. md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
  40. md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
  41. md_processing/dr_egeria_inbox/data_field.md +54 -0
  42. md_processing/dr_egeria_inbox/data_spec.md +77 -0
  43. md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
  44. md_processing/dr_egeria_inbox/data_test.md +179 -0
  45. md_processing/dr_egeria_inbox/data_test2.md +429 -0
  46. md_processing/dr_egeria_inbox/data_test3.md +462 -0
  47. md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
  48. md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
  49. md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
  50. md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +318 -0
  51. md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
  52. md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
  53. md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
  54. md_processing/dr_egeria_inbox/glossary_list.md +5 -0
  55. md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
  56. md_processing/dr_egeria_inbox/glossary_test1.md +363 -0
  57. md_processing/dr_egeria_inbox/gov_def.md +482 -0
  58. md_processing/dr_egeria_inbox/gov_def2.md +447 -0
  59. md_processing/dr_egeria_inbox/img.png +0 -0
  60. md_processing/dr_egeria_inbox/output_tests.md +103 -0
  61. md_processing/dr_egeria_inbox/product.md +211 -0
  62. md_processing/dr_egeria_inbox/rel.md +8 -0
  63. md_processing/dr_egeria_inbox/sb.md +119 -0
  64. md_processing/dr_egeria_inbox/solution-components.md +136 -0
  65. md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
  66. md_processing/dr_egeria_inbox/synonym_test.md +42 -0
  67. md_processing/dr_egeria_inbox/t2.md +268 -0
  68. md_processing/dr_egeria_outbox/.DS_Store +0 -0
  69. md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
  70. md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
  71. md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +7 -0
  72. md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +33 -0
  73. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/main.js +5164 -0
  74. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/manifest.json +10 -0
  75. md_processing/dr_egeria_outbox/.obsidian/plugins/buttons/styles.css +624 -0
  76. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
  77. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
  78. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
  79. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
  80. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  81. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  82. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  83. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
  84. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  85. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  86. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/data.json +38 -0
  87. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
  88. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  89. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
  90. md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
  91. md_processing/dr_egeria_outbox/.obsidian/workspace.json +270 -0
  92. md_processing/dr_egeria_outbox/Button Test.md +11 -0
  93. md_processing/dr_egeria_outbox/Scripts/.DS_Store +0 -0
  94. md_processing/dr_egeria_outbox/Scripts/sendRest.js +24 -0
  95. md_processing/dr_egeria_outbox/Templates/sendToApi.md.md +17 -0
  96. md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
  97. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
  98. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
  99. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
  100. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
  101. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
  102. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
  103. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
  104. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
  105. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
  106. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
  107. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
  108. md_processing/md_commands/glossary_commands.py +2 -2
  109. md_processing/md_commands/old_project_commands.py +164 -0
  110. md_processing/md_commands/product_manager_commands.py +5 -5
  111. md_processing/md_commands/project_commands.py +368 -134
  112. md_processing/md_processing_utils/common_md_proc_utils.py +1 -0
  113. md_processing/md_processing_utils/common_md_utils.py +13 -1
  114. md_processing/md_processing_utils/debug_log +3 -574
  115. md_processing/md_processing_utils/debug_log.log +0 -0
  116. md_processing/md_processing_utils/determine_width.py +103 -0
  117. md_processing/md_processing_utils/generate_dr_help.py +44 -18
  118. md_processing/md_processing_utils/logs/pyegeria.log +56 -0
  119. md_processing/md_processing_utils/md_processing_constants.py +37 -4
  120. pyegeria/.DS_Store +0 -0
  121. pyegeria/_output_formats.py +38 -10
  122. pyegeria/glossary_manager.py +0 -2
  123. pyegeria/output_formatter.py +9 -8
  124. pyegeria/project_manager.py +541 -420
  125. {pyegeria-5.4.0.33.dist-info → pyegeria-5.4.0.34.dist-info}/METADATA +2 -1
  126. {pyegeria-5.4.0.33.dist-info → pyegeria-5.4.0.34.dist-info}/RECORD +129 -24
  127. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-30-37.md +0 -163
  128. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-35-58.md +0 -474
  129. md_processing/md_processing_utils/dr-egeria-help-2025-07-17T17:22:09.md +0 -2065
  130. {pyegeria-5.4.0.33.dist-info → pyegeria-5.4.0.34.dist-info}/LICENSE +0 -0
  131. {pyegeria-5.4.0.33.dist-info → pyegeria-5.4.0.34.dist-info}/WHEEL +0 -0
  132. {pyegeria-5.4.0.33.dist-info → pyegeria-5.4.0.34.dist-info}/entry_points.txt +0 -0
@@ -9,17 +9,23 @@ Copyright Contributors to the ODPi Egeria project.
9
9
 
10
10
  import asyncio
11
11
 
12
+ from pyegeria import select_output_format_set
12
13
  from pyegeria._client_new import Client2
14
+ from pyegeria._output_formats import get_output_format_type_match
13
15
  from pyegeria.config import settings as app_settings
14
16
  from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody,
15
- TemplateRequestBody)
17
+ TemplateRequestBody, DeleteRequestBody, UpdateElementRequestBody,
18
+ NewRelationshipRequestBody)
19
+ from pyegeria.output_formatter import generate_output, populate_columns_from_properties, \
20
+ _extract_referenceable_properties, get_required_relationships
16
21
  from pyegeria.utils import body_slimmer, dynamic_catch
17
22
 
18
23
  EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
19
- from pyegeria._globals import NO_ELEMENTS_FOUND, NO_PROJECTS_FOUND
24
+ from loguru import logger
20
25
 
21
26
  PROJECT_TYPES = ["Project", "Campaign", "StudyProject", "Task", "PersonalProject"]
22
27
 
28
+
23
29
  class ProjectManager(Client2):
24
30
  """
25
31
  Create and manage projects. Projects may be organized in a hierarchy.
@@ -45,30 +51,105 @@ class ProjectManager(Client2):
45
51
  user_id: str,
46
52
  user_pwd: str = None,
47
53
  token: str = None,
48
- ):
54
+ ):
49
55
  self.view_server = view_server
50
56
  self.platform_url = platform_url
51
57
  self.user_id = user_id
52
58
  self.user_pwd = user_pwd
53
59
  self.project_command_base: str = (
54
- f"/api/open-metadata/project-manager/projects"
60
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects"
55
61
  )
56
62
  Client2.__init__(self, view_server, platform_url, user_id, user_pwd, token)
57
63
 
64
+ def _extract_additional_project_properties(self, element: dict, columns_struct: dict)-> dict:
65
+
66
+
67
+ roles_required = any(column.get('key') == 'project_roles'
68
+ for column in columns_struct.get('formats', {}).get('columns', []))
69
+ project_props = {}
70
+
71
+ if roles_required:
72
+ project_roles = element['elementHeader'].get('projectRoles', [])
73
+ project_roles_list = []
74
+ for project_role in project_roles:
75
+ project_roles_list.append(project_role.get('classificationName', ""))
76
+ project_roles_md = (", \n".join(project_roles_list)).rstrip(',') if project_roles_list else ''
77
+ project_props = {
78
+ 'project_roles': project_roles_md,
79
+ }
80
+ return project_props
81
+
82
+
83
+
84
+
85
+ def _extract_project_properties(self, element: dict, columns_struct: dict) -> dict:
86
+ props = element.get('properties', {}) or {}
87
+ normalized = {
88
+ 'properties': props,
89
+ 'elementHeader': element.get('elementHeader', {}),
90
+ }
91
+ col_data = populate_columns_from_properties(element, columns_struct)
92
+ # col_data = populate_columns_from_properties(normalized, columns_struct)
93
+ columns_list = col_data.get('formats', {}).get('columns', [])
94
+ header_props = _extract_referenceable_properties(element)
95
+ # Populate requested relationship-based columns generically
96
+ col_data = get_required_relationships(element, col_data)
97
+ additional_props = self._extract_additional_project_properties(element, columns_struct)
98
+ guid = header_props.get('GUID')
99
+
100
+ for column in columns_list:
101
+ key = column.get('key')
102
+ if key in header_props:
103
+ column['value'] = header_props.get(key)
104
+ elif key == 'project_roles':
105
+ column['value'] = additional_props.get('project_roles', '')
106
+ elif isinstance(key, str) and key.lower() == 'guid':
107
+ column['value'] = guid
108
+
109
+ for column in columns_list:
110
+ if column.get('key') == 'mermaid' and not column.get('value'):
111
+ column['value'] = element.get('mermaidGraph', '') or ''
112
+ break
113
+ return col_data
114
+
115
+
116
+ def _generate_project_output(self, elements: dict | list[dict], search_string: str,
117
+ element_type_name: str | None,
118
+ output_format: str = 'DICT',
119
+ output_format_set: dict | str = None) -> str | list[dict]:
120
+ entity_type = 'Project'
121
+ if output_format_set:
122
+ if isinstance(output_format_set, str):
123
+ output_formats = select_output_format_set(output_format_set, output_format)
124
+ elif isinstance(output_format_set, dict):
125
+ output_formats = get_output_format_type_match(output_format_set, output_format)
126
+ else:
127
+ output_formats = None
128
+ else:
129
+ output_formats = select_output_format_set(entity_type, output_format)
130
+ if output_formats is None:
131
+ output_formats = select_output_format_set('Default', output_format)
132
+ return generate_output(
133
+ elements=elements,
134
+ search_string=search_string,
135
+ entity_type=entity_type,
136
+ output_format=output_format,
137
+ extract_properties_func=self._extract_project_properties,
138
+ get_additional_props_func=None,
139
+ columns_struct=output_formats,
140
+ )
58
141
 
59
- def _generate_project_output(self):
60
- pass
61
142
  #
62
143
  # Retrieving Projects= Information - https://egeria-project.org/concepts/project
63
144
  #
145
+ @dynamic_catch
64
146
  async def _async_get_linked_projects(
65
147
  self,
66
148
  parent_guid: str,
67
- project_status: str = None,
68
- effective_time: str = None,
69
- start_from: int = 0,
70
- page_size: int = None,
71
- ) -> list | str:
149
+ body: dict | GetRequestBody = None,
150
+ output_format: str = 'JSON',
151
+ output_format_set: str | dict = None,
152
+ ) -> list | str:
72
153
  """Returns the list of projects that are linked off of the supplied element. Any relationship will do.
73
154
  The request body is optional, but if supplied acts as a filter on project status. Async version.
74
155
 
@@ -78,8 +159,6 @@ class ProjectManager(Client2):
78
159
  The identity of the parent to find linked projects from.
79
160
  project_status: str, optional
80
161
  Optionally, filter results by project status.
81
- effective_time: str, optional
82
- Time at which to query for projects. Time format is "YYYY-MM-DDTHH:MM:SS" (ISO 8601).
83
162
 
84
163
  start_from: int, [default=0], optional
85
164
  When multiple pages of results are available, the page number to start from.
@@ -104,30 +183,25 @@ class ProjectManager(Client2):
104
183
 
105
184
  """
106
185
 
107
- if page_size is None:
108
- page_size = self.page_size
109
-
110
- body = {
111
- "filter": project_status,
112
- "effectiveTime": effective_time,
113
- }
114
- body_s = body_slimmer(body)
115
186
  url = (
116
187
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/"
117
- f"metadata-elements/{parent_guid}/projects?startFrom={start_from}&pageSize={page_size}"
188
+ f"metadata-elements/{parent_guid}/projects"
118
189
  )
119
190
 
120
- resp = await self._async_make_request("POST", url, body_s)
121
- return resp.json().get("elements", "No linked projects found")
191
+ response = await self._async_get_guid_request(url, "Project", self._extract_project_properties,
192
+ body=body,
193
+ output_format=output_format,
194
+ output_format_set=output_format_set)
195
+ return response
122
196
 
197
+ @dynamic_catch
123
198
  def get_linked_projects(
124
199
  self,
125
200
  parent_guid: str,
126
- project_status: str = None,
127
- effective_time: str = None,
128
- start_from: int = 0,
129
- page_size: int = None,
130
- ) -> list | str:
201
+ body: dict | GetRequestBody = None,
202
+ output_format: str = 'JSON',
203
+ output_format_set: str | dict = None) -> str | dict:
204
+
131
205
  """Returns the list of projects that are linked off of the supplied element. Any relationship will do.
132
206
  The request body is optional, but if supplied acts as a filter on project status.
133
207
 
@@ -166,21 +240,23 @@ class ProjectManager(Client2):
166
240
  resp = loop.run_until_complete(
167
241
  self._async_get_linked_projects(
168
242
  parent_guid,
169
- project_status,
170
- effective_time,
171
- start_from,
172
- page_size,
173
- )
243
+ body,
244
+ output_format,
245
+ output_format_set
174
246
  )
247
+ )
175
248
  return resp
176
249
 
250
+ @dynamic_catch
177
251
  async def _async_get_classified_projects(
178
252
  self,
179
253
  project_classification: str,
180
- effective_time: str = None,
181
254
  start_from: int = 0,
182
- page_size: int = None,
183
- ) -> list | str:
255
+ page_size: int = 0,
256
+ output_format: str = 'JSON',
257
+ output_format_set: str | dict = None,
258
+ body: dict | GetRequestBody = None,) -> str | dict:
259
+
184
260
  """Returns the list of projects with a particular classification. The name of the classification is
185
261
  supplied in the request body. Examples of these classifications include StudyProject, PersonalProject,
186
262
  Campaign or Task. There is also GlossaryProject and GovernanceProject. Async version.
@@ -189,9 +265,6 @@ class ProjectManager(Client2):
189
265
  ----------
190
266
  project_classification: str
191
267
  The project classification to search for.
192
- effective_time: str, optional
193
- Time at which to query for projects. Time format is "YYYY-MM-DDTHH:MM:SS" (ISO 8601).
194
-
195
268
  start_from: int, [default=0], optional
196
269
  When multiple pages of results are available, the page number to start from.
197
270
  page_size: int, [default=None]
@@ -215,29 +288,28 @@ class ProjectManager(Client2):
215
288
 
216
289
  """
217
290
 
218
- if page_size is None:
219
- page_size = self.page_size
220
291
 
221
- body = {
222
- "filter": project_classification,
223
- "effectiveTime": effective_time,
224
- }
225
- body_s = body_slimmer(body)
226
292
  url = (
227
293
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/"
228
- f"projects/by-classifications?startFrom={start_from}&pageSize={page_size}"
294
+ f"projects/by-classifications"
229
295
  )
296
+ response = await self._async_get_name_request(url, "Project", self._extract_project_properties,
297
+ filter_string = project_classification, start_from=start_from,
298
+ page_size=page_size, body=body,
299
+ output_format=output_format,
300
+ output_format_set=output_format_set)
301
+ return response
230
302
 
231
- resp = await self._async_make_request("POST", url, body_s)
232
- return resp.json()
233
-
303
+ @dynamic_catch
234
304
  def get_classified_projects(
235
305
  self,
236
306
  project_classification: str,
237
- effective_time: str = None,
238
307
  start_from: int = 0,
239
- page_size: int = None,
240
- ) -> list | str:
308
+ page_size: int = 0,
309
+ output_format: str = 'JSON',
310
+ output_format_set: str | dict = None,
311
+ body: dict | GetRequestBody = None,
312
+ ) -> str | dict:
241
313
  """Returns the list of projects with a particular classification. The name of the classification is
242
314
  supplied in the request body. Examples of these classifications include StudyProject, PersonalProject,
243
315
  Campaign or Task. There is also GlossaryProject and GovernanceProject.
@@ -273,129 +345,14 @@ class ProjectManager(Client2):
273
345
  """
274
346
  loop = asyncio.get_event_loop()
275
347
  resp = loop.run_until_complete(
276
- self._async_get_classified_projects(
277
- project_classification,
278
- effective_time,
279
- start_from,
280
- page_size,
281
- )
282
- )
283
- return resp
348
+ self._async_get_classified_projects(project_classification,
349
+ start_from,page_size,
284
350
 
285
- async def _async_get_project_team(
286
- self,
287
- project_guid: str,
288
- team_role: str = None,
289
- effective_time: str = None,
290
- start_from: int = 0,
291
- page_size: int = None,
292
- ) -> list | str:
293
- """Returns the list of actors that are linked off of the project. This includes the project managers.
294
- The optional request body allows a teamRole to be specified as a filter. To filter out the project managers,
295
- specify ProjectManagement as the team role. See https://egeria-project.org/concepts/project for details.
296
- Async version.
297
-
298
- Parameters
299
- ----------
300
- project_guid: str
301
- The identity of the project to return team information about.
302
- team_role: str, optional
303
- team role to filter on. Project managers would be "ProjectManagement".
304
- effective_time: str, optional
305
- Time at which to query the team role. Time format is "YYYY-MM-DDTHH:MM:SS" (ISO 8601).
306
-
307
- start_from: int, [default=0], optional
308
- When multiple pages of results are available, the page number to start from.
309
- page_size: int, [default=None]
310
- The number of items to return in a single page. If not specified, the default will be taken from
311
- the class instance.
312
-
313
- Returns
314
- -------
315
- list | str
316
- The list of actors linked off the project, including project managers Returns a string if none found.
317
-
318
- Raises
319
- ------
320
- InvalidParameterException
321
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values.
322
- PropertyServerException
323
- Raised by the server when an issue arises in processing a valid request.
324
- NotAuthorizedException
325
- The principle specified by the user_id does not have authorization for the requested action.
326
- Notes
327
- -----
328
- """
329
-
330
- if page_size is None:
331
- page_size = self.page_size
332
-
333
- body = {effective_time: effective_time, "filter": team_role}
334
- body_s = body_slimmer(body)
335
- url = (
336
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
337
- f"{project_guid}/team?startFrom={start_from}&pageSize={page_size}"
338
- )
339
-
340
- resp = await self._async_make_request("POST", url, body_s)
341
-
342
- result = resp.json().get("elements", NO_ELEMENTS_FOUND)
343
- return result
344
-
345
- def get_project_team(
346
- self,
347
- project_guid: str,
348
- team_role: str = None,
349
- effective_time: str = None,
350
- start_from: int = 0,
351
- page_size: int = None,
352
- ) -> list | str:
353
- """Returns the list of actors that are linked off of the project. This includes the project managers.
354
- The optional request body allows a teamRole to be specified as a filter. To filter out the project managers,
355
- specify ProjectManagement as the team role. See https://egeria-project.org/concepts/project for details.
356
- Async version.
357
-
358
- Parameters
359
- ----------
360
- project_guid: str
361
- The identity of the project to return team information about.
362
- team_role: str, optional
363
- team role to filter on. Project managers would be "ProjectManagement".
364
- effective_time: str, optional
365
- Time at which to query the team role. Time format is "YYYY-MM-DDTHH:MM:SS" (ISO 8601).
366
-
367
- start_from: int, [default=0], optional
368
- When multiple pages of results are available, the page number to start from.
369
- page_size: int, [default=None]
370
- The number of items to return in a single page. If not specified, the default will be taken from
371
- the class instance.
372
-
373
- Returns
374
- -------
375
- list | str
376
- The list of actors linked off the project, including project managers Returns a string if none found.
377
-
378
- Raises
379
- ------
380
- InvalidParameterException
381
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values.
382
- PropertyServerException
383
- Raised by the server when an issue arises in processing a valid request.
384
- NotAuthorizedException
385
- The principle specified by the user_id does not have authorization for the requested action.
386
- Notes
387
- -----
388
- """
389
- loop = asyncio.get_event_loop()
390
- resp = loop.run_until_complete(
391
- self._async_get_project_team(
392
- project_guid,
393
- team_role,
394
- effective_time,
395
- start_from,
396
- page_size,
397
- )
351
+ output_format,
352
+ output_format_set,
353
+ body
398
354
  )
355
+ )
399
356
  return resp
400
357
 
401
358
  @dynamic_catch
@@ -409,7 +366,7 @@ class ProjectManager(Client2):
409
366
  page_size: int = 0,
410
367
  output_format: str = "json", output_format_set: str | dict = None,
411
368
  body: dict | SearchStringRequestBody = None
412
- ) -> list | str:
369
+ ) -> list | str:
413
370
  """Returns the list of projects matching the search string.
414
371
  The search string is located in the request body and is interpreted as a plain string.
415
372
  The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
@@ -479,7 +436,7 @@ class ProjectManager(Client2):
479
436
  page_size: int = 0,
480
437
  output_format: str = "json", output_format_set: str | dict = None,
481
438
  body: dict | SearchStringRequestBody = None
482
- ) -> list | str:
439
+ ) -> list | str:
483
440
 
484
441
  """Returns the list of projects matching the search string.
485
442
  The search string is located in the request body and is interpreted as a plain string.
@@ -534,29 +491,29 @@ class ProjectManager(Client2):
534
491
  output_format,
535
492
  output_format_set,
536
493
  body,
537
- )
538
494
  )
495
+ )
539
496
 
540
497
  return resp
541
498
 
542
499
  @dynamic_catch
543
500
  async def _async_get_projects_by_name(
544
- self, filter_string: str = None, classification_names: list[str] = None,
545
- body: dict | FilterRequestBody = None,
546
- start_from: int = 0, page_size: int = 0,
547
- output_format: str = 'JSON',
548
- output_format_set: str | dict = None) -> list | str:
549
- url = f"{self.project_command_base}/by-name"
550
-
551
- response = await self._async_get_name_request(url, _type="Projects",
552
- _gen_output=self._generate_projects_output,
553
- filter_string=filter_string,
554
- classification_names=classification_names,
555
- start_from=start_from, page_size=page_size,
556
- output_format=output_format, output_format_set=output_format_set,
557
- body=body)
501
+ self, filter_string: str = None, classification_names: list[str] = None,
502
+ body: dict | FilterRequestBody = None,
503
+ start_from: int = 0, page_size: int = 0,
504
+ output_format: str = 'JSON',
505
+ output_format_set: str | dict = None) -> list | str:
506
+ url = f"{self.project_command_base}/by-name"
558
507
 
559
- return response
508
+ response = await self._async_get_name_request(url, _type="Projects",
509
+ _gen_output=self._generate_project_output,
510
+ filter_string=filter_string,
511
+ classification_names=classification_names,
512
+ start_from=start_from, page_size=page_size,
513
+ output_format=output_format, output_format_set=output_format_set,
514
+ body=body)
515
+
516
+ return response
560
517
 
561
518
  @dynamic_catch
562
519
  def get_projects_by_name(
@@ -576,15 +533,15 @@ class ProjectManager(Client2):
576
533
  page_size,
577
534
  output_format,
578
535
  output_format_set,
579
- )
580
536
  )
537
+ )
581
538
  return resp
582
539
 
583
540
  @dynamic_catch
584
541
  async def _async_get_project_by_guid(self, project_guid: str, element_type: str = None,
585
- body: dict | GetRequestBody = None,
586
- output_format: str = 'JSON',
587
- output_format_set: str | dict = None) -> dict | str:
542
+ body: dict | GetRequestBody = None,
543
+ output_format: str = 'JSON',
544
+ output_format_set: str | dict = None) -> dict | str:
588
545
  """Return the properties of a specific project. Async version.
589
546
 
590
547
  Parameters
@@ -633,7 +590,7 @@ class ProjectManager(Client2):
633
590
  type = element_type if element_type else "Collection"
634
591
 
635
592
  response = await self._async_get_guid_request(url, _type=type,
636
- _gen_output=self._generate_collection_output,
593
+ _gen_output=self._generate_project_output,
637
594
  output_format=output_format, output_format_set=output_format_set,
638
595
  body=body)
639
596
 
@@ -641,9 +598,9 @@ class ProjectManager(Client2):
641
598
 
642
599
  @dynamic_catch
643
600
  def get_project_by_guid(self, project_guid: str, element_type: str = None,
644
- body: dict | GetRequestBody = None,
645
- output_format: str = 'JSON',
646
- output_format_set: str | dict = None) -> dict | str:
601
+ body: dict | GetRequestBody = None,
602
+ output_format: str = 'JSON',
603
+ output_format_set: str | dict = None) -> dict | str:
647
604
  """Return the properties of a specific project.
648
605
 
649
606
  Parameters
@@ -688,8 +645,8 @@ class ProjectManager(Client2):
688
645
  """
689
646
  loop = asyncio.get_event_loop()
690
647
  resp = loop.run_until_complete(
691
- self._async_get_project_by_guid(project_guid, element_type, body, output_format, output_format_set )
692
- )
648
+ self._async_get_project_by_guid(project_guid, element_type, body, output_format, output_format_set)
649
+ )
693
650
 
694
651
  return resp
695
652
 
@@ -701,7 +658,7 @@ class ProjectManager(Client2):
701
658
  body: dict | GetRequestBody = None,
702
659
  output_format: str = 'JSON',
703
660
  output_format_set: str | dict = None,
704
- ) -> dict | str:
661
+ ) -> dict | str:
705
662
  """Return the mermaid graph of a specific project. Async version.
706
663
 
707
664
  Parameters
@@ -728,7 +685,6 @@ class ProjectManager(Client2):
728
685
 
729
686
  """
730
687
 
731
-
732
688
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/pr"
733
689
  f"ojects/{project_guid}/graph")
734
690
 
@@ -747,7 +703,7 @@ class ProjectManager(Client2):
747
703
  body: dict | GetRequestBody = None,
748
704
  output_format: str = 'JSON',
749
705
  output_format_set: str | dict = None,
750
- ) -> dict | str:
706
+ ) -> dict | str:
751
707
  """Return the mermaid graph of a specific project. Async version.
752
708
 
753
709
  Parameters
@@ -776,7 +732,7 @@ class ProjectManager(Client2):
776
732
  loop = asyncio.get_event_loop()
777
733
  resp = loop.run_until_complete(
778
734
  self._async_get_project_graph(project_guid, element_type, body, output_format, output_format_set)
779
- )
735
+ )
780
736
 
781
737
  return resp
782
738
 
@@ -787,7 +743,7 @@ class ProjectManager(Client2):
787
743
  async def _async_create_project(
788
744
  self,
789
745
  body: dict | NewElementRequestBody,
790
- ) -> str:
746
+ ) -> str:
791
747
  """Create project: https://egeria-project.org/concepts/project Async version.
792
748
 
793
749
  Parameters
@@ -841,16 +797,15 @@ class ProjectManager(Client2):
841
797
 
842
798
  """
843
799
 
844
-
845
800
  url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects"
846
801
 
847
- return await self._async_create_element_body_request(body, ["ProjectProperties"], body)
802
+ return await self._async_create_element_body_request(url, ["ProjectProperties"], body)
848
803
 
849
804
  @dynamic_catch
850
805
  def create_project(
851
806
  self,
852
807
  body: dict | NewElementRequestBody,
853
- ) -> str:
808
+ ) -> str:
854
809
  """Create project: https://egeria-project.org/concepts/project
855
810
 
856
811
  Parameters
@@ -898,14 +853,14 @@ class ProjectManager(Client2):
898
853
  loop = asyncio.get_event_loop()
899
854
  resp = loop.run_until_complete(
900
855
  self._async_create_project(body)
901
- )
856
+ )
902
857
  return resp
903
858
 
904
859
  @dynamic_catch
905
860
  async def _async_create_project_from_template(
906
861
  self,
907
862
  body: dict | TemplateRequestBody,
908
- ) -> str:
863
+ ) -> str:
909
864
  """Create a new metadata element to represent a project using an existing metadata element as a template.
910
865
  The template defines additional classifications and relationships that should be added to the new project.
911
866
  Async version.
@@ -968,7 +923,7 @@ class ProjectManager(Client2):
968
923
  def create_project_from_template(
969
924
  self,
970
925
  body: dict,
971
- ) -> str:
926
+ ) -> str:
972
927
  """Create a new metadata element to represent a project using an existing metadata element as a template.
973
928
  The template defines additional classifications and relationships that should be added to the new project.
974
929
 
@@ -1028,20 +983,12 @@ class ProjectManager(Client2):
1028
983
  #
1029
984
  #
1030
985
 
986
+ @dynamic_catch
1031
987
  async def _async_update_project(
1032
988
  self,
1033
989
  project_guid: str,
1034
- qualified_name: str = None,
1035
- identifier: str = None,
1036
- display_name: str = None,
1037
- description: str = None,
1038
- project_status: str = None,
1039
- project_phase: str = None,
1040
- project_health: str = None,
1041
- start_date: str = None,
1042
- planned_end_date: str = None,
1043
- replace_all_props: bool = False,
1044
- ) -> None:
990
+ body: dict | UpdateElementRequestBody
991
+ ) -> None:
1045
992
  """Update the properties of a project. Async Version.
1046
993
 
1047
994
  Parameters
@@ -1083,42 +1030,20 @@ class ProjectManager(Client2):
1083
1030
  The principle specified by the user_id does not have authorization for the requested action
1084
1031
  """
1085
1032
 
1086
- replace_all_props_s = str(replace_all_props).lower()
1087
1033
  url = (
1088
1034
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1089
- f"update?replaceAllProperties={replace_all_props_s}"
1035
+ f"update"
1090
1036
  )
1091
1037
 
1092
- body = {
1093
- "class": "ProjectProperties",
1094
- "qualifiedName": qualified_name,
1095
- "identifier": identifier,
1096
- "name": display_name,
1097
- "description": description,
1098
- "projectStatus": project_status,
1099
- "projectPhase": project_phase,
1100
- "projectHealth": project_health,
1101
- "startDate": start_date,
1102
- "plannedEndDate": planned_end_date,
1103
- }
1104
- body_s = body_slimmer(body)
1105
- await self._async_make_request("POST", url, body_s)
1106
- return
1038
+ await self._async_update_element_body_request(url, ["ProjectProperties"], body)
1039
+ logger.info(f"Updated digital subscription {project_guid}")
1107
1040
 
1041
+ @dynamic_catch
1108
1042
  def update_project(
1109
1043
  self,
1110
1044
  project_guid: str,
1111
- qualified_name: str = None,
1112
- identifier: str = None,
1113
- display_name: str = None,
1114
- description: str = None,
1115
- project_status: str = None,
1116
- project_phase: str = None,
1117
- project_health: str = None,
1118
- start_date: str = None,
1119
- planned_end_date: str = None,
1120
- replace_all_props: bool = False,
1121
- ) -> None:
1045
+ body: dict | UpdateElementRequestBody,
1046
+ ) -> None:
1122
1047
  """Update the properties of a project.
1123
1048
 
1124
1049
  Parameters
@@ -1161,26 +1086,12 @@ class ProjectManager(Client2):
1161
1086
  """
1162
1087
  loop = asyncio.get_event_loop()
1163
1088
  loop.run_until_complete(
1164
- self._async_update_project(
1165
- project_guid,
1166
- qualified_name,
1167
- identifier,
1168
- display_name,
1169
- description,
1170
- project_status,
1171
- project_phase,
1172
- project_health,
1173
- start_date,
1174
- planned_end_date,
1175
- replace_all_props,
1176
- )
1177
- )
1178
- return
1089
+ self._async_update_project(project_guid, body))
1179
1090
 
1091
+ @dynamic_catch
1180
1092
  async def _async_delete_project(
1181
1093
  self,
1182
- project_guid: str, cascade: bool = False
1183
- ) -> None:
1094
+ project_guid: str, cascade: bool = False, body: dict | DeleteRequestBody = None) -> None:
1184
1095
  """Delete a project. It is detected from all parent elements. Async version
1185
1096
 
1186
1097
  Parameters
@@ -1207,18 +1118,16 @@ class ProjectManager(Client2):
1207
1118
  cascade_s = str(cascade).lower()
1208
1119
  url = (
1209
1120
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
1210
- f"{project_guid}/delete?cascadedDelete={cascade_s}"
1121
+ f"{project_guid}/delete"
1211
1122
  )
1212
1123
 
1213
- body = {"class": "NullRequestBody"}
1214
-
1215
- await self._async_make_request("POST", url, body)
1216
- return
1124
+ await self._async_delete_request(url, body, cascade)
1125
+ logger.info(f"Deleted project {project_guid} with cascade {cascade}")
1217
1126
 
1127
+ @dynamic_catch
1218
1128
  def delete_project(
1219
1129
  self,
1220
- project_guid: str, cascade: bool = False
1221
- ) -> None:
1130
+ project_guid: str, cascade: bool = False, body: dict | DeleteRequestBody = None) -> None:
1222
1131
  """Delete a project. It is detected from all parent elements.
1223
1132
 
1224
1133
  Parameters
@@ -1247,40 +1156,30 @@ class ProjectManager(Client2):
1247
1156
 
1248
1157
  """
1249
1158
  loop = asyncio.get_event_loop()
1250
- loop.run_until_complete(self._async_delete_project(project_guid, cascade))
1251
- return
1159
+ loop.run_until_complete(self._async_delete_project(project_guid, cascade, body))
1252
1160
 
1253
- async def _async_add_to_project_team(
1254
- self,
1255
- project_guid: str,
1256
- actor_guid: str,
1257
- team_role: str = None,
1258
- effective_from: str = None,
1259
- effective_to: str = None,
1260
- ) -> None:
1261
- """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1262
- the actor plays in the project. Async version.
1161
+ @dynamic_catch
1162
+ async def _async_set_project_dependency(self, project_guid: str,
1163
+ upstream_project_guid: str,
1164
+ body: dict | NewRelationshipRequestBody = None):
1165
+ """ A project depends on an upstream project.
1166
+ Request body is optional. Async version.
1263
1167
 
1264
1168
  Parameters
1265
1169
  ----------
1170
+ upstream_project_guid: str
1171
+ The guid of the project depended on.
1266
1172
  project_guid: str
1267
- identity of the project to update.
1268
- actor_guid: str
1269
- identity of the actor to add.
1270
- team_role: str, optional, defaults to None
1271
- Name of the role the actor plays in the project.
1272
- effective_from: str, optional, defaults to None
1273
- Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1274
- effective_to: str, optional, defaults to None
1275
- Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1173
+ The guid of the dependent project
1174
+ body: dict | NewRelationshipRequestBody, optional, default = None
1175
+ A dict representing the details of the relationship.
1276
1176
 
1277
1177
  Returns
1278
1178
  -------
1279
- None
1179
+ Nothing
1280
1180
 
1281
1181
  Raises
1282
1182
  ------
1283
-
1284
1183
  InvalidParameterException
1285
1184
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1286
1185
  PropertyServerException
@@ -1288,56 +1187,122 @@ class ProjectManager(Client2):
1288
1187
  NotAuthorizedException
1289
1188
  The principle specified by the user_id does not have authorization for the requested action
1290
1189
 
1291
- """
1190
+ Notes
1191
+ -----
1292
1192
 
1193
+ """
1293
1194
  url = (
1294
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1295
- f"members/{actor_guid}/attach"
1195
+ f"{self.project_command_base}/{project_guid}/project-dependencies/{upstream_project_guid}/attach"
1296
1196
  )
1297
- body = {
1298
- "class": "ProjectTeamProperties",
1299
- "teamRole": team_role,
1300
- "effectiveFrom": effective_from,
1301
- "effectiveTo": effective_to,
1302
- }
1303
- body_s = body_slimmer(body)
1304
- if body_s is None:
1305
- await self._async_make_request("POST", url)
1306
- else:
1307
- await self._async_make_request("POST", url, body_s)
1308
- return
1197
+ await self._async_new_relationship_request(url, "ProjectDependencyProperties", body)
1198
+ logger.info(f"Project {project_guid} depends on -> {upstream_project_guid}")
1309
1199
 
1310
- def add_to_project_team(
1311
- self,
1312
- project_guid: str,
1313
- actor_guid: str,
1314
- team_role: str = None,
1315
- effective_from: str = None,
1316
- effective_to: str = None,
1317
- ) -> None:
1318
- """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1319
- the actor plays in the project.
1200
+ @dynamic_catch
1201
+ async def _async_set_project_dependency(self, project_guid: str,
1202
+ upstream_project_guid: str,
1203
+ body: dict | NewRelationshipRequestBody = None):
1204
+ """ Link two dependent digital products. The linked elements are of type DigitalProduct.
1205
+ Request body is optional.
1206
+
1207
+ Parameters
1208
+ ----------
1209
+ upstream_digital_prod_guid: str
1210
+ The guid of the first digital product
1211
+ downstream_digital_prod_guid: str
1212
+ The guid of the downstream digital product
1213
+ body: dict | NewRelationshipRequestBody, optional, default = None
1214
+ A structure representing the details of the relationship.
1215
+
1216
+ Returns
1217
+ -------
1218
+ Nothing
1219
+
1220
+ Raises
1221
+ ------
1222
+ InvalidParameterException
1223
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1224
+ PropertyServerException
1225
+ Raised by the server when an issue arises in processing a valid request
1226
+ NotAuthorizedException
1227
+ The principle specified by the user_id does not have authorization for the requested action
1228
+
1229
+ Notes
1230
+ -----
1231
+
1232
+ """
1233
+ loop = asyncio.get_event_loop()
1234
+ loop.run_until_complete(
1235
+ self._async_set_project_dependency(project_guid, upstream_project_guid,
1236
+ body))
1237
+
1238
+ @dynamic_catch
1239
+ async def _async_clear_project_dependency(self, project_guid: str,
1240
+ upstream_project_guid: str,
1241
+ body: dict | DeleteRequestBody = None) -> None:
1242
+ """ Unlink two dependent projects. Request body is optional. Async version.
1320
1243
 
1321
1244
  Parameters
1322
1245
  ----------
1323
1246
  project_guid: str
1324
- identity of the project to update.
1325
- actor_guid: str
1326
- identity of the actor to add.
1327
- team_role: str, optional, defaults to None
1328
- Name of the role the actor plays in the project.
1329
- effective_from: str, optional, defaults to None
1330
- Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1331
- effective_to: str, optional, defaults to None
1332
- Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1247
+ The guid of the dependent project.
1248
+ upstream_project_guid: str
1249
+ The guid of the upstream digital project
1250
+ body: dict | DeleteRequestBody, optional, default = None
1251
+ A structure representing the details of the relationship.
1333
1252
 
1334
1253
  Returns
1335
1254
  -------
1336
- None
1255
+ Nothing
1337
1256
 
1338
1257
  Raises
1339
1258
  ------
1259
+ InvalidParameterException
1260
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1261
+ PropertyServerException
1262
+ Raised by the server when an issue arises in processing a valid request
1263
+ NotAuthorizedException
1264
+ The principle specified by the user_id does not have authorization for the requested action
1340
1265
 
1266
+ Notes
1267
+ -----
1268
+ JSON Structure looks like:
1269
+ {
1270
+ "class": "DeleteRequestBody",
1271
+ "externalSourceGUID": "add guid here",
1272
+ "externalSourceName": "add qualified name here",
1273
+ "effectiveTime": "{{$isoTimestamp}}",
1274
+ "forLineage": false,
1275
+ "forDuplicateProcessing": false
1276
+ }
1277
+
1278
+ """
1279
+
1280
+ url = "{self.project_command_base}/{project_guid}/project-dependencies/{upstream_project_guid}/detach"
1281
+
1282
+ await self._async_delete_request(url, body)
1283
+ logger.info(
1284
+ f"Detached project {project_guid} from -> {upstream_project_guid}")
1285
+
1286
+ @dynamic_catch
1287
+ def clear_project_dependency(self, project_guid: str, upstream_project_guid: str,
1288
+ body: dict | DeleteRequestBody = None):
1289
+ """ Unlink two dependent projects. Request body is optional.
1290
+
1291
+ Parameters
1292
+ ----------
1293
+ project_guid: str
1294
+ The guid of the dependent project.
1295
+ upstream_project_guid: str
1296
+ The guid of the upstream digital project
1297
+ body: dict | DeleteRequestBody, optional, default = None
1298
+ A structure representing the details of the relationship.
1299
+
1300
+ Returns
1301
+ -------
1302
+ Nothing
1303
+
1304
+ Raises
1305
+ ------
1341
1306
  InvalidParameterException
1342
1307
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1343
1308
  PropertyServerException
@@ -1345,40 +1310,45 @@ class ProjectManager(Client2):
1345
1310
  NotAuthorizedException
1346
1311
  The principle specified by the user_id does not have authorization for the requested action
1347
1312
 
1313
+ Notes
1314
+ -----
1315
+ JSON Structure looks like:
1316
+ {
1317
+ "class": "DeleteRequestBody",
1318
+ "externalSourceGUID": "add guid here",
1319
+ "externalSourceName": "add qualified name here",
1320
+ "effectiveTime": "{{$isoTimestamp}}",
1321
+ "forLineage": false,
1322
+ "forDuplicateProcessing": false
1323
+ }
1348
1324
  """
1349
1325
  loop = asyncio.get_event_loop()
1350
1326
  loop.run_until_complete(
1351
- self._async_add_to_project_team(
1352
- project_guid,
1353
- actor_guid,
1354
- team_role,
1355
- effective_from,
1356
- effective_to,
1357
- )
1358
- )
1359
- return
1327
+ self._async_clear_project_dependency(project_guid, upstream_project_guid, body))
1328
+
1329
+ @dynamic_catch
1330
+ async def _async_set_project_hierarchy(self, project_guid: str,
1331
+ parent_project_guid: str,
1332
+ body: dict | NewRelationshipRequestBody = None):
1333
+ """ Set a hierarchy relationship between two projects.
1334
+ Request body is optional. Async version.
1360
1335
 
1361
- async def _async_remove_from_project_team(
1362
- self,
1363
- project_guid: str,
1364
- actor_guid: str,
1365
- ) -> None:
1366
- """Remove an actor from a project. Async version.
1367
1336
 
1368
1337
  Parameters
1369
1338
  ----------
1339
+ parent_project_guid: str
1340
+ The guid of the project depended on.
1370
1341
  project_guid: str
1371
- identity of the project to remove members from.
1372
- actor_guid: str
1373
- identity of the actor to remove.
1342
+ The guid of the dependent project
1343
+ body: dict | NewRelationshipRequestBody, optional, default = None
1344
+ A dict representing the details of the relationship.
1374
1345
 
1375
1346
  Returns
1376
1347
  -------
1377
- None
1348
+ Nothing
1378
1349
 
1379
1350
  Raises
1380
1351
  ------
1381
-
1382
1352
  InvalidParameterException
1383
1353
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1384
1354
  PropertyServerException
@@ -1386,38 +1356,122 @@ class ProjectManager(Client2):
1386
1356
  NotAuthorizedException
1387
1357
  The principle specified by the user_id does not have authorization for the requested action
1388
1358
 
1389
- """
1359
+ Notes
1360
+ -----
1390
1361
 
1362
+ """
1391
1363
  url = (
1392
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1393
- f"members/{actor_guid}/detach"
1364
+ f"{self.project_command_base}/{parent_project_guid}/project-dependencies/{project_guid}/attach"
1394
1365
  )
1366
+ await self._async_new_relationship_request(url, ["ProjectHierarchyProperties"], body)
1367
+ logger.info(f"Project {project_guid} managed by -> {parent_project_guid}")
1395
1368
 
1396
- body = {"class": "NullRequestBody"}
1397
- await self._async_make_request("POST", url, body)
1398
- return
1369
+ @dynamic_catch
1370
+ def set_project_hierarchy(self, project_guid: str,
1371
+ parent_project_guid: str,
1372
+ body: dict | NewRelationshipRequestBody = None):
1373
+ """ Link two dependent digital products. The linked elements are of type DigitalProduct.
1374
+ Request body is optional.
1399
1375
 
1400
- def remove_from_project_team(
1401
- self,
1402
- project_guid: str,
1403
- actor_guid: str,
1404
- ) -> None:
1405
- """Remove an actor from a project.
1376
+ Parameters
1377
+ ----------
1378
+ upstream_digital_prod_guid: str
1379
+ The guid of the first digital product
1380
+ downstream_digital_prod_guid: str
1381
+ The guid of the downstream digital product
1382
+ body: dict | NewRelationshipRequestBody, optional, default = None
1383
+ A structure representing the details of the relationship.
1384
+
1385
+ Returns
1386
+ -------
1387
+ Nothing
1388
+
1389
+ Raises
1390
+ ------
1391
+ InvalidParameterException
1392
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1393
+ PropertyServerException
1394
+ Raised by the server when an issue arises in processing a valid request
1395
+ NotAuthorizedException
1396
+ The principle specified by the user_id does not have authorization for the requested action
1397
+
1398
+ Notes
1399
+ -----
1400
+
1401
+ """
1402
+ loop = asyncio.get_event_loop()
1403
+ loop.run_until_complete(
1404
+ self._async_set_project_hierarchy(project_guid, parent_project_guid,
1405
+ body))
1406
+
1407
+ @dynamic_catch
1408
+ async def _async_clear_project_hierarchy(self, project_guid: str,
1409
+ parent_project_guid: str,
1410
+ body: dict | DeleteRequestBody = None) -> None:
1411
+ """ Unlink hierarchy relationship. Request body is optional. Async version.
1406
1412
 
1407
1413
  Parameters
1408
1414
  ----------
1409
1415
  project_guid: str
1410
- identity of the project.
1411
- actor_guid: str
1412
- identity of the actor to remove.
1416
+ The guid of the dependent project.
1417
+ parent_project_guid: str
1418
+ The guid of the upstream digital project
1419
+ body: dict | DeleteRequestBody, optional, default = None
1420
+ A structure representing the details of the relationship.
1413
1421
 
1414
1422
  Returns
1415
1423
  -------
1416
- None
1424
+ Nothing
1417
1425
 
1418
1426
  Raises
1419
1427
  ------
1428
+ InvalidParameterException
1429
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1430
+ PropertyServerException
1431
+ Raised by the server when an issue arises in processing a valid request
1432
+ NotAuthorizedException
1433
+ The principle specified by the user_id does not have authorization for the requested action
1420
1434
 
1435
+ Notes
1436
+ -----
1437
+ JSON Structure looks like:
1438
+ {
1439
+ "class": "DeleteRequestBody",
1440
+ "externalSourceGUID": "add guid here",
1441
+ "externalSourceName": "add qualified name here",
1442
+ "effectiveTime": "{{$isoTimestamp}}",
1443
+ "forLineage": false,
1444
+ "forDuplicateProcessing": false
1445
+ }
1446
+
1447
+ """
1448
+
1449
+ url = "{self.project_command_base}/{parent_project_guid}/project-dependencies/{project_guid}/detach"
1450
+
1451
+ await self._async_delete_request(url, body)
1452
+ logger.info(
1453
+ f"Detached project {project_guid} from -> {parent_project_guid}")
1454
+
1455
+ @dynamic_catch
1456
+ def clear_project_hierarchy(self, project_guid: str, parent_project_guid: str,
1457
+ body: dict | DeleteRequestBody = None):
1458
+ """ Unlink two dependent projects. Request body is optional.
1459
+
1460
+ Parameters
1461
+ ----------
1462
+ project_guid: str
1463
+ The guid of the dependent project.
1464
+ parent_project_guid: str
1465
+ The guid of the upstream digital project
1466
+ body: dict | DeleteRequestBody, optional, default = None
1467
+ A structure representing the details of the relationship.
1468
+
1469
+ Returns
1470
+ -------
1471
+ Nothing
1472
+
1473
+ Raises
1474
+ ------
1421
1475
  InvalidParameterException
1422
1476
  If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1423
1477
  PropertyServerException
@@ -1425,28 +1479,46 @@ class ProjectManager(Client2):
1425
1479
  NotAuthorizedException
1426
1480
  The principle specified by the user_id does not have authorization for the requested action
1427
1481
 
1482
+ Notes
1483
+ -----
1484
+ JSON Structure looks like:
1485
+ {
1486
+ "class": "DeleteRequestBody",
1487
+ "externalSourceGUID": "add guid here",
1488
+ "externalSourceName": "add qualified name here",
1489
+ "effectiveTime": "{{$isoTimestamp}}",
1490
+ "forLineage": false,
1491
+ "forDuplicateProcessing": false
1492
+ }
1428
1493
  """
1429
1494
  loop = asyncio.get_event_loop()
1430
1495
  loop.run_until_complete(
1431
- self._async_remove_from_project_team(project_guid, actor_guid)
1432
- )
1433
- return
1496
+ self._async_clear_project_hierarchy(project_guid, parent_project_guid, body))
1434
1497
 
1435
- async def _async_setup_project_management_role(
1498
+ @dynamic_catch
1499
+ async def _async_add_to_project_team(
1436
1500
  self,
1437
1501
  project_guid: str,
1438
- project_role_guid: str,
1439
- ) -> None:
1440
- """Create a ProjectManagement relationship between a project and a person role to show that anyone appointed to
1441
- the role is a member of the project. Async version.
1502
+ actor_guid: str,
1503
+ assignment_type: str = None,
1504
+ description: str = None,
1505
+ body: dict | NewRelationshipRequestBody = None
1506
+ ) -> None:
1507
+ """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1508
+ the actor plays in the project. Async version.
1442
1509
 
1443
1510
  Parameters
1444
1511
  ----------
1445
1512
  project_guid: str
1446
- identity of the project.
1447
- project_role_guid: str
1448
- guid of the role to assign to the project.
1449
-
1513
+ identity of the project to update.
1514
+ actor_guid: str
1515
+ identity of the actor to add.
1516
+ team_role: str, optional, defaults to None
1517
+ Name of the role the actor plays in the project.
1518
+ effective_from: str, optional, defaults to None
1519
+ Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1520
+ effective_to: str, optional, defaults to None
1521
+ Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1450
1522
 
1451
1523
  Returns
1452
1524
  -------
@@ -1466,28 +1538,46 @@ class ProjectManager(Client2):
1466
1538
 
1467
1539
  url = (
1468
1540
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1469
- f"project-management-roles/{project_role_guid}/attach"
1541
+ f"members/{actor_guid}/attach"
1470
1542
  )
1543
+ if body is None:
1544
+ body = {
1545
+ "class": "NewRelationshipRequestBody",
1546
+ "properties": {
1547
+ "class": "AssignmentScopeProperties",
1548
+ "description": description,
1549
+ "assignmentType": assignment_type,
1550
+ }
1551
+ }
1552
+ body_s = body_slimmer(body)
1471
1553
 
1472
- body = {"class": "NullRequestBody"}
1473
- await self._async_make_request("POST", url, body)
1474
- return
1554
+ await self._async_new_relationship_request(url, ["AssignmentScopeRelationship"], body)
1555
+ logger.info(f"Added member {actor_guid} to project {project_guid}")
1475
1556
 
1476
- def setup_project_management_role(
1557
+ @dynamic_catch
1558
+ def add_to_project_team(
1477
1559
  self,
1478
1560
  project_guid: str,
1479
- project_role_guid: str,
1480
- ) -> None:
1481
- """Create a ProjectManagement relationship between a project and a person role to show that anyone appointed to
1482
- the role is a member of the project. Async version.
1561
+ actor_guid: str,
1562
+ assignment_type: str = None,
1563
+ description: str = None,
1564
+ body: dict | NewRelationshipRequestBody = None
1565
+ ) -> None:
1566
+ """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1567
+ the actor plays in the project.
1483
1568
 
1484
1569
  Parameters
1485
1570
  ----------
1486
1571
  project_guid: str
1487
- identity of the project.
1488
- project_role_guid: str
1489
- guid of the role to assign to the project.
1490
-
1572
+ identity of the project to update.
1573
+ actor_guid: str
1574
+ identity of the actor to add.
1575
+ team_role: str, optional, defaults to None
1576
+ Name of the role the actor plays in the project.
1577
+ effective_from: str, optional, defaults to None
1578
+ Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1579
+ effective_to: str, optional, defaults to None
1580
+ Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1491
1581
 
1492
1582
  Returns
1493
1583
  -------
@@ -1506,24 +1596,30 @@ class ProjectManager(Client2):
1506
1596
  """
1507
1597
  loop = asyncio.get_event_loop()
1508
1598
  loop.run_until_complete(
1509
- self._async_setup_project_management_role(project_guid, project_role_guid)
1599
+ self._async_add_to_project_team(
1600
+ project_guid,
1601
+ actor_guid,
1602
+ assignment_type,
1603
+ description,
1604
+ body,
1510
1605
  )
1511
- return
1606
+ )
1512
1607
 
1513
- async def _async_clear_project_management_role(
1608
+ @dynamic_catch
1609
+ async def _async_remove_from_project_team(
1514
1610
  self,
1515
1611
  project_guid: str,
1516
- project_role_guid: str,
1517
- ) -> None:
1518
- """Remove a ProjectManagement relationship between a project and a person role. Async version.
1612
+ actor_guid: str,
1613
+ body: dict | DeleteRequestBody = None
1614
+ ) -> None:
1615
+ """Remove an actor from a project. Async version.
1519
1616
 
1520
1617
  Parameters
1521
1618
  ----------
1522
1619
  project_guid: str
1523
- identity of the project.
1524
- project_role_guid: str
1525
- guid of the role to assign to the project.
1526
-
1620
+ identity of the project to remove members from.
1621
+ actor_guid: str
1622
+ identity of the actor to remove.
1527
1623
 
1528
1624
  Returns
1529
1625
  -------
@@ -1543,27 +1639,27 @@ class ProjectManager(Client2):
1543
1639
 
1544
1640
  url = (
1545
1641
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1546
- f"project-management-roles/{project_role_guid}/detach"
1642
+ f"members/{actor_guid}/detach"
1547
1643
  )
1548
1644
 
1549
- body = {"class": "NullRequestBody"}
1550
- await self._async_make_request("POST", url, body)
1551
- return
1645
+ await self._async_delete_request(url, body)
1646
+ logger.info(f"Removed member {actor_guid} from project {project_guid}")
1552
1647
 
1553
- def clear_project_management_role(
1648
+ @dynamic_catch
1649
+ def remove_from_project_team(
1554
1650
  self,
1555
1651
  project_guid: str,
1556
- project_role_guid: str,
1557
- ) -> None:
1558
- """Clear a ProjectManagement relationship between a project and a person role.
1652
+ actor_guid: str,
1653
+ body: dict | DeleteRequestBody = None
1654
+ ) -> None:
1655
+ """Remove an actor from a project.
1559
1656
 
1560
1657
  Parameters
1561
1658
  ----------
1562
1659
  project_guid: str
1563
1660
  identity of the project.
1564
- project_role_guid: str
1565
- guid of the role to assign to the project.
1566
-
1661
+ actor_guid: str
1662
+ identity of the actor to remove.
1567
1663
 
1568
1664
  Returns
1569
1665
  -------
@@ -1582,9 +1678,34 @@ class ProjectManager(Client2):
1582
1678
  """
1583
1679
  loop = asyncio.get_event_loop()
1584
1680
  loop.run_until_complete(
1585
- self._async_clear_project_management_role(project_guid, project_role_guid)
1586
- )
1587
- return
1681
+ self._async_remove_from_project_team(project_guid, actor_guid, body)
1682
+ )
1683
+
1684
+ @dynamic_catch
1685
+ async def _async_create_task_for_project(
1686
+ self,
1687
+ project_guid: str,
1688
+ body: dict | NewElementRequestBody
1689
+ ) -> str:
1690
+ """Create a new task for a project. Async version."""
1691
+
1692
+ url = f"{self.project_command_base}/{project_guid}/task"
1693
+ response = await self._async_new_element_request(url, body)
1694
+ logger.info(f"Created task for project {project_guid}")
1695
+ return response
1696
+
1697
+ @dynamic_catch
1698
+ def create_task_for_project(
1699
+ self,
1700
+ project_guid: str,
1701
+ body: dict | NewElementRequestBody
1702
+ ) -> str:
1703
+ """Create a new task for a project. """
1704
+ loop = asyncio.get_event_loop()
1705
+ resp = loop.run_until_complete(
1706
+ self._async_create_task_for_project(project_guid, body)
1707
+ )
1708
+ return resp
1588
1709
 
1589
1710
 
1590
1711
  if __name__ == "__main__":