julee 0.1.0__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 +3 -0
- julee/api/__init__.py +20 -0
- julee/api/app.py +180 -0
- julee/api/dependencies.py +257 -0
- julee/api/requests.py +175 -0
- julee/api/responses.py +43 -0
- julee/api/routers/__init__.py +43 -0
- julee/api/routers/assembly_specifications.py +212 -0
- julee/api/routers/documents.py +182 -0
- julee/api/routers/knowledge_service_configs.py +79 -0
- julee/api/routers/knowledge_service_queries.py +293 -0
- julee/api/routers/system.py +137 -0
- julee/api/routers/workflows.py +234 -0
- julee/api/services/__init__.py +20 -0
- julee/api/services/system_initialization.py +214 -0
- julee/api/tests/__init__.py +14 -0
- julee/api/tests/routers/__init__.py +17 -0
- julee/api/tests/routers/test_assembly_specifications.py +749 -0
- julee/api/tests/routers/test_documents.py +301 -0
- julee/api/tests/routers/test_knowledge_service_configs.py +234 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +738 -0
- julee/api/tests/routers/test_system.py +179 -0
- julee/api/tests/routers/test_workflows.py +393 -0
- julee/api/tests/test_app.py +285 -0
- julee/api/tests/test_dependencies.py +245 -0
- julee/api/tests/test_requests.py +250 -0
- julee/domain/__init__.py +22 -0
- julee/domain/models/__init__.py +49 -0
- julee/domain/models/assembly/__init__.py +17 -0
- julee/domain/models/assembly/assembly.py +103 -0
- julee/domain/models/assembly/tests/__init__.py +0 -0
- julee/domain/models/assembly/tests/factories.py +37 -0
- julee/domain/models/assembly/tests/test_assembly.py +430 -0
- julee/domain/models/assembly_specification/__init__.py +24 -0
- julee/domain/models/assembly_specification/assembly_specification.py +172 -0
- julee/domain/models/assembly_specification/knowledge_service_query.py +123 -0
- julee/domain/models/assembly_specification/tests/__init__.py +0 -0
- julee/domain/models/assembly_specification/tests/factories.py +78 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +490 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +310 -0
- julee/domain/models/custom_fields/__init__.py +0 -0
- julee/domain/models/custom_fields/content_stream.py +68 -0
- julee/domain/models/custom_fields/tests/__init__.py +0 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +53 -0
- julee/domain/models/document/__init__.py +17 -0
- julee/domain/models/document/document.py +150 -0
- julee/domain/models/document/tests/__init__.py +0 -0
- julee/domain/models/document/tests/factories.py +76 -0
- julee/domain/models/document/tests/test_document.py +297 -0
- julee/domain/models/knowledge_service_config/__init__.py +17 -0
- julee/domain/models/knowledge_service_config/knowledge_service_config.py +86 -0
- julee/domain/models/policy/__init__.py +15 -0
- julee/domain/models/policy/document_policy_validation.py +220 -0
- julee/domain/models/policy/policy.py +203 -0
- julee/domain/models/policy/tests/__init__.py +0 -0
- julee/domain/models/policy/tests/factories.py +47 -0
- julee/domain/models/policy/tests/test_document_policy_validation.py +420 -0
- julee/domain/models/policy/tests/test_policy.py +546 -0
- julee/domain/repositories/__init__.py +27 -0
- julee/domain/repositories/assembly.py +45 -0
- julee/domain/repositories/assembly_specification.py +52 -0
- julee/domain/repositories/base.py +146 -0
- julee/domain/repositories/document.py +49 -0
- julee/domain/repositories/document_policy_validation.py +52 -0
- julee/domain/repositories/knowledge_service_config.py +54 -0
- julee/domain/repositories/knowledge_service_query.py +44 -0
- julee/domain/repositories/policy.py +49 -0
- julee/domain/use_cases/__init__.py +17 -0
- julee/domain/use_cases/decorators.py +107 -0
- julee/domain/use_cases/extract_assemble_data.py +649 -0
- julee/domain/use_cases/initialize_system_data.py +842 -0
- julee/domain/use_cases/tests/__init__.py +7 -0
- julee/domain/use_cases/tests/test_extract_assemble_data.py +548 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +455 -0
- julee/domain/use_cases/tests/test_validate_document.py +1228 -0
- julee/domain/use_cases/validate_document.py +736 -0
- julee/fixtures/assembly_specifications.yaml +70 -0
- julee/fixtures/documents.yaml +178 -0
- julee/fixtures/knowledge_service_configs.yaml +37 -0
- julee/fixtures/knowledge_service_queries.yaml +27 -0
- julee/repositories/__init__.py +17 -0
- julee/repositories/memory/__init__.py +31 -0
- julee/repositories/memory/assembly.py +84 -0
- julee/repositories/memory/assembly_specification.py +125 -0
- julee/repositories/memory/base.py +227 -0
- julee/repositories/memory/document.py +149 -0
- julee/repositories/memory/document_policy_validation.py +104 -0
- julee/repositories/memory/knowledge_service_config.py +123 -0
- julee/repositories/memory/knowledge_service_query.py +120 -0
- julee/repositories/memory/policy.py +87 -0
- julee/repositories/memory/tests/__init__.py +0 -0
- julee/repositories/memory/tests/test_document.py +212 -0
- julee/repositories/memory/tests/test_document_policy_validation.py +161 -0
- julee/repositories/memory/tests/test_policy.py +443 -0
- julee/repositories/minio/__init__.py +31 -0
- julee/repositories/minio/assembly.py +103 -0
- julee/repositories/minio/assembly_specification.py +170 -0
- julee/repositories/minio/client.py +570 -0
- julee/repositories/minio/document.py +530 -0
- julee/repositories/minio/document_policy_validation.py +120 -0
- julee/repositories/minio/knowledge_service_config.py +187 -0
- julee/repositories/minio/knowledge_service_query.py +211 -0
- julee/repositories/minio/policy.py +106 -0
- julee/repositories/minio/tests/__init__.py +0 -0
- julee/repositories/minio/tests/fake_client.py +213 -0
- julee/repositories/minio/tests/test_assembly.py +374 -0
- julee/repositories/minio/tests/test_assembly_specification.py +391 -0
- julee/repositories/minio/tests/test_client_protocol.py +57 -0
- julee/repositories/minio/tests/test_document.py +591 -0
- julee/repositories/minio/tests/test_document_policy_validation.py +192 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +374 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +438 -0
- julee/repositories/minio/tests/test_policy.py +559 -0
- julee/repositories/temporal/__init__.py +38 -0
- julee/repositories/temporal/activities.py +114 -0
- julee/repositories/temporal/activity_names.py +34 -0
- julee/repositories/temporal/proxies.py +159 -0
- julee/services/__init__.py +18 -0
- julee/services/knowledge_service/__init__.py +48 -0
- julee/services/knowledge_service/anthropic/__init__.py +12 -0
- julee/services/knowledge_service/anthropic/knowledge_service.py +331 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +318 -0
- julee/services/knowledge_service/factory.py +138 -0
- julee/services/knowledge_service/knowledge_service.py +160 -0
- julee/services/knowledge_service/memory/__init__.py +13 -0
- julee/services/knowledge_service/memory/knowledge_service.py +278 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +345 -0
- julee/services/knowledge_service/test_factory.py +112 -0
- julee/services/temporal/__init__.py +38 -0
- julee/services/temporal/activities.py +86 -0
- julee/services/temporal/activity_names.py +22 -0
- julee/services/temporal/proxies.py +41 -0
- julee/util/__init__.py +0 -0
- julee/util/domain.py +119 -0
- julee/util/repos/__init__.py +0 -0
- julee/util/repos/minio/__init__.py +0 -0
- julee/util/repos/minio/file_storage.py +213 -0
- julee/util/repos/temporal/__init__.py +11 -0
- julee/util/repos/temporal/client_proxies/file_storage.py +68 -0
- julee/util/repos/temporal/data_converter.py +123 -0
- julee/util/repos/temporal/minio_file_storage.py +12 -0
- julee/util/repos/temporal/proxies/__init__.py +0 -0
- julee/util/repos/temporal/proxies/file_storage.py +58 -0
- julee/util/repositories.py +55 -0
- julee/util/temporal/__init__.py +22 -0
- julee/util/temporal/activities.py +123 -0
- julee/util/temporal/decorators.py +473 -0
- julee/util/tests/__init__.py +1 -0
- julee/util/tests/test_decorators.py +770 -0
- julee/util/validation/__init__.py +29 -0
- julee/util/validation/repository.py +100 -0
- julee/util/validation/type_guards.py +369 -0
- julee/worker.py +211 -0
- julee/workflows/__init__.py +26 -0
- julee/workflows/extract_assemble.py +215 -0
- julee/workflows/validate_document.py +228 -0
- julee-0.1.0.dist-info/METADATA +195 -0
- julee-0.1.0.dist-info/RECORD +161 -0
- julee-0.1.0.dist-info/WHEEL +5 -0
- julee-0.1.0.dist-info/licenses/LICENSE +674 -0
- julee-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for API request models.
|
|
3
|
+
|
|
4
|
+
Since the request models delegate validation to domain models, these tests
|
|
5
|
+
focus on verifying the delegation works correctly and that the API-specific
|
|
6
|
+
behavior (like field copying and conversion methods) functions as expected.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pydantic import ValidationError
|
|
12
|
+
|
|
13
|
+
from julee.api.requests import (
|
|
14
|
+
CreateAssemblySpecificationRequest,
|
|
15
|
+
CreateKnowledgeServiceQueryRequest,
|
|
16
|
+
)
|
|
17
|
+
from julee.domain.models import (
|
|
18
|
+
AssemblySpecification,
|
|
19
|
+
AssemblySpecificationStatus,
|
|
20
|
+
KnowledgeServiceQuery,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestCreateAssemblySpecificationRequest:
|
|
25
|
+
"""Test CreateAssemblySpecificationRequest model."""
|
|
26
|
+
|
|
27
|
+
def test_valid_request_creation(self) -> None:
|
|
28
|
+
"""Test that a valid request can be created."""
|
|
29
|
+
request = CreateAssemblySpecificationRequest(
|
|
30
|
+
name="Meeting Minutes",
|
|
31
|
+
applicability="Online video meeting transcripts",
|
|
32
|
+
jsonschema={
|
|
33
|
+
"type": "object",
|
|
34
|
+
"properties": {"title": {"type": "string"}},
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
assert request.name == "Meeting Minutes"
|
|
39
|
+
assert request.applicability == "Online video meeting transcripts"
|
|
40
|
+
assert request.jsonschema == {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {"title": {"type": "string"}},
|
|
43
|
+
}
|
|
44
|
+
assert request.knowledge_service_queries == {} # Default empty dict
|
|
45
|
+
assert request.version == "0.1.0" # Default version
|
|
46
|
+
|
|
47
|
+
def test_validation_delegation_to_domain_model(self) -> None:
|
|
48
|
+
"""Test that validation is properly delegated to domain model."""
|
|
49
|
+
# Test that domain model validation errors are raised
|
|
50
|
+
with pytest.raises(ValidationError) as err:
|
|
51
|
+
CreateAssemblySpecificationRequest(
|
|
52
|
+
name="", # Invalid empty name
|
|
53
|
+
applicability="Valid applicability",
|
|
54
|
+
jsonschema={"type": "object"},
|
|
55
|
+
)
|
|
56
|
+
errors = err.value.errors()
|
|
57
|
+
# Check that the error is for the 'name' field and is a value error
|
|
58
|
+
assert any(
|
|
59
|
+
e["loc"] == ("name",)
|
|
60
|
+
and e["type"].startswith("value_error")
|
|
61
|
+
and "name cannot be empty" in e["msg"]
|
|
62
|
+
for e in errors
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
with pytest.raises(ValidationError) as err:
|
|
66
|
+
CreateAssemblySpecificationRequest(
|
|
67
|
+
name="Valid Name",
|
|
68
|
+
applicability="Valid applicability",
|
|
69
|
+
jsonschema={"invalid": "schema"}, # Missing 'type' field
|
|
70
|
+
)
|
|
71
|
+
errors = err.value.errors()
|
|
72
|
+
# Check that the error is for the 'jsonschema' field
|
|
73
|
+
assert any(
|
|
74
|
+
e["loc"] == ("jsonschema",)
|
|
75
|
+
and e["type"].startswith("value_error")
|
|
76
|
+
and "type" in e["msg"]
|
|
77
|
+
for e in errors
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def test_to_domain_model_conversion(self) -> None:
|
|
81
|
+
"""Test conversion from request model to domain model."""
|
|
82
|
+
request = CreateAssemblySpecificationRequest(
|
|
83
|
+
name="Test Assembly",
|
|
84
|
+
applicability="Test documents",
|
|
85
|
+
jsonschema={
|
|
86
|
+
"type": "object",
|
|
87
|
+
"properties": {"content": {"type": "string"}},
|
|
88
|
+
},
|
|
89
|
+
knowledge_service_queries={"/properties/content": "query-123"},
|
|
90
|
+
version="1.0.0",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
domain_model = request.to_domain_model("spec-456")
|
|
94
|
+
|
|
95
|
+
assert isinstance(domain_model, AssemblySpecification)
|
|
96
|
+
assert domain_model.assembly_specification_id == "spec-456"
|
|
97
|
+
assert domain_model.name == "Test Assembly"
|
|
98
|
+
assert domain_model.applicability == "Test documents"
|
|
99
|
+
assert domain_model.jsonschema == {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {"content": {"type": "string"}},
|
|
102
|
+
}
|
|
103
|
+
assert domain_model.knowledge_service_queries == {
|
|
104
|
+
"/properties/content": "query-123"
|
|
105
|
+
}
|
|
106
|
+
assert domain_model.version == "1.0.0"
|
|
107
|
+
assert domain_model.status == AssemblySpecificationStatus.DRAFT
|
|
108
|
+
assert isinstance(domain_model.created_at, datetime)
|
|
109
|
+
assert isinstance(domain_model.updated_at, datetime)
|
|
110
|
+
|
|
111
|
+
def test_field_definitions_match_domain_model(self) -> None:
|
|
112
|
+
"""Test that field definitions are copied from domain model."""
|
|
113
|
+
request_fields = CreateAssemblySpecificationRequest.model_fields
|
|
114
|
+
domain_fields = AssemblySpecification.model_fields
|
|
115
|
+
|
|
116
|
+
# Verify shared fields have identical definitions
|
|
117
|
+
shared_field_names = [
|
|
118
|
+
"name",
|
|
119
|
+
"applicability",
|
|
120
|
+
"jsonschema",
|
|
121
|
+
"knowledge_service_queries",
|
|
122
|
+
"version",
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
for field_name in shared_field_names:
|
|
126
|
+
assert field_name in request_fields
|
|
127
|
+
assert field_name in domain_fields
|
|
128
|
+
# Field descriptions should match
|
|
129
|
+
assert (
|
|
130
|
+
request_fields[field_name].description
|
|
131
|
+
== domain_fields[field_name].description
|
|
132
|
+
)
|
|
133
|
+
# Default values should match where applicable
|
|
134
|
+
if (
|
|
135
|
+
hasattr(domain_fields[field_name], "default")
|
|
136
|
+
and domain_fields[field_name].default is not None
|
|
137
|
+
):
|
|
138
|
+
assert (
|
|
139
|
+
request_fields[field_name].default
|
|
140
|
+
== domain_fields[field_name].default
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestCreateKnowledgeServiceQueryRequest:
|
|
145
|
+
"""Test CreateKnowledgeServiceQueryRequest model."""
|
|
146
|
+
|
|
147
|
+
def test_valid_request_creation(self) -> None:
|
|
148
|
+
"""Test that a valid request can be created."""
|
|
149
|
+
request = CreateKnowledgeServiceQueryRequest(
|
|
150
|
+
name="Extract Meeting Summary",
|
|
151
|
+
knowledge_service_id="anthropic-claude",
|
|
152
|
+
prompt="Extract the main summary from this meeting transcript",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
assert request.name == "Extract Meeting Summary"
|
|
156
|
+
assert request.knowledge_service_id == "anthropic-claude"
|
|
157
|
+
assert request.prompt == "Extract the main summary from this meeting transcript"
|
|
158
|
+
assert request.query_metadata == {} # Default empty dict
|
|
159
|
+
assert request.assistant_prompt is None # Default None
|
|
160
|
+
|
|
161
|
+
def test_validation_delegation_to_domain_model(self) -> None:
|
|
162
|
+
"""Test that validation is properly delegated to domain model."""
|
|
163
|
+
# Test that domain model validation errors are raised
|
|
164
|
+
with pytest.raises(ValidationError) as err:
|
|
165
|
+
CreateKnowledgeServiceQueryRequest(
|
|
166
|
+
name="", # Invalid empty name
|
|
167
|
+
knowledge_service_id="valid-service",
|
|
168
|
+
prompt="Valid prompt",
|
|
169
|
+
)
|
|
170
|
+
errors = err.value.errors()
|
|
171
|
+
# Check that the error is for the 'name' field
|
|
172
|
+
assert any(
|
|
173
|
+
e["loc"] == ("name",)
|
|
174
|
+
and e["type"].startswith("value_error")
|
|
175
|
+
and "name cannot be empty" in e["msg"]
|
|
176
|
+
for e in errors
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
with pytest.raises(ValidationError) as err:
|
|
180
|
+
CreateKnowledgeServiceQueryRequest(
|
|
181
|
+
name="Valid Name",
|
|
182
|
+
knowledge_service_id="", # Invalid empty service ID
|
|
183
|
+
prompt="Valid prompt",
|
|
184
|
+
)
|
|
185
|
+
errors = err.value.errors()
|
|
186
|
+
# Check that the error is for the 'knowledge_service_id' field
|
|
187
|
+
assert any(
|
|
188
|
+
e["loc"] == ("knowledge_service_id",)
|
|
189
|
+
and e["type"].startswith("value_error")
|
|
190
|
+
and "service ID cannot be empty" in e["msg"]
|
|
191
|
+
for e in errors
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def test_to_domain_model_conversion(self) -> None:
|
|
195
|
+
"""Test conversion from request model to domain model."""
|
|
196
|
+
request = CreateKnowledgeServiceQueryRequest(
|
|
197
|
+
name="Test Query",
|
|
198
|
+
knowledge_service_id="test-service",
|
|
199
|
+
prompt="Test prompt for extraction",
|
|
200
|
+
query_metadata={"model": "claude-3", "temperature": 0.2},
|
|
201
|
+
assistant_prompt="Please format as JSON",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
domain_model = request.to_domain_model("query-456")
|
|
205
|
+
|
|
206
|
+
assert isinstance(domain_model, KnowledgeServiceQuery)
|
|
207
|
+
assert domain_model.query_id == "query-456"
|
|
208
|
+
assert domain_model.name == "Test Query"
|
|
209
|
+
assert domain_model.knowledge_service_id == "test-service"
|
|
210
|
+
assert domain_model.prompt == "Test prompt for extraction"
|
|
211
|
+
assert domain_model.query_metadata == {
|
|
212
|
+
"model": "claude-3",
|
|
213
|
+
"temperature": 0.2,
|
|
214
|
+
}
|
|
215
|
+
assert domain_model.assistant_prompt == "Please format as JSON"
|
|
216
|
+
assert isinstance(domain_model.created_at, datetime)
|
|
217
|
+
assert isinstance(domain_model.updated_at, datetime)
|
|
218
|
+
assert domain_model.created_at == domain_model.updated_at
|
|
219
|
+
|
|
220
|
+
def test_field_definitions_match_domain_model(self) -> None:
|
|
221
|
+
"""Test that field definitions are copied from domain model."""
|
|
222
|
+
request_fields = CreateKnowledgeServiceQueryRequest.model_fields
|
|
223
|
+
domain_fields = KnowledgeServiceQuery.model_fields
|
|
224
|
+
|
|
225
|
+
# Verify shared fields have identical descriptions
|
|
226
|
+
shared_field_names = [
|
|
227
|
+
"name",
|
|
228
|
+
"knowledge_service_id",
|
|
229
|
+
"prompt",
|
|
230
|
+
"query_metadata",
|
|
231
|
+
"assistant_prompt",
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
for field_name in shared_field_names:
|
|
235
|
+
assert field_name in request_fields
|
|
236
|
+
assert field_name in domain_fields
|
|
237
|
+
# Field descriptions should match
|
|
238
|
+
assert (
|
|
239
|
+
request_fields[field_name].description
|
|
240
|
+
== domain_fields[field_name].description
|
|
241
|
+
)
|
|
242
|
+
# Default values should match where applicable
|
|
243
|
+
if (
|
|
244
|
+
hasattr(domain_fields[field_name], "default")
|
|
245
|
+
and domain_fields[field_name].default is not None
|
|
246
|
+
):
|
|
247
|
+
assert (
|
|
248
|
+
request_fields[field_name].default
|
|
249
|
+
== domain_fields[field_name].default
|
|
250
|
+
)
|
julee/domain/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Domain layer for julee.
|
|
3
|
+
|
|
4
|
+
This package contains the core business logic and domain models following
|
|
5
|
+
Clean Architecture principles. All domain concerns are framework-independent
|
|
6
|
+
and have no external dependencies.
|
|
7
|
+
|
|
8
|
+
Subpackages:
|
|
9
|
+
- models: Domain entities and value objects
|
|
10
|
+
- repositories: Repository interface protocols
|
|
11
|
+
- use_cases: Business logic and application services
|
|
12
|
+
|
|
13
|
+
Import domain components using package imports for convenience, e.g.:
|
|
14
|
+
# Models from the models package
|
|
15
|
+
from julee.domain.models import Document, Assembly, Policy
|
|
16
|
+
|
|
17
|
+
# Repository protocols from the repositories package
|
|
18
|
+
from julee.domain.repositories import DocumentRepository
|
|
19
|
+
|
|
20
|
+
# Use cases from the use_cases package
|
|
21
|
+
from julee.domain.use_cases import ValidateDocumentUseCase
|
|
22
|
+
"""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Domain models for julee.
|
|
3
|
+
|
|
4
|
+
This package contains all the domain entities and value objects following
|
|
5
|
+
Clean Architecture principles. These models are framework-independent and
|
|
6
|
+
contain only business logic.
|
|
7
|
+
|
|
8
|
+
Re-exports commonly used models for convenient importing:
|
|
9
|
+
from julee.domain.models import Document, Assembly, Policy
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Document models
|
|
13
|
+
from .document import Document, DocumentStatus
|
|
14
|
+
|
|
15
|
+
# Custom field types
|
|
16
|
+
from .custom_fields.content_stream import ContentStream
|
|
17
|
+
|
|
18
|
+
# Assembly models
|
|
19
|
+
from .assembly import Assembly, AssemblyStatus
|
|
20
|
+
from .assembly_specification import (
|
|
21
|
+
AssemblySpecification,
|
|
22
|
+
AssemblySpecificationStatus,
|
|
23
|
+
KnowledgeServiceQuery,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Configuration models
|
|
27
|
+
from .knowledge_service_config import KnowledgeServiceConfig
|
|
28
|
+
|
|
29
|
+
# Policy models
|
|
30
|
+
from .policy import Policy, PolicyStatus, DocumentPolicyValidation
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Document models
|
|
34
|
+
"Document",
|
|
35
|
+
"DocumentStatus",
|
|
36
|
+
"ContentStream",
|
|
37
|
+
# Assembly models
|
|
38
|
+
"Assembly",
|
|
39
|
+
"AssemblyStatus",
|
|
40
|
+
"AssemblySpecification",
|
|
41
|
+
"AssemblySpecificationStatus",
|
|
42
|
+
"KnowledgeServiceQuery",
|
|
43
|
+
# Configuration models
|
|
44
|
+
"KnowledgeServiceConfig",
|
|
45
|
+
# Policy models
|
|
46
|
+
"Policy",
|
|
47
|
+
"PolicyStatus",
|
|
48
|
+
"DocumentPolicyValidation",
|
|
49
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Assembly domain package for the Capture, Extract, Assemble, Publish workflow.
|
|
3
|
+
|
|
4
|
+
This package contains the Assembly domain object that represents assembly
|
|
5
|
+
processes in the CEAP workflow.
|
|
6
|
+
|
|
7
|
+
Assembly represents a specific instance of assembling a document using an
|
|
8
|
+
AssemblySpecification, linking an input document with the specification and
|
|
9
|
+
producing a single assembled document as output.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .assembly import Assembly, AssemblyStatus
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Assembly",
|
|
16
|
+
"AssemblyStatus",
|
|
17
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Assembly domain models for the Capture, Extract, Assemble, Publish workflow.
|
|
3
|
+
|
|
4
|
+
This module contains the Assembly domain object that represents the actual
|
|
5
|
+
assembly process/instance in the CEAP workflow system.
|
|
6
|
+
|
|
7
|
+
An Assembly represents a specific instance of assembling a document using
|
|
8
|
+
an AssemblySpecification. It links an input document with an assembly
|
|
9
|
+
specification and produces a single assembled document as output.
|
|
10
|
+
|
|
11
|
+
All domain models use Pydantic BaseModel for validation, serialization,
|
|
12
|
+
and type safety, following the patterns established in the sample project.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, Field, field_validator
|
|
16
|
+
from typing import Optional
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from enum import Enum
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AssemblyStatus(str, Enum):
|
|
22
|
+
"""Status of an assembly process."""
|
|
23
|
+
|
|
24
|
+
PENDING = "pending"
|
|
25
|
+
IN_PROGRESS = "in_progress"
|
|
26
|
+
COMPLETED = "completed"
|
|
27
|
+
FAILED = "failed"
|
|
28
|
+
CANCELLED = "cancelled"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Assembly(BaseModel):
|
|
32
|
+
"""Assembly process that links a specification with input document and
|
|
33
|
+
produces an assembled document.
|
|
34
|
+
|
|
35
|
+
An Assembly represents a specific instance of the document assembly
|
|
36
|
+
process. It connects an AssemblySpecification (which defines how to
|
|
37
|
+
assemble) with an input Document (what to assemble from) and produces
|
|
38
|
+
a single assembled document as output.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Core assembly identification
|
|
42
|
+
assembly_id: str = Field(description="Unique identifier for this assembly instance")
|
|
43
|
+
assembly_specification_id: str = Field(
|
|
44
|
+
description="ID of the AssemblySpecification defining how to assemble"
|
|
45
|
+
)
|
|
46
|
+
input_document_id: str = Field(
|
|
47
|
+
description="ID of the input document to assemble from"
|
|
48
|
+
)
|
|
49
|
+
workflow_id: str = Field(
|
|
50
|
+
description="Temporal workflow ID that created this assembly"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Assembly process tracking
|
|
54
|
+
status: AssemblyStatus = AssemblyStatus.PENDING
|
|
55
|
+
assembled_document_id: Optional[str] = Field(
|
|
56
|
+
default=None,
|
|
57
|
+
description="ID of the assembled document produced by this assembly",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Assembly metadata
|
|
61
|
+
created_at: Optional[datetime] = Field(
|
|
62
|
+
default_factory=lambda: datetime.now(timezone.utc)
|
|
63
|
+
)
|
|
64
|
+
updated_at: Optional[datetime] = Field(
|
|
65
|
+
default_factory=lambda: datetime.now(timezone.utc)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@field_validator("assembly_id")
|
|
69
|
+
@classmethod
|
|
70
|
+
def assembly_id_must_not_be_empty(cls, v: str) -> str:
|
|
71
|
+
if not v or not v.strip():
|
|
72
|
+
raise ValueError("Assembly ID cannot be empty")
|
|
73
|
+
return v.strip()
|
|
74
|
+
|
|
75
|
+
@field_validator("assembly_specification_id")
|
|
76
|
+
@classmethod
|
|
77
|
+
def assembly_specification_id_must_not_be_empty(cls, v: str) -> str:
|
|
78
|
+
if not v or not v.strip():
|
|
79
|
+
raise ValueError("Assembly specification ID cannot be empty")
|
|
80
|
+
return v.strip()
|
|
81
|
+
|
|
82
|
+
@field_validator("input_document_id")
|
|
83
|
+
@classmethod
|
|
84
|
+
def input_document_id_must_not_be_empty(cls, v: str) -> str:
|
|
85
|
+
if not v or not v.strip():
|
|
86
|
+
raise ValueError("Input document ID cannot be empty")
|
|
87
|
+
return v.strip()
|
|
88
|
+
|
|
89
|
+
@field_validator("assembled_document_id")
|
|
90
|
+
@classmethod
|
|
91
|
+
def assembled_document_id_must_not_be_empty_if_provided(
|
|
92
|
+
cls, v: Optional[str]
|
|
93
|
+
) -> Optional[str]:
|
|
94
|
+
if v is not None and (not v or not v.strip()):
|
|
95
|
+
raise ValueError("Assembled document ID cannot be empty string")
|
|
96
|
+
return v.strip() if v else None
|
|
97
|
+
|
|
98
|
+
@field_validator("workflow_id")
|
|
99
|
+
@classmethod
|
|
100
|
+
def workflow_id_must_not_be_empty(cls, v: str) -> str:
|
|
101
|
+
if not v or not v.strip():
|
|
102
|
+
raise ValueError("Workflow ID cannot be empty")
|
|
103
|
+
return v.strip()
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test factories for Assembly domain objects using factory_boy.
|
|
3
|
+
|
|
4
|
+
This module provides factory_boy factories for creating test instances of
|
|
5
|
+
Assembly domain objects with sensible defaults.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from factory.base import Factory
|
|
10
|
+
from factory.faker import Faker
|
|
11
|
+
from factory.declarations import LazyFunction
|
|
12
|
+
|
|
13
|
+
from julee.domain.models.assembly import (
|
|
14
|
+
Assembly,
|
|
15
|
+
AssemblyStatus,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AssemblyFactory(Factory):
|
|
20
|
+
"""Factory for creating Assembly instances with sensible test defaults."""
|
|
21
|
+
|
|
22
|
+
class Meta:
|
|
23
|
+
model = Assembly
|
|
24
|
+
|
|
25
|
+
# Core assembly identification
|
|
26
|
+
assembly_id = Faker("uuid4")
|
|
27
|
+
assembly_specification_id = Faker("uuid4")
|
|
28
|
+
input_document_id = Faker("uuid4")
|
|
29
|
+
workflow_id = Faker("uuid4")
|
|
30
|
+
|
|
31
|
+
# Assembly process tracking
|
|
32
|
+
status = AssemblyStatus.PENDING
|
|
33
|
+
assembled_document_id = None
|
|
34
|
+
|
|
35
|
+
# Timestamps
|
|
36
|
+
created_at = LazyFunction(lambda: datetime.now(timezone.utc))
|
|
37
|
+
updated_at = LazyFunction(lambda: datetime.now(timezone.utc))
|