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.
Files changed (82) hide show
  1. julee/__init__.py +1 -1
  2. julee/api/tests/routers/test_assembly_specifications.py +2 -0
  3. julee/api/tests/routers/test_documents.py +8 -6
  4. julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
  5. julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
  6. julee/api/tests/routers/test_system.py +2 -0
  7. julee/api/tests/routers/test_workflows.py +2 -0
  8. julee/api/tests/test_app.py +2 -0
  9. julee/api/tests/test_dependencies.py +2 -0
  10. julee/api/tests/test_requests.py +2 -0
  11. julee/contrib/polling/__init__.py +22 -19
  12. julee/contrib/polling/apps/__init__.py +17 -0
  13. julee/contrib/polling/apps/worker/__init__.py +17 -0
  14. julee/contrib/polling/apps/worker/pipelines.py +288 -0
  15. julee/contrib/polling/domain/__init__.py +7 -9
  16. julee/contrib/polling/domain/models/__init__.py +6 -7
  17. julee/contrib/polling/domain/models/polling_config.py +18 -1
  18. julee/contrib/polling/domain/services/__init__.py +6 -5
  19. julee/contrib/polling/domain/services/poller.py +1 -1
  20. julee/contrib/polling/infrastructure/__init__.py +9 -8
  21. julee/contrib/polling/infrastructure/services/__init__.py +6 -5
  22. julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
  23. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
  24. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
  25. julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
  26. julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
  27. julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
  28. julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
  29. julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
  30. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
  31. julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
  32. julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
  33. julee/docs/sphinx_hcd/__init__.py +4 -10
  34. julee/docs/sphinx_hcd/accelerators.py +277 -180
  35. julee/docs/sphinx_hcd/apps.py +78 -59
  36. julee/docs/sphinx_hcd/config.py +16 -16
  37. julee/docs/sphinx_hcd/epics.py +47 -42
  38. julee/docs/sphinx_hcd/integrations.py +53 -49
  39. julee/docs/sphinx_hcd/journeys.py +124 -110
  40. julee/docs/sphinx_hcd/personas.py +75 -53
  41. julee/docs/sphinx_hcd/stories.py +99 -71
  42. julee/docs/sphinx_hcd/utils.py +23 -18
  43. julee/domain/models/assembly/tests/test_assembly.py +2 -0
  44. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
  45. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
  46. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
  47. julee/domain/models/document/document.py +12 -21
  48. julee/domain/models/document/tests/test_document.py +16 -34
  49. julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
  50. julee/domain/models/policy/tests/test_policy.py +2 -0
  51. julee/domain/use_cases/extract_assemble_data.py +1 -1
  52. julee/domain/use_cases/initialize_system_data.py +75 -21
  53. julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
  54. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
  55. julee/domain/use_cases/tests/test_validate_document.py +2 -0
  56. julee/fixtures/documents.yaml +4 -43
  57. julee/fixtures/knowledge_service_queries.yaml +9 -0
  58. julee/maintenance/release.py +90 -30
  59. julee/repositories/memory/document.py +19 -13
  60. julee/repositories/memory/tests/test_document.py +20 -18
  61. julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
  62. julee/repositories/memory/tests/test_policy.py +2 -0
  63. julee/repositories/minio/document.py +25 -22
  64. julee/repositories/minio/tests/test_assembly.py +2 -0
  65. julee/repositories/minio/tests/test_assembly_specification.py +2 -0
  66. julee/repositories/minio/tests/test_client_protocol.py +3 -0
  67. julee/repositories/minio/tests/test_document.py +18 -16
  68. julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
  69. julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
  70. julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
  71. julee/repositories/minio/tests/test_policy.py +2 -0
  72. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
  73. julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
  74. julee/services/knowledge_service/test_factory.py +2 -0
  75. julee/util/tests/test_decorators.py +2 -0
  76. julee-0.1.5.dist-info/METADATA +103 -0
  77. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/RECORD +80 -74
  78. julee/fixtures/assembly_specifications.yaml +0 -70
  79. julee-0.1.3.dist-info/METADATA +0 -198
  80. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/WHEEL +0 -0
  81. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/licenses/LICENSE +0 -0
  82. {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(persona_name: str, epic_registry: dict, story_registry: list) -> list[tuple[str, dict]]:
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['feature'])] = story['persona_normalized']
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('stories', []):
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['feature'])] = story['app']
68
+ story_apps[normalize_name(story["feature"])] = story["app"]
67
69
 
68
- for story_title in epic.get('stories', []):
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['persona_normalized'] == persona_normalized:
91
- apps.add(story['app'])
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(persona_name: str, epics: list[tuple[str, dict]],
97
- story_registry: list, app_registry: dict) -> str:
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('name', app_slug.replace('-', ' ').title())
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, epic in epics:
139
- epic_id = epic_slug.replace('-', '_')
140
- epic_name = epic_slug.replace('-', ' ').title()
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, epic in epics:
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, epic in epics:
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(persona_type: str, personas: list[str],
166
- epic_registry: dict, story_registry: list,
167
- app_registry: dict) -> str:
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('name', app_slug.replace('-', ' ').title())
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('-', ' ').title()
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['persona'] = persona_name
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['group_type'] = group_type
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(story_registry: list, app_registry: dict) -> dict[str, set[str]]:
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['app']
327
- persona = story['persona']
344
+ app_slug = story["app"]
345
+ persona = story["persona"]
328
346
 
329
- if persona == 'unknown':
347
+ if persona == "unknown":
330
348
  continue
331
349
 
332
350
  app_data = app_registry.get(app_slug, {})
333
- app_type = app_data.get('type', 'unknown').lower()
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['uml'] = puml_source
366
- node['incdir'] = os.path.dirname(docname)
367
- node['filename'] = os.path.basename(docname)
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['uml'] = puml_source
399
- node['incdir'] = os.path.dirname(docname)
400
- node['filename'] = os.path.basename(docname)
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['persona']
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['group_type']
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