pyegeria 0.7.18__tar.gz → 0.7.19__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.
- {pyegeria-0.7.18 → pyegeria-0.7.19}/PKG-INFO +1 -1
- pyegeria-0.7.19/examples/widgets/cat/get_project_dependencies.py +135 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/get_project_structure.py +3 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_todos.py +25 -12
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/egeria.py +19 -4
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/egeria_cat.py +20 -5
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/egeria_my.py +8 -1
- pyegeria-0.7.19/examples/widgets/my/todo_actions.py +160 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/my_profile_omvs.py +12 -13
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyproject.toml +7 -1
- {pyegeria-0.7.18 → pyegeria-0.7.19}/LICENSE +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/README.md +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/doc_samples/Create_Collection_Sample.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/doc_samples/Create_Sustainability_Collection_Sample.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/README.md +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/get_asset_graph.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/get_collection.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/get_tech_type_elements.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/get_tech_type_template.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_assets.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_cert_types.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_glossary.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_projects.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_relationships.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cat/list_tech_types.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/egeria_ops.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/egeria_tech.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/cli/ops_config.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/README.md +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/list_my_profile.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/list_my_roles.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/monitor_my_todos.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/monitor_open_todos.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/my/my_profile_actions.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/README.md +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/engine_actions.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/integration_daemon_actions.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/list_catalog_targets.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/load_archive.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_asset_events.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_coco_status.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_engine_activity.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_gov_eng_status.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_integ_daemon_status.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_platform_status.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_server_list.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/monitor_server_status.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/refresh_integration_daemon.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/ops/restart_integration_daemon.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/README.md +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/get_element_info.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/get_guid_info.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/get_tech_details.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_asset_types.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_elements.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_registered_services.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_related_specification.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_relationship_types.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_tech_templates.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/list_valid_metadata_values.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/examples/widgets/tech/x_list_related_elements.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/Xloaded_resources_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/__init__.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/_client.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/_deprecated_gov_engine.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/_exceptions.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/_globals.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/_validators.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/action_author_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/asset_catalog_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/automated_curation_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/classification_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/collection_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/core_omag_server_config.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/create_tech_guid_lists.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/feedback_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/full_omag_server_config.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/glossary_browser_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/glossary_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/platform_services.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/project_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/registered_info.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/runtime_manager_omvs.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/server_operations.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/utils.py +0 -0
- {pyegeria-0.7.18 → pyegeria-0.7.19}/pyegeria/valid_metadata_omvs.py +0 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
4
|
+
Copyright Contributors to the ODPi Egeria project.
|
5
|
+
|
6
|
+
A simple viewer for project dependencies - provide the root and we display the dependency tree
|
7
|
+
|
8
|
+
"""
|
9
|
+
|
10
|
+
import argparse
|
11
|
+
import os
|
12
|
+
|
13
|
+
from rich import print
|
14
|
+
from rich.console import Console
|
15
|
+
from rich.markdown import Markdown
|
16
|
+
from rich.panel import Panel
|
17
|
+
from rich.prompt import Prompt
|
18
|
+
from rich.tree import Tree
|
19
|
+
|
20
|
+
from pyegeria import (ProjectManager, UserNotAuthorizedException, PropertyServerException, InvalidParameterException)
|
21
|
+
from pyegeria._exceptions import (print_exception_response, )
|
22
|
+
|
23
|
+
disable_ssl_warnings = True
|
24
|
+
EGERIA_METADATA_STORE = os.environ.get("EGERIA_METADATA_STORE", "active-metadata-store")
|
25
|
+
EGERIA_KAFKA_ENDPOINT = os.environ.get('KAFKA_ENDPOINT', 'localhost:9092')
|
26
|
+
EGERIA_PLATFORM_URL = os.environ.get('EGERIA_PLATFORM_URL', 'https://localhost:9443')
|
27
|
+
EGERIA_VIEW_SERVER = os.environ.get('VIEW_SERVER', 'view-server')
|
28
|
+
EGERIA_VIEW_SERVER_URL = os.environ.get('EGERIA_VIEW_SERVER_URL', 'https://localhost:9443')
|
29
|
+
EGERIA_INTEGRATION_DAEMON = os.environ.get('INTEGRATION_DAEMON', 'integration-daemon')
|
30
|
+
EGERIA_ADMIN_USER = os.environ.get('ADMIN_USER', 'garygeeke')
|
31
|
+
EGERIA_ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', 'secret')
|
32
|
+
EGERIA_USER = os.environ.get('EGERIA_USER', 'erinoverview')
|
33
|
+
EGERIA_USER_PASSWORD = os.environ.get('EGERIA_USER_PASSWORD', 'secret')
|
34
|
+
EGERIA_JUPYTER = bool(os.environ.get('EGERIA_JUPYTER', 'False'))
|
35
|
+
EGERIA_WIDTH = int(os.environ.get('EGERIA_WIDTH', '200'))
|
36
|
+
|
37
|
+
|
38
|
+
def project_dependency_viewer(root: str, server_name: str, platform_url: str, user: str, user_password: str,
|
39
|
+
jupyter: bool = EGERIA_JUPYTER, width: int = EGERIA_WIDTH, timeout: int = 30):
|
40
|
+
""" A simple collection viewer"""
|
41
|
+
|
42
|
+
def walk_project_hierarchy(project_client: ProjectManager, project_name: str, tree: Tree,
|
43
|
+
root: bool = False) -> None:
|
44
|
+
"""Recursively build a Tree with collection contents."""
|
45
|
+
t = None
|
46
|
+
style = "bright_white on black"
|
47
|
+
|
48
|
+
project = project_client.get_projects_by_name(project_name)
|
49
|
+
if type(project) is list:
|
50
|
+
proj_guid = project[0]['elementHeader']['guid']
|
51
|
+
proj_props = project[0]['properties']
|
52
|
+
|
53
|
+
proj_type = proj_props.get('typeName', '---')
|
54
|
+
proj_unique = proj_props.get('qualifiedName', '---')
|
55
|
+
proj_identifier = proj_props.get('identifier', '---')
|
56
|
+
proj_name = proj_props.get('name', '---')
|
57
|
+
proj_desc = proj_props.get('description', '---')
|
58
|
+
proj_status = proj_props.get('projectStatus', '---')
|
59
|
+
proj_priority = proj_props.get('priority', '---')
|
60
|
+
proj_start = proj_props.get('startDate', '---')[:-10]
|
61
|
+
proj_props_md = (f"* Name: {proj_name}\n"
|
62
|
+
f"* Identifier: {proj_identifier}\n"
|
63
|
+
f"* Type: {proj_type}\n"
|
64
|
+
f"* Status: {proj_status}\n"
|
65
|
+
f"* priority: {proj_priority}\n"
|
66
|
+
f"* Start: {proj_start}\n"
|
67
|
+
f"* Description: {proj_desc}\n"
|
68
|
+
f"* GUID: {proj_guid}")
|
69
|
+
else:
|
70
|
+
return
|
71
|
+
|
72
|
+
team = project_client.get_project_team(proj_guid)
|
73
|
+
member_md = ""
|
74
|
+
if type(team) is list:
|
75
|
+
for member in team:
|
76
|
+
member_guid = member['member']['guid']
|
77
|
+
member_unique = member['member']['uniqueName']
|
78
|
+
member_md += f"* Member Unique Name: {member_unique}\n* Member GUID: {member_guid}"
|
79
|
+
proj_props_md += f"\n### Team Members\n {member_md}"
|
80
|
+
|
81
|
+
proj_props_out = Markdown(proj_props_md)
|
82
|
+
p = Panel.fit(proj_props_out, style=style, title=project_name)
|
83
|
+
t = tree.add(p)
|
84
|
+
|
85
|
+
linked_projects = project_client.get_linked_projects(proj_guid)
|
86
|
+
if type(linked_projects) is list:
|
87
|
+
for proj in linked_projects:
|
88
|
+
child_md = ""
|
89
|
+
child_guid = proj['elementHeader']['guid']
|
90
|
+
child_name = proj['properties']['name']
|
91
|
+
relationship = proj['relatedElement']['relationshipHeader']['type']['typeName']
|
92
|
+
if relationship != 'ProjectDependency':
|
93
|
+
continue
|
94
|
+
walk_project_hierarchy(project_client, child_name, t)
|
95
|
+
|
96
|
+
else:
|
97
|
+
return t
|
98
|
+
|
99
|
+
try:
|
100
|
+
console = Console(width=width, force_terminal=not jupyter)
|
101
|
+
tree = Tree(f"[bold bright green on black]{root}", guide_style="bold bright_blue")
|
102
|
+
p_client = ProjectManager(server_name, platform_url, user_id=user)
|
103
|
+
|
104
|
+
token1 = p_client.create_egeria_bearer_token(user, user_password)
|
105
|
+
|
106
|
+
walk_project_hierarchy(p_client, root, tree, root=True)
|
107
|
+
print(tree)
|
108
|
+
|
109
|
+
except (InvalidParameterException, PropertyServerException, UserNotAuthorizedException) as e:
|
110
|
+
print_exception_response(e)
|
111
|
+
|
112
|
+
|
113
|
+
def main():
|
114
|
+
parser = argparse.ArgumentParser()
|
115
|
+
|
116
|
+
parser.add_argument("--server", help="Name of the server to display status for")
|
117
|
+
parser.add_argument("--url", help="URL Platform to connect to")
|
118
|
+
parser.add_argument("--userid", help="User Id")
|
119
|
+
parser.add_argument("--password", help="User Password")
|
120
|
+
args = parser.parse_args()
|
121
|
+
|
122
|
+
server = args.server if args.server is not None else EGERIA_VIEW_SERVER
|
123
|
+
url = args.url if args.url is not None else EGERIA_PLATFORM_URL
|
124
|
+
userid = args.userid if args.userid is not None else EGERIA_USER
|
125
|
+
user_pass = args.password if args.password is not None else EGERIA_USER_PASSWORD
|
126
|
+
|
127
|
+
try:
|
128
|
+
root_project = Prompt.ask("Enter the Root Project to start from:", default="Sustainability Campaign")
|
129
|
+
project_dependency_viewer(root_project, server, url, userid, user_pass)
|
130
|
+
except (KeyboardInterrupt):
|
131
|
+
pass
|
132
|
+
|
133
|
+
|
134
|
+
if __name__ == "__main__":
|
135
|
+
main()
|
@@ -88,6 +88,9 @@ def project_structure_viewer(root: str, server_name: str, platform_url: str, use
|
|
88
88
|
child_md = ""
|
89
89
|
child_guid = proj['elementHeader']['guid']
|
90
90
|
child_name = proj['properties']['name']
|
91
|
+
relationship = proj['relatedElement']['relationshipHeader']['type']['typeName']
|
92
|
+
if relationship != 'ProjectHierarchy':
|
93
|
+
continue
|
91
94
|
walk_project_hierarchy(project_client, child_name, t)
|
92
95
|
|
93
96
|
else:
|
@@ -15,6 +15,7 @@ import time
|
|
15
15
|
|
16
16
|
from rich import box
|
17
17
|
from rich.console import Console
|
18
|
+
from rich.markdown import Markdown
|
18
19
|
from rich.prompt import Prompt
|
19
20
|
from rich.table import Table
|
20
21
|
|
@@ -40,7 +41,7 @@ EGERIA_USER_PASSWORD = os.environ.get('EGERIA_USER_PASSWORD', 'secret')
|
|
40
41
|
EGERIA_JUPYTER = bool(os.environ.get('EGERIA_JUPYTER', 'False'))
|
41
42
|
EGERIA_WIDTH = int(os.environ.get('EGERIA_WIDTH', '200'))
|
42
43
|
|
43
|
-
def display_to_dos(search_string: str, server: str, url: str, username: str, user_pass:str,
|
44
|
+
def display_to_dos(search_string: str, status_filter: str, server: str, url: str, username: str, user_pass:str,
|
44
45
|
jupyter:bool=EGERIA_JUPYTER, width:int = EGERIA_WIDTH):
|
45
46
|
|
46
47
|
m_client = MyProfile(server, url, user_id=username)
|
@@ -60,15 +61,16 @@ def display_to_dos(search_string: str, server: str, url: str, username: str, use
|
|
60
61
|
|
61
62
|
table.add_column("Name")
|
62
63
|
table.add_column("Type Name")
|
63
|
-
|
64
|
+
table.add_column("GUID", no_wrap=True)
|
64
65
|
table.add_column("Created")
|
65
66
|
table.add_column("Priority")
|
66
67
|
table.add_column("Due")
|
67
68
|
table.add_column("Completion")
|
68
69
|
table.add_column("Status")
|
69
70
|
table.add_column("Sponsor")
|
71
|
+
table.add_column("Assigned")
|
70
72
|
|
71
|
-
todo_items = m_client.find_to_do(search_string)
|
73
|
+
todo_items = m_client.find_to_do(search_string,status = status_filter)
|
72
74
|
|
73
75
|
if type(todo_items) is str:
|
74
76
|
name = " "
|
@@ -80,18 +82,27 @@ def display_to_dos(search_string: str, server: str, url: str, username: str, use
|
|
80
82
|
|
81
83
|
status = " "
|
82
84
|
sponsor = " "
|
85
|
+
assigned_out = ''
|
83
86
|
else:
|
84
87
|
for item in todo_items:
|
88
|
+
guid = item['elementHeader']['guid']
|
85
89
|
props = item["properties"]
|
86
90
|
name = props["name"]
|
87
91
|
type_name = props.get("toDoType", " ")
|
88
|
-
created = props.get("creationTime", " ")
|
92
|
+
created = props.get("creationTime", " ")[:-19]
|
89
93
|
priority = str(props.get("priority", " "))
|
90
|
-
due = props.get("dueTime", "
|
91
|
-
completed = props.get("completionTime", "
|
92
|
-
status = props.get("
|
93
|
-
|
94
|
-
|
94
|
+
due = props.get("dueTime", " ")[:-19]
|
95
|
+
completed = props.get("completionTime", " ")[:-10]
|
96
|
+
status = props.get("toDoStatus")
|
97
|
+
|
98
|
+
assigned_out = ''
|
99
|
+
assigned_actors = item["assignedActors"]
|
100
|
+
if type(assigned_actors) is list:
|
101
|
+
assigned_md = ''
|
102
|
+
for actor in assigned_actors:
|
103
|
+
assigned_md += f"* {actor['uniqueName'].split(',')[0]}\n"
|
104
|
+
assigned_out = Markdown(assigned_md)
|
105
|
+
|
95
106
|
sponsor = "erinoverview"
|
96
107
|
if status in ("WAITING", "OPEN"):
|
97
108
|
status = f"[yellow]{status}"
|
@@ -101,7 +112,7 @@ def display_to_dos(search_string: str, server: str, url: str, username: str, use
|
|
101
112
|
status = f"[red]{status}"
|
102
113
|
|
103
114
|
table.add_row(
|
104
|
-
name, type_name, created, priority, due, completed, status, sponsor
|
115
|
+
name, type_name, guid, created, priority, due, completed, status, sponsor, assigned_out
|
105
116
|
)
|
106
117
|
|
107
118
|
m_client.close_session()
|
@@ -140,8 +151,10 @@ def main():
|
|
140
151
|
userid = args.userid if args.userid is not None else EGERIA_USER
|
141
152
|
user_pass = args.password if args.password is not None else EGERIA_USER_PASSWORD
|
142
153
|
try:
|
143
|
-
search_string = Prompt.ask("Enter the ToDo you are searching for
|
144
|
-
|
154
|
+
search_string = Prompt.ask("Enter the ToDo you are searching for", default="*")
|
155
|
+
status_filter = Prompt.ask("Enter an optional status filter ['OPEN','IN_PROGRESS','WAITING','COMPLETE',"
|
156
|
+
"'ABANDONED', 'None']", default=None)
|
157
|
+
display_to_dos(search_string, status_filter,server, url, userid, user_pass)
|
145
158
|
except KeyboardInterrupt:
|
146
159
|
pass
|
147
160
|
|
@@ -13,6 +13,7 @@ import click
|
|
13
13
|
from trogon import tui
|
14
14
|
|
15
15
|
from examples.widgets.cat.get_project_structure import project_structure_viewer
|
16
|
+
from examples.widgets.cat.get_project_dependencies import project_dependency_viewer
|
16
17
|
from examples.widgets.cat.list_cert_types import display_certifications
|
17
18
|
from examples.widgets.cat.get_asset_graph import asset_viewer
|
18
19
|
from examples.widgets.cat.get_collection import collection_viewer
|
@@ -22,7 +23,7 @@ from examples.widgets.cat.list_assets import display_assets
|
|
22
23
|
from examples.widgets.cat.list_glossary import display_glossary_terms
|
23
24
|
from examples.widgets.cat.list_projects import display_project_list
|
24
25
|
from examples.widgets.cat.list_tech_types import display_tech_types
|
25
|
-
from examples.widgets.cat.list_todos import display_to_dos
|
26
|
+
from examples.widgets.cat.list_todos import display_to_dos as list_todos
|
26
27
|
from examples.widgets.cli.ops_config import Config
|
27
28
|
from examples.widgets.cat.list_relationships import list_relationships
|
28
29
|
|
@@ -415,11 +416,23 @@ def show_certification_types(ctx, search_string):
|
|
415
416
|
help="Enter the root project to start from")
|
416
417
|
@click.pass_context
|
417
418
|
def show_project_structure(ctx, project):
|
418
|
-
"""Show the structure of the project starting from a root project"""
|
419
|
+
"""Show the organization structure of the project starting from a root project"""
|
419
420
|
c = ctx.obj
|
420
421
|
project_structure_viewer(project, c.view_server, c.view_server_url, c.userid,
|
421
422
|
c.password, c.jupyter, c.width, c.timeout)
|
422
423
|
|
424
|
+
@show.command('project-dependencies')
|
425
|
+
@click.option('--project', default = 'Clinical Trials Management',
|
426
|
+
help="Enter the root project to start from")
|
427
|
+
@click.pass_context
|
428
|
+
def show_project_dependencies(ctx, project):
|
429
|
+
"""Show the dependencies of a project starting from a root project"""
|
430
|
+
c = ctx.obj
|
431
|
+
project_dependency_viewer(project, c.view_server, c.view_server_url, c.userid,
|
432
|
+
c.password, c.jupyter, c.width, c.timeout)
|
433
|
+
|
434
|
+
|
435
|
+
|
423
436
|
@show.command('relationships')
|
424
437
|
@click.option('--relationship', default = 'Certification',
|
425
438
|
help="Relationship type name to search for.")
|
@@ -500,11 +513,13 @@ def show_projects(ctx, search_string):
|
|
500
513
|
@show.command('to-dos')
|
501
514
|
@click.option('--search-string', default='*',
|
502
515
|
help='View the list of To-Do items')
|
516
|
+
@click.option('--status', type=click.Choice(['OPEN','IN_PROGRESS','WAITING','COMPLETE', 'ABANDONED', 'None'],
|
517
|
+
case_sensitive='False'), help = 'Enter an optional status filter', required=False, default=None)
|
503
518
|
@click.pass_context
|
504
|
-
def show_todos(ctx, search_string):
|
519
|
+
def show_todos(ctx, search_string, status):
|
505
520
|
"""Display a tree graph of information about an asset """
|
506
521
|
c = ctx.obj
|
507
|
-
|
522
|
+
list_todos(search_string, status, c.view_server, c.view_server_url, c.userid,
|
508
523
|
c.password, c.jupyter, c.width)
|
509
524
|
|
510
525
|
|
@@ -20,8 +20,9 @@ from examples.widgets.cat.list_assets import display_assets
|
|
20
20
|
from examples.widgets.cat.list_glossary import display_glossary_terms
|
21
21
|
from examples.widgets.cat.list_tech_types import display_tech_types
|
22
22
|
from examples.widgets.cat.list_projects import display_project_list
|
23
|
-
from examples.widgets.cat.list_todos import display_to_dos
|
23
|
+
from examples.widgets.cat.list_todos import display_to_dos as list_todos
|
24
24
|
from examples.widgets.cat.get_project_structure import project_structure_viewer
|
25
|
+
from examples.widgets.cat.get_project_dependencies import project_dependency_viewer
|
25
26
|
from examples.widgets.cat.list_cert_types import display_certifications
|
26
27
|
from examples.widgets.cat.list_relationships import list_relationships
|
27
28
|
|
@@ -207,11 +208,23 @@ def show_certification_types(ctx, search_string):
|
|
207
208
|
help="Enter the root project to start from")
|
208
209
|
@click.pass_context
|
209
210
|
def show_project_structure(ctx, project):
|
210
|
-
"""Show the structure of the project starting from a root project"""
|
211
|
+
"""Show the organization structure of the project starting from a root project"""
|
211
212
|
c = ctx.obj
|
212
213
|
project_structure_viewer(project, c.view_server, c.view_server_url, c.userid,
|
213
214
|
c.password, c.jupyter, c.width, c.timeout)
|
214
215
|
|
216
|
+
@show.command('project-dependencies')
|
217
|
+
@click.option('--project', default = 'Clinical Trials Management',
|
218
|
+
help="Enter the root project to start from")
|
219
|
+
@click.pass_context
|
220
|
+
def show_project_dependencies(ctx, project):
|
221
|
+
"""Show the dependencies of a project starting from a root project"""
|
222
|
+
c = ctx.obj
|
223
|
+
project_dependency_viewer(project, c.view_server, c.view_server_url, c.userid,
|
224
|
+
c.password, c.jupyter, c.width, c.timeout)
|
225
|
+
|
226
|
+
|
227
|
+
|
215
228
|
@show.command('relationships')
|
216
229
|
@click.option('--relationship', default = 'Certification',
|
217
230
|
help="Relationship type name to search for.")
|
@@ -227,12 +240,14 @@ def show_relationships(ctx, relationship):
|
|
227
240
|
@show.command('to-dos')
|
228
241
|
@click.option('--search-string', default='*',
|
229
242
|
help='View the list of To-Do items')
|
243
|
+
@click.option('--status', type=click.Choice(['OPEN','IN_PROGRESS','WAITING','COMPLETE', 'ABANDONED', 'None'],
|
244
|
+
case_sensitive='False'), help = 'Enter an optional status filter', required=False, default=None)
|
230
245
|
@click.pass_context
|
231
|
-
def show_todos(ctx, search_string):
|
246
|
+
def show_todos(ctx, search_string, status):
|
232
247
|
"""Display a tree graph of information about an asset """
|
233
248
|
c = ctx.obj
|
234
|
-
|
235
|
-
|
249
|
+
list_todos(search_string, status, c.view_server, c.view_server_url, c.userid,
|
250
|
+
c.password, c.jupyter, c.width)
|
236
251
|
|
237
252
|
#
|
238
253
|
# Tell
|
@@ -12,13 +12,15 @@ This is an emerging capability based on the **click** package. Feedback welcome!
|
|
12
12
|
import click
|
13
13
|
from trogon import tui
|
14
14
|
|
15
|
+
from examples.widgets.cat import list_todos
|
15
16
|
from examples.widgets.cli.ops_config import Config
|
16
17
|
|
17
18
|
from examples.widgets.my.monitor_open_todos import display_todos
|
18
19
|
from examples.widgets.my.monitor_my_todos import display_my_todos
|
19
20
|
from examples.widgets.my.list_my_profile import display_my_profile
|
20
21
|
from examples.widgets.my.list_my_roles import display_my_roles
|
21
|
-
|
22
|
+
from examples.widgets.my.todo_actions import create_todo, delete_todo, change_todo_status, mark_todo_complete, \
|
23
|
+
reassign_todo
|
22
24
|
|
23
25
|
|
24
26
|
# class Config(object):
|
@@ -148,6 +150,11 @@ def show_open_todos(ctx):
|
|
148
150
|
def tell(ctx):
|
149
151
|
"""Perform actions an Egeria Objects"""
|
150
152
|
pass
|
153
|
+
tell.add_command(create_todo)
|
154
|
+
tell.add_command(delete_todo)
|
155
|
+
tell.add_command(change_todo_status)
|
156
|
+
tell.add_command(mark_todo_complete)
|
157
|
+
tell.add_command(reassign_todo)
|
151
158
|
|
152
159
|
|
153
160
|
if __name__ == '__main__':
|
@@ -0,0 +1,160 @@
|
|
1
|
+
"""
|
2
|
+
SPDX-License-Identifier: Apache-2.0
|
3
|
+
Copyright Contributors to the ODPi Egeria project.
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
Execute ToDo actions.
|
8
|
+
|
9
|
+
"""
|
10
|
+
import json
|
11
|
+
import time
|
12
|
+
|
13
|
+
import click
|
14
|
+
# from ops_config import Config, pass_config
|
15
|
+
from pyegeria import ServerOps, AutomatedCuration, INTEGRATION_GUIDS, MyProfile
|
16
|
+
from pyegeria._exceptions import (
|
17
|
+
InvalidParameterException,
|
18
|
+
PropertyServerException,
|
19
|
+
UserNotAuthorizedException,
|
20
|
+
print_exception_response,
|
21
|
+
)
|
22
|
+
|
23
|
+
erins_guid = "a588fb08-ae09-4415-bd5d-991882ceacba"
|
24
|
+
peter_guid ="a187bc48-8154-491f-97b4-a2f3c3f1a00e"
|
25
|
+
tanya_guid ="26ec1614-bede-4b25-a2a3-f8ed26db3aaa"
|
26
|
+
|
27
|
+
|
28
|
+
@click.command('create-todo')
|
29
|
+
@click.option('--name',prompt='Todo Name',help='Name of Todo', required=True)
|
30
|
+
@click.option('--description',prompt='Description',help='Brief description of To Do item', required=True)
|
31
|
+
@click.option('--type',prompt='Todo Type',help='Type of Todo', required=True, default = 'forMe')
|
32
|
+
@click.option('--priority',prompt='Todo Priority',type = int, help='Priority of Todo', required=True, default= 0)
|
33
|
+
@click.option('--due',prompt='Due Date',help='Due date of Todo (yyyy-mm-dd)', required=True)
|
34
|
+
@click.option('--assigned-to',prompt='Assigned to',help='Party the Todo is assigned to', required=True,
|
35
|
+
default = 'Peter')
|
36
|
+
@click.pass_context
|
37
|
+
def create_todo(ctx,name,description,type,priority,due,assigned_to):
|
38
|
+
"""Create a new ToDo item"""
|
39
|
+
c = ctx.obj
|
40
|
+
m_client = MyProfile(c.view_server, c.view_server_url, user_id=c.userid, user_pwd=c.password)
|
41
|
+
token = m_client.create_egeria_bearer_token()
|
42
|
+
try:
|
43
|
+
body = {
|
44
|
+
"properties": {
|
45
|
+
"class": "ToDoProperties",
|
46
|
+
"qualifiedName": f"{name}-{time.asctime()}",
|
47
|
+
"name": name,
|
48
|
+
"description": description,
|
49
|
+
"toDoType": type,
|
50
|
+
"priority": priority,
|
51
|
+
"dueTime": due,
|
52
|
+
"status": "OPEN"
|
53
|
+
},
|
54
|
+
"assignToActorGUID": peter_guid
|
55
|
+
}
|
56
|
+
|
57
|
+
resp = m_client.create_to_do(body)
|
58
|
+
# if type(resp) is str:
|
59
|
+
click.echo(f"Response was {resp}")
|
60
|
+
# elif type(resp) is dict:
|
61
|
+
# click.echo(json.dumps(resp), indent = 2)
|
62
|
+
|
63
|
+
except (InvalidParameterException, PropertyServerException) as e:
|
64
|
+
print_exception_response(e)
|
65
|
+
finally:
|
66
|
+
m_client.close_session()
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
@click.command('delete-todo')
|
71
|
+
@click.argument('todo-guid')
|
72
|
+
@click.pass_context
|
73
|
+
def delete_todo(ctx, todo_guid):
|
74
|
+
"""Delete the todo item specified """
|
75
|
+
c = ctx.obj
|
76
|
+
m_client = MyProfile(c.view_server, c.view_server_url, user_id=c.userid, user_pwd=c.password)
|
77
|
+
token = m_client.create_egeria_bearer_token()
|
78
|
+
try:
|
79
|
+
m_client.delete_to_do(todo_guid)
|
80
|
+
|
81
|
+
click.echo(f"Deleted Todo item {todo_guid}")
|
82
|
+
|
83
|
+
except (InvalidParameterException, PropertyServerException) as e:
|
84
|
+
print_exception_response(e)
|
85
|
+
finally:
|
86
|
+
m_client.close_session()
|
87
|
+
|
88
|
+
@click.command('change-todo-status')
|
89
|
+
@click.argument('todo-guid')
|
90
|
+
@click.option('--new-status', type=click.Choice(['OPEN','IN_PROGRESS','WAITING','COMPLETE', 'ABANDONED'],
|
91
|
+
case_sensitive='False'), help = 'Enter the new ToDo item status', required=True)
|
92
|
+
@click.pass_context
|
93
|
+
def change_todo_status(ctx, todo_guid, new_status):
|
94
|
+
"""Update a ToDo item status"""
|
95
|
+
c = ctx.obj
|
96
|
+
m_client = MyProfile(c.view_server, c.view_server_url, user_id=c.userid, user_pwd=c.password)
|
97
|
+
token = m_client.create_egeria_bearer_token()
|
98
|
+
try:
|
99
|
+
|
100
|
+
|
101
|
+
body = {
|
102
|
+
"properties": {
|
103
|
+
"class": "ToDoProperties",
|
104
|
+
"toDoStatus": new_status
|
105
|
+
},
|
106
|
+
}
|
107
|
+
m_client.update_to_do(todo_guid, body, is_merge_update=True)
|
108
|
+
|
109
|
+
click.echo(f"Marked todo item {todo_guid} as complete.")
|
110
|
+
|
111
|
+
except (InvalidParameterException, PropertyServerException) as e:
|
112
|
+
print_exception_response(e)
|
113
|
+
finally:
|
114
|
+
m_client.close_session()
|
115
|
+
|
116
|
+
@click.command('mark-todo-complete')
|
117
|
+
@click.argument('todo-guid')
|
118
|
+
@click.pass_context
|
119
|
+
def mark_todo_complete(ctx, todo_guid):
|
120
|
+
"""Mark the specified todo as complete"""
|
121
|
+
try:
|
122
|
+
c = ctx.obj
|
123
|
+
m_client = MyProfile(c.view_server, c.view_server_url, user_id=c.userid, user_pwd=c.password)
|
124
|
+
token = m_client.create_egeria_bearer_token()
|
125
|
+
body = {
|
126
|
+
"properties": {
|
127
|
+
"class": "ToDoProperties",
|
128
|
+
"completionTime" : time.asctime(),
|
129
|
+
"toDoStatus": "COMPLETE"
|
130
|
+
},
|
131
|
+
}
|
132
|
+
m_client.update_to_do(todo_guid, body, is_merge_update=True)
|
133
|
+
|
134
|
+
click.echo(f"Marked todo item {todo_guid} as complete.")
|
135
|
+
|
136
|
+
except (InvalidParameterException, PropertyServerException) as e:
|
137
|
+
print_exception_response(e)
|
138
|
+
finally:
|
139
|
+
m_client.close_session()
|
140
|
+
|
141
|
+
|
142
|
+
@click.command('reassign-todo')
|
143
|
+
@click.argument('todo-guid')
|
144
|
+
@click.argument('new-actor-guid')
|
145
|
+
@click.pass_context
|
146
|
+
def reassign_todo(ctx, todo_guid, new_actor_guid):
|
147
|
+
"""Reassign ToDo item to new actor"""
|
148
|
+
c = ctx.obj
|
149
|
+
m_client = MyProfile(c.view_server, c.view_server_url, user_id=c.userid, user_pwd=c.password)
|
150
|
+
token = m_client.create_egeria_bearer_token()
|
151
|
+
try:
|
152
|
+
|
153
|
+
m_client.reassign_to_do(todo_guid, new_actor_guid)
|
154
|
+
|
155
|
+
click.echo(f"Reassigned Todo item {todo_guid} to {new_actor_guid}")
|
156
|
+
|
157
|
+
except (InvalidParameterException, PropertyServerException) as e:
|
158
|
+
print_exception_response(e)
|
159
|
+
finally:
|
160
|
+
m_client.close_session()
|
@@ -392,7 +392,7 @@ class MyProfile(Client):
|
|
392
392
|
response = await self._async_make_request("POST", url, body)
|
393
393
|
return response.json().get("guid", "No guid returned")
|
394
394
|
|
395
|
-
def create_to_do(self, body: dict, server_name: str = None) ->
|
395
|
+
def create_to_do(self, body: dict, server_name: str = None) -> str:
|
396
396
|
""" Create a To-Do item.
|
397
397
|
Parameters
|
398
398
|
----------
|
@@ -534,10 +534,12 @@ class MyProfile(Client):
|
|
534
534
|
if server_name is None:
|
535
535
|
server_name = self.server_name
|
536
536
|
|
537
|
+
is_merge_update_t = str(is_merge_update).lower()
|
538
|
+
|
537
539
|
validate_name(todo_guid)
|
538
540
|
|
539
541
|
url = (f"{self.my_profile_command_root}/{server_name}/api/open-metadata/my-profile/to-dos/"
|
540
|
-
f"{todo_guid}?
|
542
|
+
f"{todo_guid}?isMergeUpdate={is_merge_update_t}")
|
541
543
|
|
542
544
|
await self._async_make_request("POST", url, body)
|
543
545
|
return
|
@@ -576,14 +578,12 @@ class MyProfile(Client):
|
|
576
578
|
loop.run_until_complete(self._async_update_to_do(todo_guid, body, is_merge_update, server_name))
|
577
579
|
return
|
578
580
|
|
579
|
-
async def _async_delete_to_do(self, todo_guid: str,
|
581
|
+
async def _async_delete_to_do(self, todo_guid: str, server_name: str = None) -> None:
|
580
582
|
""" Delete a To-Do item. Async version.
|
581
583
|
Parameters
|
582
584
|
----------
|
583
585
|
todo_guid: str
|
584
586
|
Identifier of the To-Do item.
|
585
|
-
status: str
|
586
|
-
Filter items to match this status. Defaults to "OPEN"
|
587
587
|
server_name : str, optional
|
588
588
|
The name of the server where the to-do item will be created. If not provided,
|
589
589
|
the default server name associated with the instance of the class will be used.
|
@@ -606,21 +606,19 @@ class MyProfile(Client):
|
|
606
606
|
server_name = self.server_name
|
607
607
|
|
608
608
|
validate_name(todo_guid)
|
609
|
-
body = {"status": status}
|
610
609
|
|
611
|
-
url = f"{self.my_profile_command_root}/{server_name}/api/open-metadata/my-profile/to-dos/{todo_guid}"
|
612
610
|
|
613
|
-
|
611
|
+
url = f"{self.my_profile_command_root}/{server_name}/api/open-metadata/my-profile/to-dos/{todo_guid}/delete"
|
612
|
+
|
613
|
+
await self._async_make_request("POST", url)
|
614
614
|
return
|
615
615
|
|
616
|
-
def delete_to_do(self, todo_guid: str,
|
616
|
+
def delete_to_do(self, todo_guid: str, server_name: str = None) -> None:
|
617
617
|
""" Delete a To-Do item.
|
618
618
|
Parameters
|
619
619
|
----------
|
620
620
|
todo_guid: str
|
621
621
|
Identifier of the To-Do item.
|
622
|
-
status: str
|
623
|
-
Filter items to match this status. Defaults to "OPEN"
|
624
622
|
server_name : str, optional
|
625
623
|
The name of the server where the to-do item will be created. If not provided,
|
626
624
|
the default server name associated with the instance of the class will be used.
|
@@ -640,7 +638,7 @@ class MyProfile(Client):
|
|
640
638
|
The principle specified by the user_id does not have authorization for the requested action
|
641
639
|
"""
|
642
640
|
loop = asyncio.get_event_loop()
|
643
|
-
loop.run_until_complete(self._async_delete_to_do(todo_guid,
|
641
|
+
loop.run_until_complete(self._async_delete_to_do(todo_guid, server_name))
|
644
642
|
return
|
645
643
|
|
646
644
|
async def _async_reassign_to_do(self, todo_guid: str, actor_guid: str, status: str = "OPEN",
|
@@ -934,7 +932,7 @@ class MyProfile(Client):
|
|
934
932
|
validate_name(action_target_guid)
|
935
933
|
|
936
934
|
url = (f"{self.my_profile_command_root}/{server_name}/api/open-metadata/my-profile/to-dos/"
|
937
|
-
f"action-targets/{action_target_guid}?
|
935
|
+
f"action-targets/{action_target_guid}?isMergeUpdate={is_merge_update_t}")
|
938
936
|
|
939
937
|
await self._async_make_request("POST", url, body)
|
940
938
|
return
|
@@ -971,3 +969,4 @@ class MyProfile(Client):
|
|
971
969
|
body, is_merge_update,
|
972
970
|
server_name))
|
973
971
|
return
|
972
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "pyegeria"
|
3
|
-
version = "0.7.
|
3
|
+
version = "0.7.19"
|
4
4
|
license = 'Apache 2.0'
|
5
5
|
authors = ["Dan Wolfson <dan.wolfson@pdr-associates.com>"]
|
6
6
|
readme = "README.md"
|
@@ -63,6 +63,7 @@ pytest = "^8.2.2"
|
|
63
63
|
list_todos = "examples.widgets.cat.list_todos:main"
|
64
64
|
list_cert_types = "examples.widgets.cat.list_cert_types:main"
|
65
65
|
get_project_structure = "examples.widgets.cat.get_project_structure:main"
|
66
|
+
get_project_dependencies = "examples.widgets.cat.get_project_dependencies:main"
|
66
67
|
list_relationships = "examples.widgets.cat.list_relationships:main"
|
67
68
|
|
68
69
|
monitor_asset_events = "examples.widgets.ops.monitor_asset_events:main"
|
@@ -85,6 +86,11 @@ pytest = "^8.2.2"
|
|
85
86
|
list_my_profile = "examples.widgets.my.list_my_profile:main"
|
86
87
|
monitor_open_todos = "examples.widgets.my.monitor_open_todos:main"
|
87
88
|
monitor_my_todos = "examples.widgets.my.monitor_my_todos:main"
|
89
|
+
create_todo = "examples.widgets.my.todo_actions:create_todo"
|
90
|
+
delete_todo = "examples.widgets.my.todo_actions:delete_todo"
|
91
|
+
change_todo_status = "examples.widgets.my.todo_actions:change_todo_status"
|
92
|
+
mark_todo_complete = "examples.widgets.my.todo_actions:mark_todo_complete"
|
93
|
+
reassign_todo = "examples.widgets.my.todo_actions:reassign_todo"
|
88
94
|
|
89
95
|
hey_egeria_ops = "examples.widgets.cli.egeria_ops:cli"
|
90
96
|
hey_egeria_cat = "examples.widgets.cli.egeria_cat:cli"
|
File without changes
|
File without changes
|
File without changes
|
{pyegeria-0.7.18 → pyegeria-0.7.19}/examples/doc_samples/Create_Sustainability_Collection_Sample.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|