kodit 0.5.4__py3-none-any.whl → 0.5.6__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.

Potentially problematic release.


This version of kodit might be problematic. Click here for more details.

Files changed (64) hide show
  1. kodit/_version.py +2 -2
  2. kodit/app.py +2 -0
  3. kodit/application/factories/server_factory.py +58 -32
  4. kodit/application/services/code_search_application_service.py +89 -12
  5. kodit/application/services/commit_indexing_application_service.py +527 -195
  6. kodit/application/services/enrichment_query_service.py +311 -43
  7. kodit/application/services/indexing_worker_service.py +1 -1
  8. kodit/application/services/queue_service.py +15 -10
  9. kodit/application/services/sync_scheduler.py +2 -1
  10. kodit/domain/enrichments/architecture/architecture.py +1 -1
  11. kodit/domain/enrichments/architecture/database_schema/__init__.py +1 -0
  12. kodit/domain/enrichments/architecture/database_schema/database_schema.py +17 -0
  13. kodit/domain/enrichments/architecture/physical/physical.py +1 -1
  14. kodit/domain/enrichments/development/development.py +1 -1
  15. kodit/domain/enrichments/development/snippet/snippet.py +12 -5
  16. kodit/domain/enrichments/enrichment.py +31 -4
  17. kodit/domain/enrichments/history/__init__.py +1 -0
  18. kodit/domain/enrichments/history/commit_description/__init__.py +1 -0
  19. kodit/domain/enrichments/history/commit_description/commit_description.py +17 -0
  20. kodit/domain/enrichments/history/history.py +18 -0
  21. kodit/domain/enrichments/usage/api_docs.py +1 -1
  22. kodit/domain/enrichments/usage/usage.py +1 -1
  23. kodit/domain/entities/git.py +30 -25
  24. kodit/domain/factories/git_repo_factory.py +20 -5
  25. kodit/domain/protocols.py +60 -125
  26. kodit/domain/services/embedding_service.py +14 -16
  27. kodit/domain/services/git_repository_service.py +60 -38
  28. kodit/domain/services/git_service.py +18 -11
  29. kodit/domain/tracking/resolution_service.py +6 -16
  30. kodit/domain/value_objects.py +6 -9
  31. kodit/infrastructure/api/v1/dependencies.py +12 -3
  32. kodit/infrastructure/api/v1/query_params.py +27 -0
  33. kodit/infrastructure/api/v1/routers/commits.py +91 -85
  34. kodit/infrastructure/api/v1/routers/repositories.py +53 -37
  35. kodit/infrastructure/api/v1/routers/search.py +1 -1
  36. kodit/infrastructure/api/v1/schemas/enrichment.py +14 -0
  37. kodit/infrastructure/api/v1/schemas/repository.py +1 -1
  38. kodit/infrastructure/cloning/git/git_python_adaptor.py +41 -0
  39. kodit/infrastructure/database_schema/__init__.py +1 -0
  40. kodit/infrastructure/database_schema/database_schema_detector.py +268 -0
  41. kodit/infrastructure/slicing/api_doc_extractor.py +0 -2
  42. kodit/infrastructure/sqlalchemy/embedding_repository.py +44 -34
  43. kodit/infrastructure/sqlalchemy/enrichment_association_repository.py +73 -0
  44. kodit/infrastructure/sqlalchemy/enrichment_v2_repository.py +145 -97
  45. kodit/infrastructure/sqlalchemy/entities.py +12 -116
  46. kodit/infrastructure/sqlalchemy/git_branch_repository.py +52 -244
  47. kodit/infrastructure/sqlalchemy/git_commit_repository.py +35 -324
  48. kodit/infrastructure/sqlalchemy/git_file_repository.py +70 -0
  49. kodit/infrastructure/sqlalchemy/git_repository.py +60 -230
  50. kodit/infrastructure/sqlalchemy/git_tag_repository.py +53 -240
  51. kodit/infrastructure/sqlalchemy/query.py +331 -0
  52. kodit/infrastructure/sqlalchemy/repository.py +203 -0
  53. kodit/infrastructure/sqlalchemy/task_repository.py +79 -58
  54. kodit/infrastructure/sqlalchemy/task_status_repository.py +45 -52
  55. kodit/migrations/versions/4b1a3b2c8fa5_refactor_git_tracking.py +190 -0
  56. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/METADATA +1 -1
  57. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/RECORD +60 -50
  58. kodit/infrastructure/mappers/enrichment_mapper.py +0 -83
  59. kodit/infrastructure/mappers/git_mapper.py +0 -193
  60. kodit/infrastructure/mappers/snippet_mapper.py +0 -104
  61. kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +0 -479
  62. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/WHEEL +0 -0
  63. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/entry_points.txt +0 -0
  64. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/licenses/LICENSE +0 -0
