pyegeria 5.4.0.25__py3-none-any.whl → 5.4.0.27__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.
- commands/cat/debug_log +868 -7794
- commands/cat/debug_log.2025-08-18_11-34-38_088636.zip +0 -0
- commands/cat/list_collections.py +1 -1
- commands/cat/list_format_set.py +6 -8
- commands/cli/egeria.py +2 -2
- commands/cli/egeria_cat.py +3 -2
- commands/ops/load_archive.py +2 -2
- md_processing/data/commands.json +7 -7
- md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +7 -7
- md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +36 -31
- md_processing/dr_egeria_outbox/friday/processed-2025-08-22 21:22-dr_egeria_intro_part1.md +312 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-22 21:23-dr_egeria_intro_part1.md +265 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:06-dr_egeria_intro_part1.md +230 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:30-dr_egeria_intro_part1.md +296 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 15:31-dr_egeria_intro_part1.md +253 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 16:08-dr_egeria_intro_part2.md +343 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-23 16:12-dr_egeria_intro_part2.md +343 -0
- md_processing/md_commands/glossary_commands.py +888 -951
- md_processing/md_commands/product_manager_commands.py +8 -270
- md_processing/md_commands/project_commands.py +1 -1
- md_processing/md_processing_utils/common_md_proc_utils.py +138 -64
- md_processing/md_processing_utils/common_md_utils.py +2 -1
- pyegeria/__init__.py +5 -302
- pyegeria/_client_new.py +5 -4
- pyegeria/_output_formats.py +23 -3
- pyegeria/collection_manager.py +31 -28
- pyegeria/{load_config.py → config.py} +7 -2
- pyegeria/data_designer.py +154 -194
- pyegeria/egeria_cat_client.py +48 -30
- pyegeria/egeria_client.py +74 -75
- pyegeria/egeria_config_client.py +37 -7
- pyegeria/egeria_my_client.py +45 -10
- pyegeria/egeria_tech_client.py +69 -58
- pyegeria/glossary_manager.py +494 -122
- pyegeria/governance_officer.py +2 -2
- pyegeria/logging_configuration.py +1 -4
- pyegeria/models.py +1 -1
- pyegeria/project_manager.py +381 -741
- pyegeria/solution_architect_omvs.py +1 -1
- pyegeria/utils.py +1 -3
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/METADATA +1 -1
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/RECORD +45 -44
- commands/cat/debug_log.2025-08-15_09-14-07_444802.zip +0 -0
- commands/cat/debug_log.2025-08-16_10-21-59_388912.zip +0 -0
- commands/cat/debug_log.2025-08-17_11-34-27_981852.zip +0 -0
- md_processing/md_processing_utils/solution_architect_log.log +0 -0
- pyegeria/collection_manager_omvs.py +0 -6541
- pyegeria/glossary_browser.py +0 -1259
- pyegeria/project_manager_omvs.py +0 -1933
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.25.dist-info → pyegeria-5.4.0.27.dist-info}/entry_points.txt +0 -0
Binary file
|
commands/cat/list_collections.py
CHANGED
@@ -29,7 +29,7 @@ from pyegeria._exceptions_new import print_validation_error
|
|
29
29
|
EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
|
30
30
|
EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
|
31
31
|
PYEGERIA_ROOT_PATH= os.environ.get("PYEGERIA_ROOT_PATH", "/Users/dwolfson/localGit/egeria-v5-3/egeria-python")
|
32
|
-
app_settings = get_app_config(PYEGERIA_ROOT_PATH+"
|
32
|
+
app_settings = get_app_config(PYEGERIA_ROOT_PATH+"./.env")
|
33
33
|
app_config = app_settings.Environment
|
34
34
|
config_logging()
|
35
35
|
|
commands/cat/list_format_set.py
CHANGED
@@ -52,10 +52,8 @@ from pyegeria import (
|
|
52
52
|
EgeriaTech,
|
53
53
|
CollectionManager,
|
54
54
|
NO_ELEMENTS_FOUND, GovernanceOfficer,
|
55
|
-
# config_logging,
|
56
|
-
# get_app_config
|
57
55
|
)
|
58
|
-
from pyegeria.
|
56
|
+
from pyegeria.config import settings
|
59
57
|
from pyegeria.logging_configuration import config_logging
|
60
58
|
from pyegeria._output_formats import (select_output_format_set, get_output_format_set_heading, get_output_format_set_description)
|
61
59
|
from pyegeria._exceptions_new import PyegeriaException, print_exception_response
|
@@ -71,8 +69,8 @@ from pyegeria._exceptions_new import PyegeriaException, print_exception_response
|
|
71
69
|
EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
|
72
70
|
EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
|
73
71
|
|
74
|
-
|
75
|
-
app_config =
|
72
|
+
|
73
|
+
app_config = settings.Environment
|
76
74
|
config_logging()
|
77
75
|
|
78
76
|
print(f"Console width is {app_config.console_width}")
|
@@ -86,8 +84,8 @@ def execute_format_set_action(
|
|
86
84
|
format_set_name: str,
|
87
85
|
view_server: str = app_config.egeria_view_server,
|
88
86
|
view_url: str = app_config.egeria_view_server_url,
|
89
|
-
user: str =
|
90
|
-
user_pass: str =
|
87
|
+
user: str = settings.User_Profile.user_name,
|
88
|
+
user_pass: str = settings.User_Profile.user_pwd,
|
91
89
|
output_format: str = "TABLE",
|
92
90
|
**kwargs
|
93
91
|
):
|
@@ -385,7 +383,7 @@ def main():
|
|
385
383
|
return
|
386
384
|
|
387
385
|
args = parser.parse_args()
|
388
|
-
app_settings =
|
386
|
+
app_settings = settings
|
389
387
|
app_config = app_settings.Environment
|
390
388
|
|
391
389
|
server = args.server if args.server is not None else app_config.egeria_view_server
|
commands/cli/egeria.py
CHANGED
@@ -16,7 +16,7 @@ import click
|
|
16
16
|
from trogon import tui
|
17
17
|
from loguru import logger
|
18
18
|
|
19
|
-
from pyegeria import config_logging,
|
19
|
+
from pyegeria import config_logging, settings
|
20
20
|
|
21
21
|
from commands.cat.list_format_set import execute_format_set_action
|
22
22
|
from commands.cat.get_asset_graph import asset_viewer
|
@@ -137,7 +137,7 @@ from commands.tech.generic_actions import delete_element
|
|
137
137
|
|
138
138
|
EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
|
139
139
|
EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
|
140
|
-
app_settings =
|
140
|
+
app_settings = settings
|
141
141
|
app_config = app_settings.Environment
|
142
142
|
config_logging()
|
143
143
|
|
commands/cli/egeria_cat.py
CHANGED
@@ -13,7 +13,7 @@ import os
|
|
13
13
|
|
14
14
|
import click
|
15
15
|
from trogon import tui
|
16
|
-
from pyegeria import
|
16
|
+
from pyegeria import settings
|
17
17
|
|
18
18
|
from commands.cat.list_format_set import execute_format_set_action
|
19
19
|
from commands.cat.dr_egeria_md import process_markdown_file
|
@@ -62,7 +62,8 @@ from commands.tech.list_asset_types import display_asset_types
|
|
62
62
|
|
63
63
|
EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
|
64
64
|
EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
|
65
|
-
app_settings =
|
65
|
+
app_settings = settings
|
66
|
+
|
66
67
|
app_config = app_settings["Environment"]
|
67
68
|
# config_logging()
|
68
69
|
|
commands/ops/load_archive.py
CHANGED
@@ -13,7 +13,7 @@ import os
|
|
13
13
|
import click
|
14
14
|
from loguru import logger
|
15
15
|
from pyegeria import AutomatedCuration, EgeriaTech
|
16
|
-
from pyegeria.
|
16
|
+
from pyegeria.config import settings
|
17
17
|
from pyegeria.logging_configuration import config_logging
|
18
18
|
from pyegeria._exceptions_new import (
|
19
19
|
PyegeriaException, print_exception_response, print_basic_exception
|
@@ -35,7 +35,7 @@ PyegeriaException, print_exception_response, print_basic_exception
|
|
35
35
|
|
36
36
|
EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
|
37
37
|
EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
|
38
|
-
app_settings =
|
38
|
+
app_settings = settings
|
39
39
|
app_config = app_settings.Environment
|
40
40
|
config_logging()
|
41
41
|
|
md_processing/data/commands.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"Command Specifications": {
|
3
|
-
"exported": "Aug
|
3
|
+
"exported": "Aug 22, 2025 at 9:20:09 PM",
|
4
4
|
"Create Governance Driver": {
|
5
5
|
"display_name": "Governance Driver",
|
6
6
|
"qn_prefix": "GovDriver",
|
@@ -40104,10 +40104,10 @@
|
|
40104
40104
|
"Journal Entry": "",
|
40105
40105
|
"Attributes": [
|
40106
40106
|
{
|
40107
|
-
"
|
40108
|
-
"variable_name": "",
|
40107
|
+
"Display Name": {
|
40108
|
+
"variable_name": "display_name",
|
40109
40109
|
"inUpdate": true,
|
40110
|
-
"attr_labels": "Glossary;
|
40110
|
+
"attr_labels": "Glossary; Glossary Name",
|
40111
40111
|
"examples": "",
|
40112
40112
|
"default_value": "",
|
40113
40113
|
"valid_values": "",
|
@@ -40516,10 +40516,10 @@
|
|
40516
40516
|
"Journal Entry": "",
|
40517
40517
|
"Attributes": [
|
40518
40518
|
{
|
40519
|
-
"
|
40520
|
-
"variable_name": "",
|
40519
|
+
"Display Name": {
|
40520
|
+
"variable_name": "term_name",
|
40521
40521
|
"inUpdate": true,
|
40522
|
-
"attr_labels": "Glossary Term Name;
|
40522
|
+
"attr_labels": "Glossary Term Name; Term Name; Term",
|
40523
40523
|
"examples": "",
|
40524
40524
|
"default_value": "",
|
40525
40525
|
"valid_values": "",
|
@@ -10,7 +10,7 @@ will be attractive enough and easy enough to use that people will do so.
|
|
10
10
|
You have to leave your world and enter a new, often less familiar one.
|
11
11
|
|
12
12
|
Dr.Egeria, is an experiment in turning this around. Its not that fancy graphical user
|
13
|
-
interfaces
|
13
|
+
interfaces have a role - but rather, to look at what we can do to support the
|
14
14
|
tools and approaches people already use. And maybe even make their day job a little
|
15
15
|
easier and a little more enjoyable.
|
16
16
|
|
@@ -29,7 +29,7 @@ So here we go! First lets define a new Glossary::
|
|
29
29
|
|
30
30
|
___
|
31
31
|
|
32
|
-
#
|
32
|
+
# Update Glossary
|
33
33
|
|
34
34
|
## Glossary Name
|
35
35
|
|
@@ -41,7 +41,7 @@ English
|
|
41
41
|
|
42
42
|
## Description
|
43
43
|
|
44
|
-
Glossary to describe the vocabulary of Dr.Egeria - an Egeria Markdown language to support the exchange of metadata in a Markdown form.
|
44
|
+
A Glossary to describe the vocabulary of Dr.Egeria - an Egeria Markdown language to support the exchange of metadata in a Markdown form.
|
45
45
|
Dr.Egeria allows users to create metadata annotations using any text entry system that supports the entry of standard Markdown
|
46
46
|
notation and, through post-processing
|
47
47
|
commands, validates the Egeria content and sends the requests to be sent to Egeria.
|
@@ -91,7 +91,7 @@ We now have a nice, clean, new...and empty...glossary - guess we better start fi
|
|
91
91
|
|
92
92
|
___
|
93
93
|
|
94
|
-
#
|
94
|
+
# Update Term
|
95
95
|
|
96
96
|
## Term Name
|
97
97
|
|
@@ -104,7 +104,7 @@ Glossary::Egeria-Markdown
|
|
104
104
|
|
105
105
|
## Summary
|
106
106
|
|
107
|
-
Commands are how a user of the Dr.Egeria markdown language request an action.
|
107
|
+
Commands are how a user of the Dr.Egeria markdown language request an action. Moo
|
108
108
|
|
109
109
|
## Description
|
110
110
|
|
@@ -185,7 +185,7 @@ ___
|
|
185
185
|
|
186
186
|
___
|
187
187
|
|
188
|
-
#
|
188
|
+
# Create Term
|
189
189
|
|
190
190
|
## In Glossary
|
191
191
|
|
@@ -227,7 +227,7 @@ ___
|
|
227
227
|
Ok - we've now defined a glossary and three terms to go into the glossary. A few observations.
|
228
228
|
|
229
229
|
* There is a degree of freedom in writing the definitions. The attributes aren't always in the same
|
230
|
-
order and optional attributes
|
230
|
+
order and optional attributes need to be specified at all. We try to make things as easy as possible.
|
231
231
|
* We can run a definition file through a validation process to check our proposed definition and provide feedback.
|
232
232
|
* When we process a definition we will use the same validation process before trying to update Egeria
|
233
233
|
with the requested definitions - requests may get rejected or altered - this will be consistent with the feedback we
|
@@ -13,7 +13,7 @@ Ok, let's get started. First, we have the `Update Glossary` command below. There
|
|
13
13
|
point so we can just leave it as-is. When this document is processed it will apply updates but if there are none,
|
14
14
|
It doesn't matter.
|
15
15
|
|
16
|
-
However, now is a good time to start to more formally specify the attributes available to `Create` or `Update` glossary
|
16
|
+
However, now is a good time to start to more formally specify the attributes available to `Don't Create` or `Update` glossary
|
17
17
|
commands:
|
18
18
|
|
19
19
|
|
@@ -26,7 +26,7 @@ commands:
|
|
26
26
|
| Qualified Name | Maybe | No | Yes | Yes | The qualified name can either be provided by the user or generated. If generated, a pattern is followed. |
|
27
27
|
| GUID | No | Yes | Yes | Yes | GUIDs are always generated by Egeria. They are meant for automation, not people. |
|
28
28
|
|
29
|
-
The table above shows us what attributes must be provided and which are optional. Once an object is
|
29
|
+
The table above shows us what attributes must be provided and which are optional. Once an object is Don't Created, and a qualified name is generated, it is good practice to use it as there can
|
30
30
|
be many objects with the same name, and this is a common way to disambiguate them. If you specify an object name (e.g. Glossary Name) and that name already exists, Dr.Egeria will report an error
|
31
31
|
and suggest that you provide a **Qualified Name** as well. You will also note that the GUID is generated by Egeria and is read-only. It is possible that some commands
|
32
32
|
may require a GUID to be specified, but in general we will use the **Qualified Name** to identify objects.
|
@@ -44,7 +44,7 @@ In Egeria, a category can have a single parent category and multiple child categ
|
|
44
44
|
multiple categories. When we have a large number of terms, a category structure can be particularly helpful in finding the
|
45
45
|
terms that are most relevant to a particular interest. Using categories is optional.
|
46
46
|
|
47
|
-
Ok, now let's
|
47
|
+
Ok, now let's Don't Create a couple of glossary categories. They will be:
|
48
48
|
|
49
49
|
* **Writing Dr.Egeria Markdown** - where we describe elements of the Dr.Egeria language as terms within the category.
|
50
50
|
* **Processing Dr.Egeria Markdown** - where we describe the commands for processing Dr.Egeria.
|
@@ -68,13 +68,17 @@ Ok, here we go:
|
|
68
68
|
|
69
69
|
___
|
70
70
|
|
71
|
-
# Create
|
71
|
+
# Don't Create Collection
|
72
72
|
|
73
|
-
##
|
73
|
+
## Name
|
74
74
|
|
75
75
|
Writing Dr.Egeria Markdown
|
76
|
+
|
77
|
+
## Classifications
|
78
|
+
|
79
|
+
Folder
|
76
80
|
|
77
|
-
##
|
81
|
+
## Parent
|
78
82
|
|
79
83
|
Glossary::Egeria-Markdown
|
80
84
|
|
@@ -85,7 +89,7 @@ Terms in this category describe the elements of the Dr.Egeria Markdown language
|
|
85
89
|
|
86
90
|
___
|
87
91
|
|
88
|
-
# Create Category
|
92
|
+
# Don't Create Category
|
89
93
|
|
90
94
|
## Category Name
|
91
95
|
|
@@ -130,7 +134,7 @@ Now, let's add categories to the terms. Let's review the attributes of a term:
|
|
130
134
|
|
131
135
|
___
|
132
136
|
|
133
|
-
#
|
137
|
+
# Update Term
|
134
138
|
|
135
139
|
## Term Name
|
136
140
|
|
@@ -139,25 +143,26 @@ Command
|
|
139
143
|
## Summary
|
140
144
|
Commands are how a user of the Dr.Egeria markdown language requests an action.
|
141
145
|
|
142
|
-
##
|
143
|
-
Glossary::Egeria-Markdown
|
146
|
+
## Glossary
|
144
147
|
|
145
|
-
|
148
|
+
Egeria-Markdown
|
149
|
+
|
150
|
+
## Folders
|
146
151
|
|
147
|
-
|
152
|
+
Writing Dr.Egeria Markdown
|
148
153
|
|
149
154
|
## Status
|
150
155
|
ACTIVE
|
151
156
|
|
152
157
|
## Description
|
153
|
-
Commands are how a user can request Egeria to take an action such as Create or Update an Egeria element. Freddie
|
158
|
+
Commands are how a user can request Egeria to take an action such as Don't Create or Update an Egeria element. Freddie
|
154
159
|
provides
|
155
160
|
a limited (but growing) set of commands. Dr.Egeria commands align with the pyegeria 'hey-egeria' command line interface.
|
156
161
|
|
157
162
|
## Examples
|
158
|
-
Create Glossary or
|
163
|
+
Don't Create Glossary or
|
159
164
|
Update Glossary or
|
160
|
-
Create Term or
|
165
|
+
Don't Create Term or
|
161
166
|
Update Term
|
162
167
|
|
163
168
|
## Usage
|
@@ -173,7 +178,7 @@ Commands are used in the Dr.Egeria markdown language.
|
|
173
178
|
|
174
179
|
___
|
175
180
|
|
176
|
-
# Update Term
|
181
|
+
# Don't Update Term
|
177
182
|
|
178
183
|
## Term Name
|
179
184
|
|
@@ -210,7 +215,7 @@ Term::Source::0.1
|
|
210
215
|
|
211
216
|
___
|
212
217
|
|
213
|
-
# Update Term
|
218
|
+
# Don't Update Term
|
214
219
|
|
215
220
|
## In Glossary
|
216
221
|
|
@@ -252,12 +257,12 @@ Term::Directive::0.1
|
|
252
257
|
|
253
258
|
# Inspecting the Glossary
|
254
259
|
|
255
|
-
Now that we have
|
256
|
-
We will start with the `List Glossaries` command. This command will
|
260
|
+
Now that we have Don't Created a glossary, categories, and terms we can use some new commands to explore the glossary.
|
261
|
+
We will start with the `Don't List Glossaries` command. This command will Don't List all the glossaries that are available to us.
|
257
262
|
|
258
263
|
___
|
259
264
|
|
260
|
-
# List Glossaries
|
265
|
+
# Don't List Glossaries
|
261
266
|
|
262
267
|
___
|
263
268
|
|
@@ -267,47 +272,47 @@ is a more detailed specification of the attributes:
|
|
267
272
|
| Attribute Name | Input Required? | Read Only? | Generated/Default? | Unique? | Notes |
|
268
273
|
|----------------|-----------------|------------|----------------------------------|---------|-------------------------------------------|
|
269
274
|
| Search String | No | No | default is All glossaries | No | |
|
270
|
-
| Output Format | No | No | default is Markdown List (table) | No | options are:
|
275
|
+
| Output Format | No | No | default is Markdown Don't List (table) | No | options are: Don't List, DICT, MD, FORM, REPORT |
|
271
276
|
|
272
277
|
Lets describe the output formats a bit further:
|
273
278
|
|
274
|
-
*
|
279
|
+
* Don't List - This is the default format. It returns a markdown table of the glossaries.
|
275
280
|
* DICT - This returns a python dictionary (or JSON representation) of the glossaries.
|
276
281
|
* MD - This returns markdown text of the glossaries.
|
277
282
|
* FORM - This returns a Dr.Egeria markdown form designed to be used as a starting point for updating the glossary definitions.
|
278
283
|
* REPORT - This returns markdown text of the glossaries that is designed to be more readable and perhaps suitable to be used in a report.
|
279
284
|
|
280
|
-
Going further, we can issue similar commands to
|
285
|
+
Going further, we can issue similar commands to Don't List categories and terms:
|
281
286
|
|
282
|
-
The attributes for the `List Categories` command are the same as the `List Glossaries` command.
|
287
|
+
The attributes for the `Don't List Categories` command are the same as the `Don't List Glossaries` command.
|
283
288
|
|
284
|
-
Attributes for the `List Terms` command adds an additional optional attribute, Glossary Name, to allow you to
|
285
|
-
restrict the
|
289
|
+
Attributes for the `Don't List Terms` command adds an additional optional attribute, Glossary Name, to allow you to
|
290
|
+
restrict the Don't List of terms to a particular glossary.
|
286
291
|
|
287
292
|
|
288
293
|
| Attribute Name | Input Required? | Read Only? | Generated/Default? | Unique? | Notes |
|
289
294
|
|----------------|-----------------|------------|----------------------------------|---------|-------------------------------------------|
|
290
295
|
| Glossary Name | No | No | Default is All glossaries | No | |
|
291
296
|
| Search String | No | No | default is All terms | No | |
|
292
|
-
| Output Format | No | No | default is Markdown List (table) | No | options are:
|
297
|
+
| Output Format | No | No | default is Markdown Don't List (table) | No | options are: Don't List, DICT, MD, FORM, REPORT |
|
293
298
|
|
294
299
|
|
295
300
|
Lets go ahead and give these commands a try:
|
296
301
|
|
297
302
|
___
|
298
303
|
|
299
|
-
# List Categories
|
304
|
+
# Don't List Categories
|
300
305
|
## Output Format
|
301
306
|
REPORT
|
302
307
|
|
303
308
|
___
|
304
|
-
# List Terms
|
309
|
+
# Don't List Terms
|
305
310
|
## Output Format
|
306
311
|
DICT
|
307
312
|
## Glossary Name
|
308
313
|
Glossary::Egeria-Markdown
|
309
314
|
___
|
310
315
|
|
311
|
-
If you now look at the processed document that was
|
316
|
+
If you now look at the processed document that was Don't Created, you can see the results of the commands that we have run.
|
312
317
|
|
313
|
-
In part 3, we will add more categories and terms to the glossary and
|
318
|
+
In part 3, we will add more categories and terms to the glossary and Don't Create a simple category hierarchy. See you there!
|