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.
- julee/__init__.py +1 -1
- julee/contrib/polling/apps/worker/pipelines.py +3 -1
- julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +3 -0
- julee/docs/sphinx_hcd/__init__.py +146 -13
- julee/docs/sphinx_hcd/domain/__init__.py +5 -0
- julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
- julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
- julee/docs/sphinx_hcd/domain/models/app.py +151 -0
- julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
- julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
- julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
- julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
- julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
- julee/docs/sphinx_hcd/domain/models/story.py +128 -0
- julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
- julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
- julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
- julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
- julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
- julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
- julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
- julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
- julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
- julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
- julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
- julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
- julee/docs/sphinx_hcd/parsers/ast.py +150 -0
- julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
- julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
- julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
- julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
- julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
- julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
- julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
- julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
- julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
- julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
- julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
- julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
- julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
- julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
- julee/docs/sphinx_hcd/sphinx/context.py +163 -0
- julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
- julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
- julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
- julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
- julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
- julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
- julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
- julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
- julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
- julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
- julee/docs/sphinx_hcd/tests/__init__.py +9 -0
- julee/docs/sphinx_hcd/tests/conftest.py +6 -0
- julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
- julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
- julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
- julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
- julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
- julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
- julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
- julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
- julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
- julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
- julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
- julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
- julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/METADATA +2 -1
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/RECORD +101 -16
- julee/docs/sphinx_hcd/accelerators.py +0 -1175
- julee/docs/sphinx_hcd/apps.py +0 -518
- julee/docs/sphinx_hcd/epics.py +0 -453
- julee/docs/sphinx_hcd/integrations.py +0 -310
- julee/docs/sphinx_hcd/journeys.py +0 -797
- julee/docs/sphinx_hcd/personas.py +0 -457
- julee/docs/sphinx_hcd/stories.py +0 -960
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/WHEEL +0 -0
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Persona directives for sphinx_hcd.
|
|
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
|
+
import os
|
|
11
|
+
|
|
12
|
+
from docutils import nodes
|
|
13
|
+
|
|
14
|
+
from ...domain.use_cases import (
|
|
15
|
+
derive_personas,
|
|
16
|
+
derive_personas_by_app_type,
|
|
17
|
+
get_epics_for_persona,
|
|
18
|
+
)
|
|
19
|
+
from ...utils import normalize_name, slugify
|
|
20
|
+
from .base import HCDDirective
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PersonaDiagramPlaceholder(nodes.General, nodes.Element):
|
|
24
|
+
"""Placeholder node for persona-diagram, replaced at doctree-resolved."""
|
|
25
|
+
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PersonaIndexDiagramPlaceholder(nodes.General, nodes.Element):
|
|
30
|
+
"""Placeholder node for persona-index-diagram, replaced at doctree-resolved."""
|
|
31
|
+
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PersonaDiagramDirective(HCDDirective):
|
|
36
|
+
"""Generate PlantUML use case diagram for a single persona.
|
|
37
|
+
|
|
38
|
+
Usage::
|
|
39
|
+
|
|
40
|
+
.. persona-diagram:: Pilot Manager
|
|
41
|
+
|
|
42
|
+
Generates a diagram showing:
|
|
43
|
+
- The persona as an actor
|
|
44
|
+
- Epics they participate in as use cases
|
|
45
|
+
- Apps they interact with as components
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
required_arguments = 1
|
|
49
|
+
final_argument_whitespace = True
|
|
50
|
+
|
|
51
|
+
def run(self):
|
|
52
|
+
persona_name = self.arguments[0]
|
|
53
|
+
|
|
54
|
+
# Return placeholder - rendering in doctree-resolved
|
|
55
|
+
node = PersonaDiagramPlaceholder()
|
|
56
|
+
node["persona"] = persona_name
|
|
57
|
+
return [node]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PersonaIndexDiagramDirective(HCDDirective):
|
|
61
|
+
"""Generate PlantUML diagram for a group of personas.
|
|
62
|
+
|
|
63
|
+
Usage::
|
|
64
|
+
|
|
65
|
+
.. persona-index-diagram:: staff
|
|
66
|
+
.. persona-index-diagram:: customers
|
|
67
|
+
.. persona-index-diagram:: vendors
|
|
68
|
+
|
|
69
|
+
Groups are determined by the type field from app.yaml manifests.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
required_arguments = 1
|
|
73
|
+
option_spec = {}
|
|
74
|
+
|
|
75
|
+
def run(self):
|
|
76
|
+
group_type = self.arguments[0].lower()
|
|
77
|
+
|
|
78
|
+
# Return placeholder - rendering in doctree-resolved
|
|
79
|
+
node = PersonaIndexDiagramPlaceholder()
|
|
80
|
+
node["group_type"] = group_type
|
|
81
|
+
return [node]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_apps_for_epic(epic, all_stories) -> set[str]:
|
|
85
|
+
"""Get the set of app slugs used by stories in an epic."""
|
|
86
|
+
apps = set()
|
|
87
|
+
|
|
88
|
+
# Build lookup of story title -> app
|
|
89
|
+
story_apps = {}
|
|
90
|
+
for story in all_stories:
|
|
91
|
+
story_apps[normalize_name(story.feature_title)] = story.app_slug
|
|
92
|
+
|
|
93
|
+
for story_title in epic.story_refs:
|
|
94
|
+
story_normalized = normalize_name(story_title)
|
|
95
|
+
if story_normalized in story_apps:
|
|
96
|
+
apps.add(story_apps[story_normalized])
|
|
97
|
+
|
|
98
|
+
return apps
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def generate_persona_plantuml(persona, all_epics, all_stories, all_apps) -> str:
|
|
102
|
+
"""Generate PlantUML for a single persona's use case diagram."""
|
|
103
|
+
persona_id = slugify(persona.name).replace("-", "_")
|
|
104
|
+
app_lookup = {a.slug: a for a in all_apps}
|
|
105
|
+
|
|
106
|
+
lines = [
|
|
107
|
+
f"@startuml persona-{slugify(persona.name)}",
|
|
108
|
+
"left to right direction",
|
|
109
|
+
"skinparam actorStyle awesome",
|
|
110
|
+
"",
|
|
111
|
+
f'actor "{persona.name}" as {persona_id}',
|
|
112
|
+
"",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
# Get epics for this persona
|
|
116
|
+
epics = get_epics_for_persona(persona, all_epics, all_stories)
|
|
117
|
+
|
|
118
|
+
# Collect all apps used by this persona's epics
|
|
119
|
+
all_epic_apps = set()
|
|
120
|
+
epic_apps_map = {}
|
|
121
|
+
|
|
122
|
+
for epic in epics:
|
|
123
|
+
apps = get_apps_for_epic(epic, all_stories)
|
|
124
|
+
epic_apps_map[epic.slug] = apps
|
|
125
|
+
all_epic_apps.update(apps)
|
|
126
|
+
|
|
127
|
+
# Generate component declarations for apps
|
|
128
|
+
for app_slug in sorted(all_epic_apps):
|
|
129
|
+
app_id = app_slug.replace("-", "_")
|
|
130
|
+
app = app_lookup.get(app_slug)
|
|
131
|
+
app_name = app.name if app else app_slug.replace("-", " ").title()
|
|
132
|
+
lines.append(f'component "{app_name}" as {app_id}')
|
|
133
|
+
|
|
134
|
+
lines.append("")
|
|
135
|
+
|
|
136
|
+
# Generate usecase declarations for epics
|
|
137
|
+
for epic in epics:
|
|
138
|
+
epic_id = epic.slug.replace("-", "_")
|
|
139
|
+
epic_name = epic.slug.replace("-", " ").title()
|
|
140
|
+
lines.append(f'usecase "{epic_name}" as {epic_id}')
|
|
141
|
+
|
|
142
|
+
lines.append("")
|
|
143
|
+
|
|
144
|
+
# Generate persona -> epic connections
|
|
145
|
+
for epic in epics:
|
|
146
|
+
epic_id = epic.slug.replace("-", "_")
|
|
147
|
+
lines.append(f"{persona_id} --> {epic_id}")
|
|
148
|
+
|
|
149
|
+
lines.append("")
|
|
150
|
+
|
|
151
|
+
# Generate epic -> app connections
|
|
152
|
+
for epic in epics:
|
|
153
|
+
epic_id = epic.slug.replace("-", "_")
|
|
154
|
+
for app_slug in sorted(epic_apps_map.get(epic.slug, [])):
|
|
155
|
+
app_id = app_slug.replace("-", "_")
|
|
156
|
+
lines.append(f"{epic_id} --> {app_id}")
|
|
157
|
+
|
|
158
|
+
lines.append("")
|
|
159
|
+
lines.append("@enduml")
|
|
160
|
+
|
|
161
|
+
return "\n".join(lines)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def generate_persona_index_plantuml(
|
|
165
|
+
group_type: str, personas, all_epics, all_stories, all_apps
|
|
166
|
+
) -> str:
|
|
167
|
+
"""Generate PlantUML for a group of personas."""
|
|
168
|
+
app_lookup = {a.slug: a for a in all_apps}
|
|
169
|
+
|
|
170
|
+
lines = [
|
|
171
|
+
f"@startuml persona-{group_type}",
|
|
172
|
+
"left to right direction",
|
|
173
|
+
"skinparam actorStyle awesome",
|
|
174
|
+
"",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
# Collect data for all personas
|
|
178
|
+
persona_epics_map = {}
|
|
179
|
+
all_epic_apps = set()
|
|
180
|
+
epic_apps_map = {}
|
|
181
|
+
|
|
182
|
+
for persona in personas:
|
|
183
|
+
epics = get_epics_for_persona(persona, all_epics, all_stories)
|
|
184
|
+
persona_epics_map[persona.name] = epics
|
|
185
|
+
|
|
186
|
+
for epic in epics:
|
|
187
|
+
if epic.slug not in epic_apps_map:
|
|
188
|
+
apps = get_apps_for_epic(epic, all_stories)
|
|
189
|
+
epic_apps_map[epic.slug] = apps
|
|
190
|
+
all_epic_apps.update(apps)
|
|
191
|
+
|
|
192
|
+
# Generate actor declarations
|
|
193
|
+
for persona in sorted(personas, key=lambda p: p.name):
|
|
194
|
+
persona_id = slugify(persona.name).replace("-", "_")
|
|
195
|
+
lines.append(f'actor "{persona.name}" as {persona_id}')
|
|
196
|
+
|
|
197
|
+
lines.append("")
|
|
198
|
+
|
|
199
|
+
# Generate component declarations for apps
|
|
200
|
+
for app_slug in sorted(all_epic_apps):
|
|
201
|
+
app_id = app_slug.replace("-", "_")
|
|
202
|
+
app = app_lookup.get(app_slug)
|
|
203
|
+
app_name = app.name if app else app_slug.replace("-", " ").title()
|
|
204
|
+
lines.append(f'component "{app_name}" as {app_id}')
|
|
205
|
+
|
|
206
|
+
lines.append("")
|
|
207
|
+
|
|
208
|
+
# Collect unique epics
|
|
209
|
+
all_group_epics = set()
|
|
210
|
+
for epics in persona_epics_map.values():
|
|
211
|
+
all_group_epics.update(e.slug for e in epics)
|
|
212
|
+
|
|
213
|
+
# Generate usecase declarations for epics
|
|
214
|
+
for epic_slug in sorted(all_group_epics):
|
|
215
|
+
epic_id = epic_slug.replace("-", "_")
|
|
216
|
+
epic_name = epic_slug.replace("-", " ").title()
|
|
217
|
+
lines.append(f'usecase "{epic_name}" as {epic_id}')
|
|
218
|
+
|
|
219
|
+
lines.append("")
|
|
220
|
+
|
|
221
|
+
# Generate persona -> epic connections
|
|
222
|
+
for persona in sorted(personas, key=lambda p: p.name):
|
|
223
|
+
persona_id = slugify(persona.name).replace("-", "_")
|
|
224
|
+
for epic in sorted(
|
|
225
|
+
persona_epics_map.get(persona.name, []), key=lambda e: e.slug
|
|
226
|
+
):
|
|
227
|
+
epic_id = epic.slug.replace("-", "_")
|
|
228
|
+
lines.append(f"{persona_id} --> {epic_id}")
|
|
229
|
+
|
|
230
|
+
lines.append("")
|
|
231
|
+
|
|
232
|
+
# Generate epic -> app connections
|
|
233
|
+
for epic_slug in sorted(all_group_epics):
|
|
234
|
+
epic_id = epic_slug.replace("-", "_")
|
|
235
|
+
for app_slug in sorted(epic_apps_map.get(epic_slug, [])):
|
|
236
|
+
app_id = app_slug.replace("-", "_")
|
|
237
|
+
lines.append(f"{epic_id} --> {app_id}")
|
|
238
|
+
|
|
239
|
+
lines.append("")
|
|
240
|
+
lines.append("@enduml")
|
|
241
|
+
|
|
242
|
+
return "\n".join(lines)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def build_persona_diagram(persona_name: str, docname: str, hcd_context):
|
|
246
|
+
"""Build the PlantUML diagram for a single persona."""
|
|
247
|
+
try:
|
|
248
|
+
from sphinxcontrib.plantuml import plantuml
|
|
249
|
+
except ImportError:
|
|
250
|
+
para = nodes.paragraph()
|
|
251
|
+
para += nodes.emphasis(text="PlantUML extension not available")
|
|
252
|
+
return [para]
|
|
253
|
+
|
|
254
|
+
all_stories = hcd_context.story_repo.list_all()
|
|
255
|
+
all_epics = hcd_context.epic_repo.list_all()
|
|
256
|
+
all_apps = hcd_context.app_repo.list_all()
|
|
257
|
+
|
|
258
|
+
# Derive personas
|
|
259
|
+
personas = derive_personas(all_stories, all_epics)
|
|
260
|
+
persona_normalized = normalize_name(persona_name)
|
|
261
|
+
|
|
262
|
+
# Find the persona
|
|
263
|
+
persona = None
|
|
264
|
+
for p in personas:
|
|
265
|
+
if p.normalized_name == persona_normalized:
|
|
266
|
+
persona = p
|
|
267
|
+
break
|
|
268
|
+
|
|
269
|
+
if not persona:
|
|
270
|
+
para = nodes.paragraph()
|
|
271
|
+
para += nodes.emphasis(text=f"No persona found: '{persona_name}'")
|
|
272
|
+
return [para]
|
|
273
|
+
|
|
274
|
+
# Check if persona has epics
|
|
275
|
+
epics = get_epics_for_persona(persona, all_epics, all_stories)
|
|
276
|
+
if not epics:
|
|
277
|
+
para = nodes.paragraph()
|
|
278
|
+
para += nodes.emphasis(text=f"No epics found for persona '{persona_name}'")
|
|
279
|
+
return [para]
|
|
280
|
+
|
|
281
|
+
# Generate PlantUML
|
|
282
|
+
puml_source = generate_persona_plantuml(persona, all_epics, all_stories, all_apps)
|
|
283
|
+
|
|
284
|
+
# Create plantuml node
|
|
285
|
+
node = plantuml(puml_source)
|
|
286
|
+
node["uml"] = puml_source
|
|
287
|
+
node["incdir"] = os.path.dirname(docname)
|
|
288
|
+
node["filename"] = os.path.basename(docname)
|
|
289
|
+
|
|
290
|
+
return [node]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def build_persona_index_diagram(group_type: str, docname: str, hcd_context):
|
|
294
|
+
"""Build the PlantUML diagram for a persona group."""
|
|
295
|
+
try:
|
|
296
|
+
from sphinxcontrib.plantuml import plantuml
|
|
297
|
+
except ImportError:
|
|
298
|
+
para = nodes.paragraph()
|
|
299
|
+
para += nodes.emphasis(text="PlantUML extension not available")
|
|
300
|
+
return [para]
|
|
301
|
+
|
|
302
|
+
all_stories = hcd_context.story_repo.list_all()
|
|
303
|
+
all_epics = hcd_context.epic_repo.list_all()
|
|
304
|
+
all_apps = hcd_context.app_repo.list_all()
|
|
305
|
+
|
|
306
|
+
# Get personas grouped by app type
|
|
307
|
+
personas_by_type = derive_personas_by_app_type(all_stories, all_epics, all_apps)
|
|
308
|
+
personas = sorted(personas_by_type.get(group_type, []), key=lambda p: p.name)
|
|
309
|
+
|
|
310
|
+
if not personas:
|
|
311
|
+
para = nodes.paragraph()
|
|
312
|
+
para += nodes.emphasis(text=f"No {group_type} personas found")
|
|
313
|
+
return [para]
|
|
314
|
+
|
|
315
|
+
# Generate PlantUML
|
|
316
|
+
puml_source = generate_persona_index_plantuml(
|
|
317
|
+
group_type, personas, all_epics, all_stories, all_apps
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Create plantuml node
|
|
321
|
+
node = plantuml(puml_source)
|
|
322
|
+
node["uml"] = puml_source
|
|
323
|
+
node["incdir"] = os.path.dirname(docname)
|
|
324
|
+
node["filename"] = os.path.basename(docname)
|
|
325
|
+
|
|
326
|
+
return [node]
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def process_persona_placeholders(app, doctree, docname):
|
|
330
|
+
"""Replace persona diagram placeholders with rendered content."""
|
|
331
|
+
from ..context import get_hcd_context
|
|
332
|
+
|
|
333
|
+
hcd_context = get_hcd_context(app)
|
|
334
|
+
|
|
335
|
+
# Process persona-diagram placeholders
|
|
336
|
+
for node in doctree.traverse(PersonaDiagramPlaceholder):
|
|
337
|
+
persona = node["persona"]
|
|
338
|
+
content = build_persona_diagram(persona, docname, hcd_context)
|
|
339
|
+
node.replace_self(content)
|
|
340
|
+
|
|
341
|
+
# Process persona-index-diagram placeholders
|
|
342
|
+
for node in doctree.traverse(PersonaIndexDiagramPlaceholder):
|
|
343
|
+
group_type = node["group_type"]
|
|
344
|
+
content = build_persona_index_diagram(group_type, docname, hcd_context)
|
|
345
|
+
node.replace_self(content)
|