@@ -2,47 +2,74 @@
2
2
 
3
3
  import structlog
4
4
 
5
- from kodit.domain.enrichments.enrichment import EnrichmentV2
5
+ from kodit.domain.enrichments.architecture.architecture import (
6
+ ENRICHMENT_TYPE_ARCHITECTURE,
7
+ )
8
+ from kodit.domain.enrichments.architecture.database_schema.database_schema import (
9
+ ENRICHMENT_SUBTYPE_DATABASE_SCHEMA,
10
+ )
11
+ from kodit.domain.enrichments.architecture.physical.physical import (
12
+ ENRICHMENT_SUBTYPE_PHYSICAL,
13
+ )
14
+ from kodit.domain.enrichments.development.development import ENRICHMENT_TYPE_DEVELOPMENT
15
+ from kodit.domain.enrichments.development.snippet.snippet import (
16
+ ENRICHMENT_SUBTYPE_SNIPPET,
17
+ ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY,
18
+ )
19
+ from kodit.domain.enrichments.enrichment import EnrichmentAssociation, EnrichmentV2
20
+ from kodit.domain.enrichments.history.commit_description.commit_description import (
21
+ ENRICHMENT_SUBTYPE_COMMIT_DESCRIPTION,
22
+ )
23
+ from kodit.domain.enrichments.history.history import ENRICHMENT_TYPE_HISTORY
24
+ from kodit.domain.enrichments.usage.api_docs import ENRICHMENT_SUBTYPE_API_DOCS
25
+ from kodit.domain.enrichments.usage.usage import ENRICHMENT_TYPE_USAGE
26
+ from kodit.domain.protocols import (
27
+ EnrichmentAssociationRepository,
28
+ EnrichmentV2Repository,
29
+ )
6
30
  from kodit.domain.tracking.resolution_service import TrackableResolutionService
7
31
  from kodit.domain.tracking.trackable import Trackable
