julee 0.1.5__py3-none-any.whl → 0.1.7__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 (108) hide show
  1. julee/__init__.py +1 -1
  2. julee/contrib/polling/apps/worker/pipelines.py +3 -1
  3. julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +3 -0
  4. julee/docs/sphinx_hcd/__init__.py +146 -13
  5. julee/docs/sphinx_hcd/domain/__init__.py +5 -0
  6. julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
  7. julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
  8. julee/docs/sphinx_hcd/domain/models/app.py +151 -0
  9. julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
  10. julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
  11. julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
  12. julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
  13. julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
  14. julee/docs/sphinx_hcd/domain/models/story.py +128 -0
  15. julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
  16. julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
  17. julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
  18. julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
  19. julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
  20. julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
  21. julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
  22. julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
  23. julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
  24. julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
  25. julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
  26. julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
  27. julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
  28. julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
  29. julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
  30. julee/docs/sphinx_hcd/parsers/ast.py +150 -0
  31. julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
  32. julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
  33. julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
  34. julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
  35. julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
  36. julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
  37. julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
  38. julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
  39. julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
  40. julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
  41. julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
  42. julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
  43. julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
  44. julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
  45. julee/docs/sphinx_hcd/sphinx/context.py +163 -0
  46. julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
  47. julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
  48. julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
  49. julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
  50. julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
  51. julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
  52. julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
  53. julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
  54. julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
  55. julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
  56. julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
  57. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
  58. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
  59. julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
  60. julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
  61. julee/docs/sphinx_hcd/tests/__init__.py +9 -0
  62. julee/docs/sphinx_hcd/tests/conftest.py +6 -0
  63. julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
  64. julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
  65. julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
  66. julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
  67. julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
  68. julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
  69. julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
  70. julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
  71. julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
  72. julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
  73. julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
  74. julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
  75. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
  76. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
  77. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
  78. julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
  79. julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
  80. julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
  81. julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
  82. julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
  83. julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
  84. julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
  85. julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
  86. julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
  87. julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
  88. julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
  89. julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
  90. julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
  91. julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
  92. julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
  93. julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
  94. julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
  95. julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
  96. julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
  97. {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/METADATA +2 -1
  98. {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/RECORD +101 -16
  99. julee/docs/sphinx_hcd/accelerators.py +0 -1175
  100. julee/docs/sphinx_hcd/apps.py +0 -518
  101. julee/docs/sphinx_hcd/epics.py +0 -453
  102. julee/docs/sphinx_hcd/integrations.py +0 -310
  103. julee/docs/sphinx_hcd/journeys.py +0 -797
  104. julee/docs/sphinx_hcd/personas.py +0 -457
  105. julee/docs/sphinx_hcd/stories.py +0 -960
  106. {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/WHEEL +0 -0
  107. {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/licenses/LICENSE +0 -0
  108. {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,457 +0,0 @@
1
- """Sphinx extension for persona diagrams.
2
-
3
- Generates PlantUML use case diagrams dynamically from epic and story data.
4
-
5
- Provides directives:
6
- - persona-diagram: Generate UML diagram for a single persona showing their epics
7
- - persona-index-diagram: Generate UML diagram for staff or external persona groups
8
- """
9
-
10
- from collections import defaultdict
11
-
12
- from docutils import nodes
13
- from sphinx.util import logging
14
- from sphinx.util.docutils import SphinxDirective
15
-
16
- from .utils import normalize_name, slugify
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- def get_epics_for_persona(
22
- persona_name: str, epic_registry: dict, story_registry: list
23
- ) -> list[tuple[str, dict]]:
24
- """Get epics that contain stories for a given persona.
25
-
26
- Args:
27
- persona_name: The persona name to match
28
- epic_registry: Dict of epic_slug -> epic_data
29
- story_registry: List of story dicts
30
-
31
- Returns:
32
- List of (epic_slug, epic_data) tuples for matching epics
33
- """
34
- persona_normalized = normalize_name(persona_name)
35
-
36
- # Build lookup of story title -> persona
37
- story_personas = {}
38
- for story in story_registry:
39
- story_personas[normalize_name(story["feature"])] = story["persona_normalized"]
40
-
41
- matching_epics = []
42
- for slug, epic in epic_registry.items():
43
- # Check if any story in this epic belongs to the persona
44
- for story_title in epic.get("stories", []):
45
- story_normalized = normalize_name(story_title)
46
- if story_personas.get(story_normalized) == persona_normalized:
47
- matching_epics.append((slug, epic))
48
- break
49
-
50
- return sorted(matching_epics, key=lambda x: x[0])
51
-
52
-
53
- def get_apps_for_epic(epic: dict, story_registry: list) -> set[str]:
54
- """Get the set of app slugs used by stories in an epic.
55
-
56
- Args:
57
- epic: Epic data dict with 'stories' list
58
- story_registry: List of story dicts
59
-
60
- Returns:
61
- Set of app slug strings
62
- """
63
- apps = set()
64
-
65
- # Build lookup of story title -> app
66
- story_apps = {}
67
- for story in story_registry:
68
- story_apps[normalize_name(story["feature"])] = story["app"]
69
-
70
- for story_title in epic.get("stories", []):
71
- story_normalized = normalize_name(story_title)
72
- if story_normalized in story_apps:
73
- apps.add(story_apps[story_normalized])
74
-
75
- return apps
76
-
77
-
78
- def get_apps_for_persona(persona_name: str, story_registry: list) -> set[str]:
79
- """Get the set of app slugs used by a persona.
80
-
81
- Args:
82
- persona_name: The persona name to match
83
- story_registry: List of story dicts
84
-
85
- Returns:
86
- Set of app slug strings
87
- """
88
- persona_normalized = normalize_name(persona_name)
89
- apps = set()
90
-
91
- for story in story_registry:
92
- if story["persona_normalized"] == persona_normalized:
93
- apps.add(story["app"])
94
-
95
- return apps
96
-
97
-
98
- def generate_persona_plantuml(
99
- persona_name: str,
100
- epics: list[tuple[str, dict]],
101
- story_registry: list,
102
- app_registry: dict,
103
- ) -> str:
104
- """Generate PlantUML for a single persona's use case diagram.
105
-
106
- Args:
107
- persona_name: Display name of the persona
108
- epics: List of (epic_slug, epic_data) tuples
109
- story_registry: List of story dicts
110
- app_registry: Dict of app_slug -> app_data
111
-
112
- Returns:
113
- PlantUML source string
114
- """
115
- persona_id = slugify(persona_name).replace("-", "_")
116
-
117
- lines = [
118
- f"@startuml persona-{slugify(persona_name)}",
119
- "left to right direction",
120
- "skinparam actorStyle awesome",
121
- "",
122
- f'actor "{persona_name}" as {persona_id}',
123
- "",
124
- ]
125
-
126
- # Collect all apps used by this persona's epics
127
- all_apps = set()
128
- epic_apps = {} # epic_slug -> set of apps
129
-
130
- for epic_slug, epic in epics:
131
- apps = get_apps_for_epic(epic, story_registry)
132
- epic_apps[epic_slug] = apps
133
- all_apps.update(apps)
134
-
135
- # Generate component declarations for apps
136
- for app_slug in sorted(all_apps):
137
- app_id = app_slug.replace("-", "_")
138
- app_name = app_registry.get(app_slug, {}).get(
139
- "name", app_slug.replace("-", " ").title()
140
- )
141
- lines.append(f'component "{app_name}" as {app_id}')
142
-
143
- lines.append("")
144
-
145
- # Generate usecase declarations for epics
146
- for epic_slug, _epic in epics:
147
- epic_id = epic_slug.replace("-", "_")
148
- epic_name = epic_slug.replace("-", " ").title()
149
- lines.append(f'usecase "{epic_name}" as {epic_id}')
150
-
151
- lines.append("")
152
-
153
- # Generate persona -> epic connections
154
- for epic_slug, _epic in epics:
155
- epic_id = epic_slug.replace("-", "_")
156
- lines.append(f"{persona_id} --> {epic_id}")
157
-
158
- lines.append("")
159
-
160
- # Generate epic -> app connections
161
- for epic_slug, _epic in epics:
162
- epic_id = epic_slug.replace("-", "_")
163
- for app_slug in sorted(epic_apps.get(epic_slug, [])):
164
- app_id = app_slug.replace("-", "_")
165
- lines.append(f"{epic_id} --> {app_id}")
166
-
167
- lines.append("")
168
- lines.append("@enduml")
169
-
170
- return "\n".join(lines)
171
-
172
-
173
- def generate_persona_index_plantuml(
174
- persona_type: str,
175
- personas: list[str],
176
- epic_registry: dict,
177
- story_registry: list,
178
- app_registry: dict,
179
- ) -> str:
180
- """Generate PlantUML for a group of personas (staff or external).
181
-
182
- Args:
183
- persona_type: 'staff' or 'external'
184
- personas: List of persona names in this group
185
- epic_registry: Dict of epic_slug -> epic_data
186
- story_registry: List of story dicts
187
- app_registry: Dict of app_slug -> app_data
188
-
189
- Returns:
190
- PlantUML source string
191
- """
192
- lines = [
193
- f"@startuml persona-{persona_type}",
194
- "left to right direction",
195
- "skinparam actorStyle awesome",
196
- "",
197
- ]
198
-
199
- # Collect data for all personas
200
- persona_epics = {} # persona -> list of epic slugs
201
- all_apps = set()
202
- epic_app_map = {} # epic_slug -> set of apps
203
-
204
- for persona_name in personas:
205
- epics = get_epics_for_persona(persona_name, epic_registry, story_registry)
206
- persona_epics[persona_name] = [slug for slug, _ in epics]
207
-
208
- for epic_slug, epic in epics:
209
- if epic_slug not in epic_app_map:
210
- apps = get_apps_for_epic(epic, story_registry)
211
- epic_app_map[epic_slug] = apps
212
- all_apps.update(apps)
213
-
214
- # Generate actor declarations
215
- for persona_name in sorted(personas):
216
- persona_id = slugify(persona_name).replace("-", "_")
217
- lines.append(f'actor "{persona_name}" as {persona_id}')
218
-
219
- lines.append("")
220
-
221
- # Generate component declarations for apps
222
- for app_slug in sorted(all_apps):
223
- app_id = app_slug.replace("-", "_")
224
- app_name = app_registry.get(app_slug, {}).get(
225
- "name", app_slug.replace("-", " ").title()
226
- )
227
- lines.append(f'component "{app_name}" as {app_id}')
228
-
229
- lines.append("")
230
-
231
- # Collect unique epics across all personas in this group
232
- all_epics = set()
233
- for epic_slugs in persona_epics.values():
234
- all_epics.update(epic_slugs)
235
-
236
- # Generate usecase declarations for epics
237
- for epic_slug in sorted(all_epics):
238
- epic_id = epic_slug.replace("-", "_")
239
- epic_name = epic_slug.replace("-", " ").title()
240
- lines.append(f'usecase "{epic_name}" as {epic_id}')
241
-
242
- lines.append("")
243
-
244
- # Generate persona -> epic connections
245
- for persona_name in sorted(personas):
246
- persona_id = slugify(persona_name).replace("-", "_")
247
- for epic_slug in sorted(persona_epics.get(persona_name, [])):
248
- epic_id = epic_slug.replace("-", "_")
249
- lines.append(f"{persona_id} --> {epic_id}")
250
-
251
- lines.append("")
252
-
253
- # Generate epic -> app connections
254
- for epic_slug in sorted(all_epics):
255
- epic_id = epic_slug.replace("-", "_")
256
- for app_slug in sorted(epic_app_map.get(epic_slug, [])):
257
- app_id = app_slug.replace("-", "_")
258
- lines.append(f"{epic_id} --> {app_id}")
259
-
260
- lines.append("")
261
- lines.append("@enduml")
262
-
263
- return "\n".join(lines)
264
-
265
-
266
- class PersonaDiagramDirective(SphinxDirective):
267
- """Generate PlantUML use case diagram for a single persona.
268
-
269
- Usage::
270
-
271
- .. persona-diagram:: Pilot Manager
272
-
273
- Generates a diagram showing:
274
- - The persona as an actor
275
- - Epics they participate in as use cases
276
- - Apps they interact with as components
277
- """
278
-
279
- required_arguments = 1
280
- final_argument_whitespace = True
281
-
282
- def run(self):
283
- persona_name = self.arguments[0]
284
-
285
- # Return placeholder - actual rendering in doctree-resolved
286
- node = PersonaDiagramPlaceholder()
287
- node["persona"] = persona_name
288
- return [node]
289
-
290
-
291
- class PersonaDiagramPlaceholder(nodes.General, nodes.Element):
292
- """Placeholder node for persona-diagram, replaced at doctree-resolved."""
293
-
294
- pass
295
-
296
-
297
- class PersonaIndexDiagramDirective(SphinxDirective):
298
- """Generate PlantUML diagram for a group of personas.
299
-
300
- Usage::
301
-
302
- .. persona-index-diagram:: staff
303
- .. persona-index-diagram:: customers
304
- .. persona-index-diagram:: vendors
305
-
306
- Groups are determined by the type field from app.yaml manifests.
307
- Any value is accepted - the directive filters personas to those
308
- using apps with a matching type.
309
- """
310
-
311
- required_arguments = 1
312
- option_spec = {}
313
-
314
- def run(self):
315
- group_type = self.arguments[0].lower()
316
-
317
- # Return placeholder - actual rendering in doctree-resolved
318
- node = PersonaIndexDiagramPlaceholder()
319
- node["group_type"] = group_type
320
- return [node]
321
-
322
-
323
- class PersonaIndexDiagramPlaceholder(nodes.General, nodes.Element):
324
- """Placeholder node for persona-index-diagram, replaced at doctree-resolved."""
325
-
326
- pass
327
-
328
-
329
- def get_personas_by_app_type(
330
- story_registry: list, app_registry: dict
331
- ) -> dict[str, set[str]]:
332
- """Group personas by the type of apps they use.
333
-
334
- Args:
335
- story_registry: List of story dicts
336
- app_registry: Dict of app_slug -> app_data
337
-
338
- Returns:
339
- Dict mapping app type strings to sets of persona names
340
- """
341
- personas_by_type = defaultdict(set)
342
-
343
- for story in story_registry:
344
- app_slug = story["app"]
345
- persona = story["persona"]
346
-
347
- if persona == "unknown":
348
- continue
349
-
350
- app_data = app_registry.get(app_slug, {})
351
- app_type = app_data.get("type", "unknown").lower()
352
-
353
- personas_by_type[app_type].add(persona)
354
-
355
- return personas_by_type
356
-
357
-
358
- def build_persona_diagram(persona_name: str, env, docname: str):
359
- """Build the PlantUML diagram for a single persona."""
360
- import os
361
-
362
- from sphinxcontrib.plantuml import plantuml
363
-
364
- from . import apps, epics, stories
365
-
366
- story_registry = stories.get_story_registry()
367
- epic_registry = epics.get_epic_registry(env)
368
- app_registry = apps.get_app_registry()
369
-
370
- # Get epics for this persona
371
- persona_epics = get_epics_for_persona(persona_name, epic_registry, story_registry)
372
-
373
- if not persona_epics:
374
- para = nodes.paragraph()
375
- para += nodes.emphasis(text=f"No epics found for persona '{persona_name}'")
376
- return [para]
377
-
378
- # Generate PlantUML
379
- puml_source = generate_persona_plantuml(
380
- persona_name, persona_epics, story_registry, app_registry
381
- )
382
-
383
- # Create plantuml node with required attributes
384
- node = plantuml(puml_source)
385
- node["uml"] = puml_source
386
- node["incdir"] = os.path.dirname(docname)
387
- node["filename"] = os.path.basename(docname)
388
-
389
- return [node]
390
-
391
-
392
- def build_persona_index_diagram(group_type: str, env, docname: str):
393
- """Build the PlantUML diagram for a persona group."""
394
- import os
395
-
396
- from sphinxcontrib.plantuml import plantuml
397
-
398
- from . import apps, epics, stories
399
-
400
- story_registry = stories.get_story_registry()
401
- epic_registry = epics.get_epic_registry(env)
402
- app_registry = apps.get_app_registry()
403
-
404
- # Get personas for this group type
405
- personas_by_type = get_personas_by_app_type(story_registry, app_registry)
406
- personas = sorted(personas_by_type.get(group_type, set()))
407
-
408
- if not personas:
409
- para = nodes.paragraph()
410
- para += nodes.emphasis(text=f"No {group_type} personas found")
411
- return [para]
412
-
413
- # Generate PlantUML
414
- puml_source = generate_persona_index_plantuml(
415
- group_type, personas, epic_registry, story_registry, app_registry
416
- )
417
-
418
- # Create plantuml node with required attributes
419
- node = plantuml(puml_source)
420
- node["uml"] = puml_source
421
- node["incdir"] = os.path.dirname(docname)
422
- node["filename"] = os.path.basename(docname)
423
-
424
- return [node]
425
-
426
-
427
- def process_persona_placeholders(app, doctree, docname):
428
- """Replace persona diagram placeholders with rendered content."""
429
- env = app.env
430
-
431
- # Process persona-diagram placeholders
432
- for node in doctree.traverse(PersonaDiagramPlaceholder):
433
- persona = node["persona"]
434
- content = build_persona_diagram(persona, env, docname)
435
- node.replace_self(content)
436
-
437
- # Process persona-index-diagram placeholders
438
- for node in doctree.traverse(PersonaIndexDiagramPlaceholder):
439
- group_type = node["group_type"]
440
- content = build_persona_index_diagram(group_type, env, docname)
441
- node.replace_self(content)
442
-
443
-
444
- def setup(app):
445
- app.connect("doctree-resolved", process_persona_placeholders)
446
-
447
- app.add_directive("persona-diagram", PersonaDiagramDirective)
448
- app.add_directive("persona-index-diagram", PersonaIndexDiagramDirective)
449
-
450
- app.add_node(PersonaDiagramPlaceholder)
451
- app.add_node(PersonaIndexDiagramPlaceholder)
452
-
453
- return {
454
- "version": "1.0",
455
- "parallel_read_safe": False,
456
- "parallel_write_safe": True,
457
- }