julee 0.1.3__py3-none-any.whl → 0.1.5__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/api/tests/routers/test_assembly_specifications.py +2 -0
- julee/api/tests/routers/test_documents.py +8 -6
- julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
- julee/api/tests/routers/test_system.py +2 -0
- julee/api/tests/routers/test_workflows.py +2 -0
- julee/api/tests/test_app.py +2 -0
- julee/api/tests/test_dependencies.py +2 -0
- julee/api/tests/test_requests.py +2 -0
- julee/contrib/polling/__init__.py +22 -19
- julee/contrib/polling/apps/__init__.py +17 -0
- julee/contrib/polling/apps/worker/__init__.py +17 -0
- julee/contrib/polling/apps/worker/pipelines.py +288 -0
- julee/contrib/polling/domain/__init__.py +7 -9
- julee/contrib/polling/domain/models/__init__.py +6 -7
- julee/contrib/polling/domain/models/polling_config.py +18 -1
- julee/contrib/polling/domain/services/__init__.py +6 -5
- julee/contrib/polling/domain/services/poller.py +1 -1
- julee/contrib/polling/infrastructure/__init__.py +9 -8
- julee/contrib/polling/infrastructure/services/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
- julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
- julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
- julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
- julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
- julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
- julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
- julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
- julee/docs/sphinx_hcd/__init__.py +4 -10
- julee/docs/sphinx_hcd/accelerators.py +277 -180
- julee/docs/sphinx_hcd/apps.py +78 -59
- julee/docs/sphinx_hcd/config.py +16 -16
- julee/docs/sphinx_hcd/epics.py +47 -42
- julee/docs/sphinx_hcd/integrations.py +53 -49
- julee/docs/sphinx_hcd/journeys.py +124 -110
- julee/docs/sphinx_hcd/personas.py +75 -53
- julee/docs/sphinx_hcd/stories.py +99 -71
- julee/docs/sphinx_hcd/utils.py +23 -18
- julee/domain/models/assembly/tests/test_assembly.py +2 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
- julee/domain/models/document/document.py +12 -21
- julee/domain/models/document/tests/test_document.py +16 -34
- julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
- julee/domain/models/policy/tests/test_policy.py +2 -0
- julee/domain/use_cases/extract_assemble_data.py +1 -1
- julee/domain/use_cases/initialize_system_data.py +75 -21
- julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
- julee/domain/use_cases/tests/test_validate_document.py +2 -0
- julee/fixtures/documents.yaml +4 -43
- julee/fixtures/knowledge_service_queries.yaml +9 -0
- julee/maintenance/release.py +90 -30
- julee/repositories/memory/document.py +19 -13
- julee/repositories/memory/tests/test_document.py +20 -18
- julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
- julee/repositories/memory/tests/test_policy.py +2 -0
- julee/repositories/minio/document.py +25 -22
- julee/repositories/minio/tests/test_assembly.py +2 -0
- julee/repositories/minio/tests/test_assembly_specification.py +2 -0
- julee/repositories/minio/tests/test_client_protocol.py +3 -0
- julee/repositories/minio/tests/test_document.py +18 -16
- julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
- julee/repositories/minio/tests/test_policy.py +2 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/test_factory.py +2 -0
- julee/util/tests/test_decorators.py +2 -0
- julee-0.1.5.dist-info/METADATA +103 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/RECORD +80 -74
- julee/fixtures/assembly_specifications.yaml +0 -70
- julee-0.1.3.dist-info/METADATA +0 -198
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/WHEEL +0 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -8,17 +8,19 @@ Provides directives:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from collections import defaultdict
|
|
11
|
+
|
|
11
12
|
from docutils import nodes
|
|
12
|
-
from sphinx.util.docutils import SphinxDirective
|
|
13
13
|
from sphinx.util import logging
|
|
14
|
+
from sphinx.util.docutils import SphinxDirective
|
|
14
15
|
|
|
15
|
-
from .config import get_config
|
|
16
16
|
from .utils import normalize_name, slugify
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def get_epics_for_persona(
|
|
21
|
+
def get_epics_for_persona(
|
|
22
|
+
persona_name: str, epic_registry: dict, story_registry: list
|
|
23
|
+
) -> list[tuple[str, dict]]:
|
|
22
24
|
"""Get epics that contain stories for a given persona.
|
|
23
25
|
|
|
24
26
|
Args:
|
|
@@ -34,12 +36,12 @@ def get_epics_for_persona(persona_name: str, epic_registry: dict, story_registry
|
|
|
34
36
|
# Build lookup of story title -> persona
|
|
35
37
|
story_personas = {}
|
|
36
38
|
for story in story_registry:
|
|
37
|
-
story_personas[normalize_name(story[
|
|
39
|
+
story_personas[normalize_name(story["feature"])] = story["persona_normalized"]
|
|
38
40
|
|
|
39
41
|
matching_epics = []
|
|
40
42
|
for slug, epic in epic_registry.items():
|
|
41
43
|
# Check if any story in this epic belongs to the persona
|
|
42
|
-
for story_title in epic.get(
|
|
44
|
+
for story_title in epic.get("stories", []):
|
|
43
45
|
story_normalized = normalize_name(story_title)
|
|
44
46
|
if story_personas.get(story_normalized) == persona_normalized:
|
|
45
47
|
matching_epics.append((slug, epic))
|
|
@@ -63,9 +65,9 @@ def get_apps_for_epic(epic: dict, story_registry: list) -> set[str]:
|
|
|
63
65
|
# Build lookup of story title -> app
|
|
64
66
|
story_apps = {}
|
|
65
67
|
for story in story_registry:
|
|
66
|
-
story_apps[normalize_name(story[
|
|
68
|
+
story_apps[normalize_name(story["feature"])] = story["app"]
|
|
67
69
|
|
|
68
|
-
for story_title in epic.get(
|
|
70
|
+
for story_title in epic.get("stories", []):
|
|
69
71
|
story_normalized = normalize_name(story_title)
|
|
70
72
|
if story_normalized in story_apps:
|
|
71
73
|
apps.add(story_apps[story_normalized])
|
|
@@ -87,14 +89,18 @@ def get_apps_for_persona(persona_name: str, story_registry: list) -> set[str]:
|
|
|
87
89
|
apps = set()
|
|
88
90
|
|
|
89
91
|
for story in story_registry:
|
|
90
|
-
if story[
|
|
91
|
-
apps.add(story[
|
|
92
|
+
if story["persona_normalized"] == persona_normalized:
|
|
93
|
+
apps.add(story["app"])
|
|
92
94
|
|
|
93
95
|
return apps
|
|
94
96
|
|
|
95
97
|
|
|
96
|
-
def generate_persona_plantuml(
|
|
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:
|
|
98
104
|
"""Generate PlantUML for a single persona's use case diagram.
|
|
99
105
|
|
|
100
106
|
Args:
|
|
@@ -106,7 +112,7 @@ def generate_persona_plantuml(persona_name: str, epics: list[tuple[str, dict]],
|
|
|
106
112
|
Returns:
|
|
107
113
|
PlantUML source string
|
|
108
114
|
"""
|
|
109
|
-
persona_id = slugify(persona_name).replace(
|
|
115
|
+
persona_id = slugify(persona_name).replace("-", "_")
|
|
110
116
|
|
|
111
117
|
lines = [
|
|
112
118
|
f"@startuml persona-{slugify(persona_name)}",
|
|
@@ -128,32 +134,34 @@ def generate_persona_plantuml(persona_name: str, epics: list[tuple[str, dict]],
|
|
|
128
134
|
|
|
129
135
|
# Generate component declarations for apps
|
|
130
136
|
for app_slug in sorted(all_apps):
|
|
131
|
-
app_id = app_slug.replace(
|
|
132
|
-
app_name = app_registry.get(app_slug, {}).get(
|
|
137
|
+
app_id = app_slug.replace("-", "_")
|
|
138
|
+
app_name = app_registry.get(app_slug, {}).get(
|
|
139
|
+
"name", app_slug.replace("-", " ").title()
|
|
140
|
+
)
|
|
133
141
|
lines.append(f'component "{app_name}" as {app_id}')
|
|
134
142
|
|
|
135
143
|
lines.append("")
|
|
136
144
|
|
|
137
145
|
# Generate usecase declarations for epics
|
|
138
|
-
for epic_slug,
|
|
139
|
-
epic_id = epic_slug.replace(
|
|
140
|
-
epic_name = epic_slug.replace(
|
|
146
|
+
for epic_slug, _epic in epics:
|
|
147
|
+
epic_id = epic_slug.replace("-", "_")
|
|
148
|
+
epic_name = epic_slug.replace("-", " ").title()
|
|
141
149
|
lines.append(f'usecase "{epic_name}" as {epic_id}')
|
|
142
150
|
|
|
143
151
|
lines.append("")
|
|
144
152
|
|
|
145
153
|
# Generate persona -> epic connections
|
|
146
|
-
for epic_slug,
|
|
147
|
-
epic_id = epic_slug.replace(
|
|
154
|
+
for epic_slug, _epic in epics:
|
|
155
|
+
epic_id = epic_slug.replace("-", "_")
|
|
148
156
|
lines.append(f"{persona_id} --> {epic_id}")
|
|
149
157
|
|
|
150
158
|
lines.append("")
|
|
151
159
|
|
|
152
160
|
# Generate epic -> app connections
|
|
153
|
-
for epic_slug,
|
|
154
|
-
epic_id = epic_slug.replace(
|
|
161
|
+
for epic_slug, _epic in epics:
|
|
162
|
+
epic_id = epic_slug.replace("-", "_")
|
|
155
163
|
for app_slug in sorted(epic_apps.get(epic_slug, [])):
|
|
156
|
-
app_id = app_slug.replace(
|
|
164
|
+
app_id = app_slug.replace("-", "_")
|
|
157
165
|
lines.append(f"{epic_id} --> {app_id}")
|
|
158
166
|
|
|
159
167
|
lines.append("")
|
|
@@ -162,9 +170,13 @@ def generate_persona_plantuml(persona_name: str, epics: list[tuple[str, dict]],
|
|
|
162
170
|
return "\n".join(lines)
|
|
163
171
|
|
|
164
172
|
|
|
165
|
-
def generate_persona_index_plantuml(
|
|
166
|
-
|
|
167
|
-
|
|
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:
|
|
168
180
|
"""Generate PlantUML for a group of personas (staff or external).
|
|
169
181
|
|
|
170
182
|
Args:
|
|
@@ -201,15 +213,17 @@ def generate_persona_index_plantuml(persona_type: str, personas: list[str],
|
|
|
201
213
|
|
|
202
214
|
# Generate actor declarations
|
|
203
215
|
for persona_name in sorted(personas):
|
|
204
|
-
persona_id = slugify(persona_name).replace(
|
|
216
|
+
persona_id = slugify(persona_name).replace("-", "_")
|
|
205
217
|
lines.append(f'actor "{persona_name}" as {persona_id}')
|
|
206
218
|
|
|
207
219
|
lines.append("")
|
|
208
220
|
|
|
209
221
|
# Generate component declarations for apps
|
|
210
222
|
for app_slug in sorted(all_apps):
|
|
211
|
-
app_id = app_slug.replace(
|
|
212
|
-
app_name = app_registry.get(app_slug, {}).get(
|
|
223
|
+
app_id = app_slug.replace("-", "_")
|
|
224
|
+
app_name = app_registry.get(app_slug, {}).get(
|
|
225
|
+
"name", app_slug.replace("-", " ").title()
|
|
226
|
+
)
|
|
213
227
|
lines.append(f'component "{app_name}" as {app_id}')
|
|
214
228
|
|
|
215
229
|
lines.append("")
|
|
@@ -221,26 +235,26 @@ def generate_persona_index_plantuml(persona_type: str, personas: list[str],
|
|
|
221
235
|
|
|
222
236
|
# Generate usecase declarations for epics
|
|
223
237
|
for epic_slug in sorted(all_epics):
|
|
224
|
-
epic_id = epic_slug.replace(
|
|
225
|
-
epic_name = epic_slug.replace(
|
|
238
|
+
epic_id = epic_slug.replace("-", "_")
|
|
239
|
+
epic_name = epic_slug.replace("-", " ").title()
|
|
226
240
|
lines.append(f'usecase "{epic_name}" as {epic_id}')
|
|
227
241
|
|
|
228
242
|
lines.append("")
|
|
229
243
|
|
|
230
244
|
# Generate persona -> epic connections
|
|
231
245
|
for persona_name in sorted(personas):
|
|
232
|
-
persona_id = slugify(persona_name).replace(
|
|
246
|
+
persona_id = slugify(persona_name).replace("-", "_")
|
|
233
247
|
for epic_slug in sorted(persona_epics.get(persona_name, [])):
|
|
234
|
-
epic_id = epic_slug.replace(
|
|
248
|
+
epic_id = epic_slug.replace("-", "_")
|
|
235
249
|
lines.append(f"{persona_id} --> {epic_id}")
|
|
236
250
|
|
|
237
251
|
lines.append("")
|
|
238
252
|
|
|
239
253
|
# Generate epic -> app connections
|
|
240
254
|
for epic_slug in sorted(all_epics):
|
|
241
|
-
epic_id = epic_slug.replace(
|
|
255
|
+
epic_id = epic_slug.replace("-", "_")
|
|
242
256
|
for app_slug in sorted(epic_app_map.get(epic_slug, [])):
|
|
243
|
-
app_id = app_slug.replace(
|
|
257
|
+
app_id = app_slug.replace("-", "_")
|
|
244
258
|
lines.append(f"{epic_id} --> {app_id}")
|
|
245
259
|
|
|
246
260
|
lines.append("")
|
|
@@ -270,12 +284,13 @@ class PersonaDiagramDirective(SphinxDirective):
|
|
|
270
284
|
|
|
271
285
|
# Return placeholder - actual rendering in doctree-resolved
|
|
272
286
|
node = PersonaDiagramPlaceholder()
|
|
273
|
-
node[
|
|
287
|
+
node["persona"] = persona_name
|
|
274
288
|
return [node]
|
|
275
289
|
|
|
276
290
|
|
|
277
291
|
class PersonaDiagramPlaceholder(nodes.General, nodes.Element):
|
|
278
292
|
"""Placeholder node for persona-diagram, replaced at doctree-resolved."""
|
|
293
|
+
|
|
279
294
|
pass
|
|
280
295
|
|
|
281
296
|
|
|
@@ -301,16 +316,19 @@ class PersonaIndexDiagramDirective(SphinxDirective):
|
|
|
301
316
|
|
|
302
317
|
# Return placeholder - actual rendering in doctree-resolved
|
|
303
318
|
node = PersonaIndexDiagramPlaceholder()
|
|
304
|
-
node[
|
|
319
|
+
node["group_type"] = group_type
|
|
305
320
|
return [node]
|
|
306
321
|
|
|
307
322
|
|
|
308
323
|
class PersonaIndexDiagramPlaceholder(nodes.General, nodes.Element):
|
|
309
324
|
"""Placeholder node for persona-index-diagram, replaced at doctree-resolved."""
|
|
325
|
+
|
|
310
326
|
pass
|
|
311
327
|
|
|
312
328
|
|
|
313
|
-
def get_personas_by_app_type(
|
|
329
|
+
def get_personas_by_app_type(
|
|
330
|
+
story_registry: list, app_registry: dict
|
|
331
|
+
) -> dict[str, set[str]]:
|
|
314
332
|
"""Group personas by the type of apps they use.
|
|
315
333
|
|
|
316
334
|
Args:
|
|
@@ -323,14 +341,14 @@ def get_personas_by_app_type(story_registry: list, app_registry: dict) -> dict[s
|
|
|
323
341
|
personas_by_type = defaultdict(set)
|
|
324
342
|
|
|
325
343
|
for story in story_registry:
|
|
326
|
-
app_slug = story[
|
|
327
|
-
persona = story[
|
|
344
|
+
app_slug = story["app"]
|
|
345
|
+
persona = story["persona"]
|
|
328
346
|
|
|
329
|
-
if persona ==
|
|
347
|
+
if persona == "unknown":
|
|
330
348
|
continue
|
|
331
349
|
|
|
332
350
|
app_data = app_registry.get(app_slug, {})
|
|
333
|
-
app_type = app_data.get(
|
|
351
|
+
app_type = app_data.get("type", "unknown").lower()
|
|
334
352
|
|
|
335
353
|
personas_by_type[app_type].add(persona)
|
|
336
354
|
|
|
@@ -339,10 +357,12 @@ def get_personas_by_app_type(story_registry: list, app_registry: dict) -> dict[s
|
|
|
339
357
|
|
|
340
358
|
def build_persona_diagram(persona_name: str, env, docname: str):
|
|
341
359
|
"""Build the PlantUML diagram for a single persona."""
|
|
342
|
-
from . import stories, epics, apps
|
|
343
|
-
from sphinxcontrib.plantuml import plantuml
|
|
344
360
|
import os
|
|
345
361
|
|
|
362
|
+
from sphinxcontrib.plantuml import plantuml
|
|
363
|
+
|
|
364
|
+
from . import apps, epics, stories
|
|
365
|
+
|
|
346
366
|
story_registry = stories.get_story_registry()
|
|
347
367
|
epic_registry = epics.get_epic_registry(env)
|
|
348
368
|
app_registry = apps.get_app_registry()
|
|
@@ -362,19 +382,21 @@ def build_persona_diagram(persona_name: str, env, docname: str):
|
|
|
362
382
|
|
|
363
383
|
# Create plantuml node with required attributes
|
|
364
384
|
node = plantuml(puml_source)
|
|
365
|
-
node[
|
|
366
|
-
node[
|
|
367
|
-
node[
|
|
385
|
+
node["uml"] = puml_source
|
|
386
|
+
node["incdir"] = os.path.dirname(docname)
|
|
387
|
+
node["filename"] = os.path.basename(docname)
|
|
368
388
|
|
|
369
389
|
return [node]
|
|
370
390
|
|
|
371
391
|
|
|
372
392
|
def build_persona_index_diagram(group_type: str, env, docname: str):
|
|
373
393
|
"""Build the PlantUML diagram for a persona group."""
|
|
374
|
-
from . import stories, epics, apps
|
|
375
|
-
from sphinxcontrib.plantuml import plantuml
|
|
376
394
|
import os
|
|
377
395
|
|
|
396
|
+
from sphinxcontrib.plantuml import plantuml
|
|
397
|
+
|
|
398
|
+
from . import apps, epics, stories
|
|
399
|
+
|
|
378
400
|
story_registry = stories.get_story_registry()
|
|
379
401
|
epic_registry = epics.get_epic_registry(env)
|
|
380
402
|
app_registry = apps.get_app_registry()
|
|
@@ -395,9 +417,9 @@ def build_persona_index_diagram(group_type: str, env, docname: str):
|
|
|
395
417
|
|
|
396
418
|
# Create plantuml node with required attributes
|
|
397
419
|
node = plantuml(puml_source)
|
|
398
|
-
node[
|
|
399
|
-
node[
|
|
400
|
-
node[
|
|
420
|
+
node["uml"] = puml_source
|
|
421
|
+
node["incdir"] = os.path.dirname(docname)
|
|
422
|
+
node["filename"] = os.path.basename(docname)
|
|
401
423
|
|
|
402
424
|
return [node]
|
|
403
425
|
|
|
@@ -408,13 +430,13 @@ def process_persona_placeholders(app, doctree, docname):
|
|
|
408
430
|
|
|
409
431
|
# Process persona-diagram placeholders
|
|
410
432
|
for node in doctree.traverse(PersonaDiagramPlaceholder):
|
|
411
|
-
persona = node[
|
|
433
|
+
persona = node["persona"]
|
|
412
434
|
content = build_persona_diagram(persona, env, docname)
|
|
413
435
|
node.replace_self(content)
|
|
414
436
|
|
|
415
437
|
# Process persona-index-diagram placeholders
|
|
416
438
|
for node in doctree.traverse(PersonaIndexDiagramPlaceholder):
|
|
417
|
-
group_type = node[
|
|
439
|
+
group_type = node["group_type"]
|
|
418
440
|
content = build_persona_index_diagram(group_type, env, docname)
|
|
419
441
|
node.replace_self(content)
|
|
420
442
|
|