8
- from kodit.infrastructure.sqlalchemy.enrichment_v2_repository import (
9
- EnrichmentV2Repository,
32
+ from kodit.infrastructure.api.v1.query_params import PaginationParams
33
+ from kodit.infrastructure.sqlalchemy import entities as db_entities
34
+ from kodit.infrastructure.sqlalchemy.query import (
35
+ EnrichmentAssociationQueryBuilder,
36
+ EnrichmentQueryBuilder,
10
37
  )
11
38
 
12
39
 
13
40
  class EnrichmentQueryService:
14
- """Finds the latest commit with enrichments for a trackable.
15
-
16
- Orchestrates domain services and repositories to fulfill the use case.
17
- """
41
+ """Service for querying enrichments."""
18
42
 
19
43
  def __init__(
20
44
  self,
21
45
  trackable_resolution: TrackableResolutionService,
22
46
  enrichment_repo: EnrichmentV2Repository,
47
+ enrichment_association_repository: EnrichmentAssociationRepository,
23
48
  ) -> None:
24
49
  """Initialize the enrichment query service."""
25
50
  self.trackable_resolution = trackable_resolution
26
51
  self.enrichment_repo = enrichment_repo
52
+ self.enrichment_association_repository = enrichment_association_repository
27
53
  self.log = structlog.get_logger(__name__)
28
54
 
55
+ async def associations_for_commit(
56
+ self, commit_sha: str
57
+ ) -> list[EnrichmentAssociation]:
58
+ """Get enrichments for a commit."""
59
+ return await self.enrichment_association_repository.find(
60
+ EnrichmentAssociationQueryBuilder.for_enrichment_associations(
61
+ entity_type=db_entities.GitCommit.__tablename__,
62
+ entity_ids=[commit_sha],
63
+ )
64
+ )
65
+
29
66
  async def find_latest_enriched_commit(
30
67
  self,
31
68
  trackable: Trackable,
32
69
  enrichment_type: str | None = None,
33
70
  max_commits_to_check: int = 100,
34
71
  ) -> str | None:
35
- """Find the most recent commit with enrichments.
36
-
37
- Args:
38
- trackable: What to track (branch, tag, or commit)
39
- enrichment_type: Optional filter for specific enrichment type
40
- max_commits_to_check: How far back in history to search
41
-
42
- Returns:
43
- Commit SHA of the most recent commit with enrichments, or None
44
-
45
- """
72
+ """Find the most recent commit with enrichments."""
46
73
  # Get candidate commits from the trackable
47
74
  candidate_commits = await self.trackable_resolution.resolve_to_commits(
48
75
  trackable, max_commits_to_check
@@ -52,44 +79,285 @@ class EnrichmentQueryService:
52
79
  return None
53
80
 
54
81
  # Check which commits have enrichments
55
- enrichments = await self.enrichment_repo.enrichments_for_entity_type(
56
- entity_type="git_commit",
57
- entity_ids=candidate_commits,
58
- )
59
-
60
- # Filter by type if specified
61
- if enrichment_type:
62
- enrichments = [e for e in enrichments if e.type == enrichment_type]
63
-
64
- # Find the first commit (newest) that has enrichments
65
82
  for commit_sha in candidate_commits:
66
- if any(e.entity_id == commit_sha for e in enrichments):
83
+ enrichments = await self.all_enrichments_for_commit(
84
+ commit_sha=commit_sha,
85
+ pagination=PaginationParams(page_size=1),
86
+ enrichment_type=enrichment_type,
87
+ )
88
+ if enrichments:
67
89
  return commit_sha
68
90
 
69
91
  return None
70
92
 
93
+ async def all_enrichments_for_commit(
94
+ self,
95
+ commit_sha: str,
96
+ pagination: PaginationParams,
97
+ enrichment_type: str | None = None,
98
+ enrichment_subtype: str | None = None,
99
+ ) -> dict[EnrichmentV2, list[EnrichmentAssociation]]:
100
+ """Get all enrichments for a specific commit."""
101
+ associations = await self.enrichment_association_repository.find(
102
+ EnrichmentAssociationQueryBuilder().for_commit(commit_sha)
103
+ )
104
+ enrichment_ids = [association.enrichment_id for association in associations]
105
+ query = EnrichmentQueryBuilder().for_ids(enrichment_ids).paginate(pagination)
106
+ if enrichment_type:
107
+ query = query.for_type(enrichment_type)
108
+ if enrichment_subtype:
109
+ query = query.for_subtype(enrichment_subtype)
110
+ enrichments = await self.enrichment_repo.find(query)
111
+ # Find all other associations for these enrichments
112
+ other_associations = await self.enrichment_association_repository.find(
113
+ EnrichmentAssociationQueryBuilder().for_enrichments(enrichments)
114
+ )
115
+ all_associations = set(associations + other_associations)
116
+ return {
117
+ enrichment: [
118
+ association
119
+ for association in all_associations
120
+ if association.enrichment_id == enrichment.id
121
+ or association.entity_id == str(enrichment.id)
122
+ ]
123
+ for enrichment in enrichments
124
+ }
125
+
126
+ async def get_all_snippets_for_commit(self, commit_sha: str) -> list[EnrichmentV2]:
127
+ """Get snippet enrichments for a commit."""
128
+ return list(
129
+ await self.all_enrichments_for_commit(
130
+ commit_sha=commit_sha,
131
+ pagination=PaginationParams(page_size=32000),
132
+ enrichment_type=ENRICHMENT_TYPE_DEVELOPMENT,
133
+ enrichment_subtype=ENRICHMENT_SUBTYPE_SNIPPET,
134
+ )
135
+ )
136
+
137
+ async def get_summaries_for_commit(self, commit_sha: str) -> list[EnrichmentV2]:
138
+ """Get summary enrichments for a commit."""
139
+ return await self.get_enrichments_for_commit(
140
+ commit_sha,
141
+ enrichment_type=ENRICHMENT_TYPE_DEVELOPMENT,
142
+ enrichment_subtype=ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY,
143
+ )
144
+
71
145
  async def get_enrichments_for_commit(
72
146
  self,
73
147
  commit_sha: str,
74
148
  enrichment_type: str | None = None,
149
+ enrichment_subtype: str | None = None,
75
150
  ) -> list[EnrichmentV2]:
76
- """Get all enrichments for a specific commit.
151
+ """Get enrichments for a commit."""
152
+ # Find associations pointing to this commit
153
+ all_associations = await self.enrichment_association_repository.find(
154
+ EnrichmentAssociationQueryBuilder().for_commit(commit_sha)
155
+ )
156
+ query = EnrichmentQueryBuilder().for_ids(
157
+ enrichment_ids=[
158
+ int(association.enrichment_id) for association in all_associations
159
+ ]
160
+ )
161
+ if enrichment_type:
162
+ query = query.for_type(enrichment_type)
163
+ if enrichment_subtype:
164
+ query = query.for_subtype(enrichment_subtype)
165
+ return await self.enrichment_repo.find(query)
77
166
 
78
- Args:
79
- commit_sha: The commit SHA to get enrichments for
80
- enrichment_type: Optional filter for specific enrichment type
167
+ async def get_architecture_docs_for_commit(
168
+ self, commit_sha: str
169
+ ) -> list[EnrichmentV2]:
170
+ """Get architecture documentation enrichments for a commit."""
171
+ return await self.get_enrichments_for_commit(
172
+ commit_sha,
173
+ enrichment_type=ENRICHMENT_TYPE_ARCHITECTURE,
174
+ enrichment_subtype=ENRICHMENT_SUBTYPE_PHYSICAL,
175
+ )
81
176
 
82
- Returns:
83
- List of enrichments for the commit
177
+ async def get_api_docs_for_commit(self, commit_sha: str) -> list[EnrichmentV2]:
178
+ """Get API documentation enrichments for a commit."""
179
+ return await self.get_enrichments_for_commit(
180
+ commit_sha,
181
+ enrichment_type=ENRICHMENT_TYPE_USAGE,
182
+ enrichment_subtype=ENRICHMENT_SUBTYPE_API_DOCS,
183
+ )
84
184
 
85
- """
86
- enrichments = await self.enrichment_repo.enrichments_for_entity_type(
87
- entity_type="git_commit",
88
- entity_ids=[commit_sha],
185
+ async def get_enrichment_entities_from_associations(
186
+ self, associations: list[EnrichmentAssociation]
187
+ ) -> list[EnrichmentV2]:
188
+ """Get enrichments by their associations."""
189
+ return await self.enrichment_repo.find(
190
+ EnrichmentQueryBuilder().for_ids(
191
+ enrichment_ids=[
192
+ int(association.entity_id) for association in associations
193
+ ]
194
+ )
89
195
  )
90
196
 
91
- # Filter by type if specified
92
- if enrichment_type:
93
- enrichments = [e for e in enrichments if e.type == enrichment_type]
197
+ async def get_enrichments_by_ids(
198
+ self, enrichment_ids: list[int]
199
+ ) -> list[EnrichmentV2]:
200
+ """Get enrichments by their IDs."""
201
+ return await self.enrichment_repo.find(
202
+ EnrichmentQueryBuilder().for_ids(enrichment_ids=enrichment_ids)
203
+ )
204
+
205
+ async def has_snippets_for_commit(self, commit_sha: str) -> bool:
206
+ """Check if a commit has snippet enrichments."""
207
+ snippets = await self.get_all_snippets_for_commit(commit_sha)
208
+ return len(snippets) > 0
209
+
210
+ async def has_summaries_for_commit(self, commit_sha: str) -> bool:
211
+ """Check if a commit has summary enrichments."""
212
+ summaries = await self.get_summaries_for_commit(commit_sha)
213
+ return len(summaries) > 0
214
+
215
+ async def has_architecture_for_commit(self, commit_sha: str) -> bool:
216
+ """Check if a commit has architecture enrichments."""
217
+ architecture_docs = await self.get_architecture_docs_for_commit(commit_sha)
218
+ return len(architecture_docs) > 0
219
+
220
+ async def has_api_docs_for_commit(self, commit_sha: str) -> bool:
221
+ """Check if a commit has API documentation enrichments."""
222
+ api_docs = await self.get_api_docs_for_commit(commit_sha)
223
+ return len(api_docs) > 0
224
+
225
+ async def get_commit_description_for_commit(
226
+ self, commit_sha: str
227
+ ) -> list[EnrichmentV2]:
228
+ """Get commit description enrichments for a commit."""
229
+ return await self.get_enrichments_for_commit(
230
+ commit_sha,
231
+ enrichment_type=ENRICHMENT_TYPE_HISTORY,
232
+ enrichment_subtype=ENRICHMENT_SUBTYPE_COMMIT_DESCRIPTION,
233
+ )
234
+
235
+ async def has_commit_description_for_commit(self, commit_sha: str) -> bool:
236
+ """Check if a commit has commit description enrichments."""
237
+ commit_descriptions = await self.get_commit_description_for_commit(commit_sha)
238
+ return len(commit_descriptions) > 0
239
+
240
+ async def get_database_schema_for_commit(
241
+ self, commit_sha: str
242
+ ) -> list[EnrichmentV2]:
243
+ """Get database schema enrichments for a commit."""
244
+ return await self.get_enrichments_for_commit(
245
+ commit_sha,
246
+ enrichment_type=ENRICHMENT_TYPE_ARCHITECTURE,
247
+ enrichment_subtype=ENRICHMENT_SUBTYPE_DATABASE_SCHEMA,
248
+ )
249
+
250
+ async def has_database_schema_for_commit(self, commit_sha: str) -> bool:
251
+ """Check if a commit has database schema enrichments."""
252
+ database_schemas = await self.get_database_schema_for_commit(commit_sha)
253
+ return len(database_schemas) > 0
254
+
255
+ async def associations_for_enrichments(
256
+ self, enrichments: list[EnrichmentV2]
257
+ ) -> list[EnrichmentAssociation]:
258
+ """Get enrichment associations for given enrichment IDs."""
259
+ return await self.enrichment_association_repository.find(
260
+ EnrichmentAssociationQueryBuilder()
261
+ .for_enrichments(enrichments)
262
+ .for_enrichment_type()
263
+ )
264
+
265
+ async def snippet_associations_from_enrichments(
266
+ self, enrichments: list[EnrichmentV2]
267
+ ) -> list[EnrichmentAssociation]:
268
+ """Get snippet enrichment associations for given enrichments."""
269
+ return await self.enrichment_association_repository.find(
270
+ EnrichmentAssociationQueryBuilder()
271
+ .for_enrichments(enrichments)
272
+ .for_enrichment_type()
273
+ )
274
+
275
+ async def snippets_for_summary_enrichments(
276
+ self, summary_enrichments: list[EnrichmentV2]
277
+ ) -> list[EnrichmentV2]:
278
+ """Get snippet enrichment IDs for summary enrichments, preserving order."""
279
+ if not summary_enrichments:
280
+ return []
281
+
282
+ # Get associations where enrichment_id points to these summaries
283
+ associations = await self.enrichment_association_repository.find(
284
+ EnrichmentAssociationQueryBuilder()
285
+ .for_enrichments(summary_enrichments)
286
+ .for_enrichment_type()
287
+ )
288
+
289
+ all_enrichments = await self.enrichment_repo.find(
290
+ EnrichmentQueryBuilder().for_ids(
291
+ enrichment_ids=[
292
+ int(association.entity_id) for association in associations
293
+ ]
294
+ )
295
+ )
296
+ snippet_enrichments = [
297
+ e for e in all_enrichments if e.subtype == ENRICHMENT_SUBTYPE_SNIPPET
298
+ ]
299
+
300
+ # Re-Sort snippet enrichments to be in the same order as the associations
301
+ original_snippet_ids = [association.entity_id for association in associations]
302
+ return sorted(
303
+ snippet_enrichments,
304
+ key=lambda x: original_snippet_ids.index(str(x.id)),
305
+ )
306
+
307
+ async def get_enrichments_pointing_to_enrichments(
308
+ self, target_enrichment_ids: list[int]
309
+ ) -> dict[int, list[EnrichmentV2]]:
310
+ """Get enrichments that point to the given enrichments, grouped by target."""
311
+ # Get associations pointing to these enrichments
312
+ associations = await self.enrichment_association_repository.find(
313
+ EnrichmentAssociationQueryBuilder()
314
+ .for_enrichment_type()
315
+ .for_entity_ids(
316
+ [str(enrichment_id) for enrichment_id in target_enrichment_ids]
317
+ )
318
+ )
319
+
320
+ if not associations:
321
+ return {eid: [] for eid in target_enrichment_ids}
322
+
323
+ # Get the enrichments referenced by these associations
324
+ enrichment_ids = [a.enrichment_id for a in associations]
325
+ enrichments = await self.enrichment_repo.find(
326
+ EnrichmentQueryBuilder().for_ids(enrichment_ids=enrichment_ids)
327
+ )
328
+
329
+ # Create lookup map
330
+ enrichment_map = {e.id: e for e in enrichments if e.id is not None}
331
+
332
+ # Group by target enrichment ID
333
+ result: dict[int, list[EnrichmentV2]] = {
334
+ eid: [] for eid in target_enrichment_ids
335
+ }
336
+ for association in associations:
337
+ target_id = int(association.entity_id)
338
+ if target_id in result and association.enrichment_id in enrichment_map:
339
+ result[target_id].append(enrichment_map[association.enrichment_id])
340
+
341
+ return result
342
+
343
+ async def summary_to_snippet_map(self, summary_ids: list[int]) -> dict[int, int]:
344
+ """Get a map of summary IDs to snippet IDs."""
345
+ # Get the snippet enrichment IDs that these summaries point to
346
+ summary_enrichments = await self.get_enrichments_by_ids(summary_ids)
347
+
348
+ # Get all the associations for these summary enrichments
349
+ all_associations = await self.associations_for_enrichments(summary_enrichments)
350
+
351
+ # Get all enrichments for these summary associations
352
+ all_snippet_enrichments = await self.get_enrichment_entities_from_associations(
353
+ all_associations
354
+ )
355
+ snippet_type_map = {e.id: e.subtype for e in all_snippet_enrichments}
94
356
 
95
- return enrichments
357
+ # Create a lookup map from summary ID to snippet ID, via the associations,
358
+ # filtering out any snippets that are not summary enrichments
359
+ return {
360
+ assoc.enrichment_id: int(assoc.entity_id)
361
+ for assoc in all_associations
362
+ if snippet_type_map[int(assoc.entity_id)] == ENRICHMENT_SUBTYPE_SNIPPET
363
+ }
@@ -75,7 +75,7 @@ class IndexingWorkerService:
75
75
  if task:
76
76
  await self._process_task(task)
77
77
  # Only remove the task if it was processed successfully
78
- await self.task_repository.remove(task)
78
+ await self.task_repository.delete(task)
79
79
  continue
80
80
 
81
81
  # If no task, sleep for a bit
@@ -8,6 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
8
8
 
9
9
  from kodit.domain.entities import Task
10
10
  from kodit.domain.value_objects import QueuePriority, TaskOperation
11
+ from kodit.infrastructure.sqlalchemy.query import FilterOperator, QueryBuilder
11
12
  from kodit.infrastructure.sqlalchemy.task_repository import (
12
13
  create_task_repository,
13
14
  )
@@ -30,16 +31,12 @@ class QueueService:
30
31
 
31
32
  async def enqueue_task(self, task: Task) -> None:
32
33
  """Queue a task in the database."""
33
- # See if task already exists
34
- db_task = await self.task_repository.get(task.id)
35
- if db_task:
36
- # Task already exists, update priority
37
- db_task.priority = task.priority
38
- await self.task_repository.update(db_task)
34
+ # Check if task already exists
35
+ exists = await self.task_repository.exists(task.id)
36
+ await self.task_repository.save(task)
37
+ if exists:
39
38
  self.log.info("Task updated", task_id=task.id, task_type=task.type)
40
39
  else:
41
- # Otherwise, add task
42
- await self.task_repository.add(task)
43
40
  self.log.info(
44
41
  "Task queued",
45
42
  task_id=task.id,
@@ -69,8 +66,16 @@ class QueueService:
69
66
  self, task_operation: TaskOperation | None = None
70
67
  ) -> list[Task]:
71
68
  """List all tasks in the queue."""
72
- return await self.task_repository.list(task_operation)
69
+ query = QueryBuilder()
70
+ if task_operation:
71
+ query.filter("type", FilterOperator.EQ, task_operation.value)
72
+ query.sort("priority", descending=True)
73
+ query.sort("created_at", descending=True)
74
+ return await self.task_repository.find(query)
73
75
 
74
76
  async def get_task(self, task_id: str) -> Task | None:
75
77
  """Get a specific task by ID."""
76
- return await self.task_repository.get(task_id)
78
+ try:
79
+ return await self.task_repository.get(task_id)
80
+ except ValueError:
81
+ return None
@@ -11,6 +11,7 @@ from kodit.domain.value_objects import (
11
11
  PrescribedOperations,
12
12
  QueuePriority,
13
13
  )
14
+ from kodit.infrastructure.sqlalchemy.query import QueryBuilder
14
15
 
15
16
 
16
17
  class SyncSchedulerService:
@@ -68,7 +69,7 @@ class SyncSchedulerService:
68
69
  self.log.info("Starting sync operation")
69
70
 
70
71
  # Sync each index - queue all 5 tasks with priority ordering
71
- for repo in await self.repo_repository.get_all():
72
+ for repo in await self.repo_repository.find(QueryBuilder()):
72
73
  await self.queue_service.enqueue_tasks(
73
74
  tasks=PrescribedOperations.SYNC_REPOSITORY,
74
75
  base_priority=QueuePriority.BACKGROUND,
@@ -10,7 +10,7 @@ from kodit.domain.enrichments.enrichment import (
10
10
  ENRICHMENT_TYPE_ARCHITECTURE = "architecture"
11
11
 
12
12
 
13
- @dataclass
13
+ @dataclass(frozen=True)
14
14
  class ArchitectureEnrichment(CommitEnrichment, ABC):
15
15
  """Enrichment containing physical architecture discovery for a commit."""
16
16
 
@@ -0,0 +1 @@
1
+ """Database schema enrichments."""
@@ -0,0 +1,17 @@
1
+ """Database schema enrichment domain entity."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kodit.domain.enrichments.architecture.architecture import ArchitectureEnrichment
6
+
7
+ ENRICHMENT_SUBTYPE_DATABASE_SCHEMA = "database_schema"
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class DatabaseSchemaEnrichment(ArchitectureEnrichment):
12
+ """Enrichment containing database schema information for a commit."""
13
+
14
+ @property
15
+ def subtype(self) -> str | None:
16
+ """Return the enrichment subtype."""
17
+ return ENRICHMENT_SUBTYPE_DATABASE_SCHEMA
@@ -7,7 +7,7 @@ from kodit.domain.enrichments.architecture.architecture import ArchitectureEnric
7
7
  ENRICHMENT_SUBTYPE_PHYSICAL = "physical"
8
8
 
9
9
 
10
- @dataclass
10
+ @dataclass(frozen=True)
11
11
  class PhysicalArchitectureEnrichment(ArchitectureEnrichment):
12
12
  """Enrichment containing physical architecture discovery for a commit."""
13
13
 
@@ -8,7 +8,7 @@ from kodit.domain.enrichments.enrichment import CommitEnrichment
8
8
  ENRICHMENT_TYPE_DEVELOPMENT = "development"
9
9
 
10
10
 
11
- @dataclass
11
+ @dataclass(frozen=True)
12
12
  class DevelopmentEnrichment(CommitEnrichment, ABC):
13
13
  """Enrichment containing development discovery for a commit."""
14
14
 
@@ -5,10 +5,11 @@ from dataclasses import dataclass
5
5
  from kodit.domain.enrichments.development.development import DevelopmentEnrichment
6
6
 
7
7
  ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY = "snippet_summary"
8
+ ENRICHMENT_SUBTYPE_SNIPPET = "snippet"
8
9
 
9
10
 
10
- @dataclass
11
- class SnippetEnrichment(DevelopmentEnrichment):
11
+ @dataclass(frozen=True)
12
+ class SnippetEnrichmentSummary(DevelopmentEnrichment):
12
13
  """Enrichment specific to code snippets."""
13
14
 
14
15
  @property
@@ -16,6 +17,12 @@ class SnippetEnrichment(DevelopmentEnrichment):
16
17
  """Return the enrichment subtype."""
17
18
  return ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY
18
19
 
19
- def entity_type_key(self) -> str:
20
- """Return the entity type key this enrichment is for."""
21
- return "snippet_v2"
20
+
21
+ @dataclass(frozen=True)
22
+ class SnippetEnrichment(DevelopmentEnrichment):
23
+ """Enrichment specific to code snippets."""
24
+
25
+ @property
26
+ def subtype(self) -> str | None:
27
+ """Return the enrichment subtype."""
28
+ return ENRICHMENT_SUBTYPE_SNIPPET
@@ -4,12 +4,14 @@ from abc import ABC, abstractmethod
4
4
  from dataclasses import dataclass
5
5
  from datetime import datetime
6
6
 
7
+ # TODO(Phil): db stuff should not exist in the domain layer, create a mapper for this.
8
+ from kodit.infrastructure.sqlalchemy import entities as db_entities
7
9
 
8
- @dataclass
10
+
11
+ @dataclass(frozen=True)
9
12
  class EnrichmentV2(ABC):
10
13
  """Generic enrichment that can be attached to any entity."""
11
14
 
12
- entity_id: str
13
15
  content: str = ""
14
16
  id: int | None = None
15
17
  created_at: datetime | None = None
@@ -30,10 +32,35 @@ class EnrichmentV2(ABC):
30
32
  """Return the entity type key this enrichment is for."""
31
33
 
32
34
 
33
- @dataclass
35
+ @dataclass(frozen=True)
34
36
  class CommitEnrichment(EnrichmentV2, ABC):
35
37
  """Enrichment specific to commits."""
36
38
 
37
39
  def entity_type_key(self) -> str:
38
40
  """Return the entity type key this enrichment is for."""
39
- return "git_commit"
41
+ return db_entities.GitCommit.__tablename__
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class EnrichmentAssociation:
46
+ """Association between an enrichment and an entity."""
47
+
48
+ enrichment_id: int
49
+ entity_id: str
50
+ entity_type: str
51
+ id: int | None = None
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class CommitEnrichmentAssociation(EnrichmentAssociation):
56
+ """Association between a commit and an enrichment."""
57
+
58
+ entity_type: str = db_entities.GitCommit.__tablename__
59
+
60
+
61
+ @dataclass
62
+ class SnippetSummaryAssociation:
63
+ """Association between a snippet and a summary enrichment."""
64
+
65
+ snippet_summary: EnrichmentAssociation
66
+ snippet: EnrichmentAssociation
@@ -0,0 +1 @@
1
+ """History enrichments."""
@@ -0,0 +1 @@
1
+ """Commit description enrichments."""
@@ -0,0 +1,17 @@
1
+ """Commit description enrichment domain entity."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kodit.domain.enrichments.history.history import HistoryEnrichment
6
+
7
+ ENRICHMENT_SUBTYPE_COMMIT_DESCRIPTION = "commit_description"
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class CommitDescriptionEnrichment(HistoryEnrichment):
12
+ """Enrichment containing a description of what a commit did."""
13
+
14
+ @property
15
+ def subtype(self) -> str | None:
16
+ """Return the enrichment subtype."""
17
+ return ENRICHMENT_SUBTYPE_COMMIT_DESCRIPTION
@@ -0,0 +1,18 @@
1
+ """History enrichment domain entity."""
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+
6
+ from kodit.domain.enrichments.enrichment import CommitEnrichment
7
+
8
+ ENRICHMENT_TYPE_HISTORY = "history"
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class HistoryEnrichment(CommitEnrichment, ABC):
13
+ """Enrichment containing historical information for a commit."""
14
+
15
+ @property
16
+ def type(self) -> str:
17
+ """Return the enrichment type."""
18
+ return ENRICHMENT_TYPE_HISTORY
@@ -7,7 +7,7 @@ from kodit.domain.enrichments.usage.usage import UsageEnrichment
7
7
  ENRICHMENT_SUBTYPE_API_DOCS = "api_docs"
8
8
 
9
9
 
10
- @dataclass
10
+ @dataclass(frozen=True)
11
11
  class APIDocEnrichment(UsageEnrichment):
12
12
  """API documentation enrichment for a module."""
13
13