quilchoom 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.
- quilchoom/__init__.py +3 -0
- quilchoom/application/__init__.py +1 -0
- quilchoom/application/apply_correction.py +75 -0
- quilchoom/application/capture_commit.py +68 -0
- quilchoom/application/capture_repository.py +53 -0
- quilchoom/application/check_document_staleness.py +26 -0
- quilchoom/application/check_knowledge_status.py +24 -0
- quilchoom/application/create_document_version.py +45 -0
- quilchoom/application/errors.py +47 -0
- quilchoom/application/generate_document.py +84 -0
- quilchoom/application/get_document_version.py +48 -0
- quilchoom/application/get_project_status.py +110 -0
- quilchoom/application/initialize_project.py +53 -0
- quilchoom/application/interpret_history.py +87 -0
- quilchoom/application/reconstruct_history.py +47 -0
- quilchoom/application/runtime.py +59 -0
- quilchoom/cli.py +1042 -0
- quilchoom/domain/correction.py +17 -0
- quilchoom/domain/document.py +33 -0
- quilchoom/domain/event.py +19 -0
- quilchoom/domain/evidence.py +19 -0
- quilchoom/domain/history.py +20 -0
- quilchoom/domain/interpretation.py +37 -0
- quilchoom/domain/knowledge_claim.py +35 -0
- quilchoom/domain/project.py +17 -0
- quilchoom/infrastructure/ai/__init__.py +0 -0
- quilchoom/infrastructure/ai/composition.py +52 -0
- quilchoom/infrastructure/ai/config.py +15 -0
- quilchoom/infrastructure/ai/credentials.py +20 -0
- quilchoom/infrastructure/ai/errors.py +35 -0
- quilchoom/infrastructure/ai/llm_document_generator.py +89 -0
- quilchoom/infrastructure/ai/llm_knowledge_interpreter.py +131 -0
- quilchoom/infrastructure/ai/models.py +119 -0
- quilchoom/infrastructure/ai/openai_provider.py +99 -0
- quilchoom/infrastructure/ai/retry.py +48 -0
- quilchoom/infrastructure/config.py +73 -0
- quilchoom/infrastructure/database/__init__.py +1 -0
- quilchoom/infrastructure/database/alembic/README +1 -0
- quilchoom/infrastructure/database/alembic/env.py +91 -0
- quilchoom/infrastructure/database/alembic/script.py.mako +28 -0
- quilchoom/infrastructure/database/alembic/versions/004b83fcafcb_create_evidence_table.py +54 -0
- quilchoom/infrastructure/database/alembic/versions/03b2175f033b_add_capture_uniqueness_constraints.py +50 -0
- quilchoom/infrastructure/database/alembic/versions/11da2eb1dbf1_create_events_table.py +54 -0
- quilchoom/infrastructure/database/alembic/versions/1d97255f2308_add_corrections.py +53 -0
- quilchoom/infrastructure/database/alembic/versions/3382e88e4db2_allow_claims_across_document_versions.py +27 -0
- quilchoom/infrastructure/database/alembic/versions/8b14085b8c73_add_knowledge_claim_basis.py +53 -0
- quilchoom/infrastructure/database/alembic/versions/9030269dd9ac_add_interpretation_runs.py +67 -0
- quilchoom/infrastructure/database/alembic/versions/a393cc967881_add_knowledge_claims.py +63 -0
- quilchoom/infrastructure/database/alembic/versions/b746cacea516_add_documents_and_document_versions.py +82 -0
- quilchoom/infrastructure/database/alembic/versions/d990a8acc606_create_projects_table.py +41 -0
- quilchoom/infrastructure/database/connection.py +17 -0
- quilchoom/infrastructure/database/errors.py +47 -0
- quilchoom/infrastructure/database/migrations.py +70 -0
- quilchoom/infrastructure/database/models.py +360 -0
- quilchoom/infrastructure/database/repositories.py +866 -0
- quilchoom/infrastructure/filesystem/__init__.py +1 -0
- quilchoom/infrastructure/filesystem/export.py +28 -0
- quilchoom/infrastructure/filesystem/workspace.py +26 -0
- quilchoom/infrastructure/git/__init__.py +1 -0
- quilchoom/infrastructure/git/repository.py +105 -0
- quilchoom/interfaces/document_generator.py +36 -0
- quilchoom/interfaces/knowledge_interpreter.py +11 -0
- quilchoom/interfaces/llm_provider.py +40 -0
- quilchoom/ui/__init__.py +3 -0
- quilchoom/ui/terminal.py +200 -0
- quilchoom-0.1.0.dist-info/METADATA +395 -0
- quilchoom-0.1.0.dist-info/RECORD +70 -0
- quilchoom-0.1.0.dist-info/WHEEL +4 -0
- quilchoom-0.1.0.dist-info/entry_points.txt +4 -0
- quilchoom-0.1.0.dist-info/licenses/LICENSE +201 -0
quilchoom/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Application use cases for Quilchoom."""
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Applies corrections to existing knowledge claims.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from quilchoom.application.errors import (
|
|
8
|
+
CorrectionProjectMismatchError,
|
|
9
|
+
SelfReplacementError,
|
|
10
|
+
)
|
|
11
|
+
from quilchoom.domain.correction import Correction
|
|
12
|
+
from quilchoom.domain.knowledge_claim import ClaimStatus
|
|
13
|
+
from quilchoom.infrastructure.database.errors import KnowledgeClaimNotFoundError
|
|
14
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
15
|
+
CorrectionRepository,
|
|
16
|
+
KnowledgeClaimRepository,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def apply_correction(
|
|
21
|
+
project_id: UUID,
|
|
22
|
+
target_claim_id: UUID,
|
|
23
|
+
reason: str,
|
|
24
|
+
claim_repository: KnowledgeClaimRepository,
|
|
25
|
+
correction_repository: CorrectionRepository,
|
|
26
|
+
replacement_claim_id: UUID | None = None,
|
|
27
|
+
) -> Correction:
|
|
28
|
+
target_claim = claim_repository.get_by_id(target_claim_id)
|
|
29
|
+
|
|
30
|
+
if target_claim is None:
|
|
31
|
+
raise KnowledgeClaimNotFoundError(
|
|
32
|
+
f"Target knowledge claim not found: {target_claim_id}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if target_claim.project_id != project_id:
|
|
36
|
+
raise CorrectionProjectMismatchError(
|
|
37
|
+
f"Target knowledge claim belongs to a different project: {target_claim.project_id}"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if replacement_claim_id is not None:
|
|
41
|
+
if replacement_claim_id == target_claim_id:
|
|
42
|
+
raise SelfReplacementError(
|
|
43
|
+
f"Claim cannot be used as its own replacement: {replacement_claim_id}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
replacement_claim = claim_repository.get_by_id(replacement_claim_id)
|
|
47
|
+
|
|
48
|
+
if replacement_claim is None:
|
|
49
|
+
raise KnowledgeClaimNotFoundError(
|
|
50
|
+
f"Replacement knowledge claim not found: {replacement_claim_id}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if replacement_claim.project_id != project_id:
|
|
54
|
+
raise CorrectionProjectMismatchError(
|
|
55
|
+
f"Replacement knowledge claim belongs to a different project: {replacement_claim.project_id}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if replacement_claim_id is not None:
|
|
59
|
+
new_status = ClaimStatus.CORRECTED
|
|
60
|
+
else:
|
|
61
|
+
new_status = ClaimStatus.INVALIDATED
|
|
62
|
+
|
|
63
|
+
correction = Correction(
|
|
64
|
+
project_id=project_id,
|
|
65
|
+
target_claim_id=target_claim.id,
|
|
66
|
+
reason=reason,
|
|
67
|
+
replacement_claim_id=replacement_claim_id,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
correction_repository.save_with_claim_status_update(
|
|
71
|
+
correction=correction,
|
|
72
|
+
status=new_status,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return correction
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Captures a Git commit as an Event and its supporting Evidence.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from quilchoom.domain.event import Event
|
|
9
|
+
from quilchoom.domain.evidence import Evidence
|
|
10
|
+
from quilchoom.domain.project import Project
|
|
11
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
12
|
+
EventRepository,
|
|
13
|
+
EvidenceRepository,
|
|
14
|
+
)
|
|
15
|
+
from quilchoom.infrastructure.git.repository import inspect_commit
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def capture_commit(
|
|
19
|
+
project: Project,
|
|
20
|
+
repository_path: Path,
|
|
21
|
+
commit_sha: str,
|
|
22
|
+
event_repository: EventRepository,
|
|
23
|
+
evidence_repository: EvidenceRepository,
|
|
24
|
+
) -> tuple[Event, Evidence]:
|
|
25
|
+
commit = inspect_commit(repository_path, commit_sha)
|
|
26
|
+
|
|
27
|
+
existing_event = event_repository.get_by_source_reference(
|
|
28
|
+
project.id,
|
|
29
|
+
"git",
|
|
30
|
+
commit.sha,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
existing_evidence = evidence_repository.get_by_reference(
|
|
34
|
+
project.id,
|
|
35
|
+
"git",
|
|
36
|
+
commit.sha,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if existing_event is not None and existing_evidence is not None:
|
|
40
|
+
return existing_event, existing_evidence
|
|
41
|
+
|
|
42
|
+
event = existing_event or Event(
|
|
43
|
+
project_id=project.id,
|
|
44
|
+
type="git_commit",
|
|
45
|
+
timestamp=commit.timestamp,
|
|
46
|
+
summary=commit.message,
|
|
47
|
+
source="git",
|
|
48
|
+
source_reference=commit.sha,
|
|
49
|
+
)
|
|
50
|
+
evidence = existing_evidence or Evidence(
|
|
51
|
+
project_id=project.id,
|
|
52
|
+
type="git_diff",
|
|
53
|
+
content=commit.diff,
|
|
54
|
+
reference=commit.sha,
|
|
55
|
+
captured_at=datetime.now(UTC),
|
|
56
|
+
source="git",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if existing_event is None and existing_evidence is None:
|
|
60
|
+
event_repository.save_with_evidence(event, evidence)
|
|
61
|
+
|
|
62
|
+
elif existing_event is None:
|
|
63
|
+
event_repository.save(event)
|
|
64
|
+
|
|
65
|
+
elif existing_evidence is None:
|
|
66
|
+
evidence_repository.save(evidence)
|
|
67
|
+
|
|
68
|
+
return event, evidence
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Captures uncaptured Git commits for a Quilchoom project.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from quilchoom.application.capture_commit import capture_commit
|
|
9
|
+
from quilchoom.domain.project import Project
|
|
10
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
11
|
+
EventRepository,
|
|
12
|
+
EvidenceRepository,
|
|
13
|
+
)
|
|
14
|
+
from quilchoom.infrastructure.git.repository import list_commit_shas
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class CaptureResult:
|
|
19
|
+
"""Represents the result of capturing repository development activity."""
|
|
20
|
+
|
|
21
|
+
discovered_commits: int
|
|
22
|
+
captured_commits: int
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def capture_repository(
|
|
26
|
+
project: Project,
|
|
27
|
+
repository_path: Path,
|
|
28
|
+
event_repository: EventRepository,
|
|
29
|
+
evidence_repository: EvidenceRepository,
|
|
30
|
+
) -> CaptureResult:
|
|
31
|
+
commit_shas = list_commit_shas(repository_path)
|
|
32
|
+
|
|
33
|
+
existing_evidence = evidence_repository.list_for_project(project.id)
|
|
34
|
+
|
|
35
|
+
references = {
|
|
36
|
+
evidence.reference for evidence in existing_evidence if evidence.source == "git"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
uncaptured = [sha for sha in commit_shas if sha not in references]
|
|
40
|
+
|
|
41
|
+
for sha in uncaptured:
|
|
42
|
+
capture_commit(
|
|
43
|
+
project=project,
|
|
44
|
+
repository_path=repository_path,
|
|
45
|
+
commit_sha=sha,
|
|
46
|
+
event_repository=event_repository,
|
|
47
|
+
evidence_repository=evidence_repository,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return CaptureResult(
|
|
51
|
+
discovered_commits=len(commit_shas),
|
|
52
|
+
captured_commits=len(uncaptured),
|
|
53
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Determines whether a document version relies on outdated knowledge claims.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from quilchoom.domain.document import DocumentVersion
|
|
6
|
+
from quilchoom.domain.knowledge_claim import ClaimStatus
|
|
7
|
+
from quilchoom.infrastructure.database.errors import KnowledgeClaimNotFoundError
|
|
8
|
+
from quilchoom.infrastructure.database.repositories import KnowledgeClaimRepository
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_document_version_stale(
|
|
12
|
+
version: DocumentVersion,
|
|
13
|
+
claim_repository: KnowledgeClaimRepository,
|
|
14
|
+
) -> bool:
|
|
15
|
+
for claim_id in version.claim_ids:
|
|
16
|
+
claim = claim_repository.get_by_id(claim_id)
|
|
17
|
+
|
|
18
|
+
if claim is None:
|
|
19
|
+
raise KnowledgeClaimNotFoundError(
|
|
20
|
+
f"Knowledge claim referenced by document version not found: {claim_id}"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if claim.status != ClaimStatus.ACTIVE:
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
return False
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Determines whether a project has captured evidence awaiting interpretation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from quilchoom.domain.project import Project
|
|
6
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
7
|
+
EvidenceRepository,
|
|
8
|
+
InterpretationRunRepository,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def count_pending_evidence(
|
|
13
|
+
project: Project,
|
|
14
|
+
evidence_repository: EvidenceRepository,
|
|
15
|
+
run_repository: InterpretationRunRepository,
|
|
16
|
+
) -> int:
|
|
17
|
+
"""Counts captured evidence that has not yet been interpreted."""
|
|
18
|
+
|
|
19
|
+
evidence = evidence_repository.list_for_project(project.id)
|
|
20
|
+
interpreted_ids = run_repository.list_interpreted_evidence_ids(project.id)
|
|
21
|
+
|
|
22
|
+
pending_ids = {item.id for item in evidence if item.id not in interpreted_ids}
|
|
23
|
+
|
|
24
|
+
return len(pending_ids)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Creates new versions of existing documentation artifacts.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from quilchoom.domain.document import DocumentVersion, DocumentVersionOrigin
|
|
8
|
+
from quilchoom.infrastructure.database.errors import DocumentNotFoundError
|
|
9
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
10
|
+
DocumentRepository,
|
|
11
|
+
DocumentVersionRepository,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_document_version(
|
|
16
|
+
document_id: UUID,
|
|
17
|
+
content: str,
|
|
18
|
+
origin: DocumentVersionOrigin,
|
|
19
|
+
claim_ids: list[UUID],
|
|
20
|
+
document_repository: DocumentRepository,
|
|
21
|
+
version_repository: DocumentVersionRepository,
|
|
22
|
+
) -> DocumentVersion:
|
|
23
|
+
target_document = document_repository.get_by_id(document_id)
|
|
24
|
+
|
|
25
|
+
if target_document is None:
|
|
26
|
+
raise DocumentNotFoundError(f"Target document not found: {document_id}")
|
|
27
|
+
|
|
28
|
+
latest_version = version_repository.get_latest(document_id)
|
|
29
|
+
|
|
30
|
+
if latest_version is not None:
|
|
31
|
+
version_number = latest_version.version_number + 1
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
version_number = 1
|
|
35
|
+
|
|
36
|
+
version = DocumentVersion(
|
|
37
|
+
document_id=document_id,
|
|
38
|
+
version_number=version_number,
|
|
39
|
+
content=content,
|
|
40
|
+
origin=origin,
|
|
41
|
+
claim_ids=claim_ids,
|
|
42
|
+
)
|
|
43
|
+
version_repository.save(version)
|
|
44
|
+
|
|
45
|
+
return version
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines application-level errors for Quilchoom.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ProjectInitializationError(Exception):
|
|
7
|
+
"""Raised when a Quilchoom project cannot be initialized."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProjectRuntimeError(Exception):
|
|
11
|
+
"""Raised when an existing Quilchoom project cannot be loaded."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HistoryReconstructionError(Exception):
|
|
15
|
+
"""Raised when a project's development history cannot be reconstructed."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CorrectionProjectMismatchError(Exception):
|
|
19
|
+
"""Raised when a correction references a claim from another project."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SelfReplacementError(Exception):
|
|
23
|
+
"""Raised when a claim is used as its own replacement."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InterpretationProjectMismatchError(Exception):
|
|
27
|
+
"""Raised when interpretation inputs belong to different projects."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class InvalidCandidateEvidenceError(Exception):
|
|
31
|
+
"""Raised when an interpretation candidate references unavailable evidence."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NoActiveClaimsError(Exception):
|
|
35
|
+
"""Raised when document generation has no active knowledge claims."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class InvalidGeneratedClaimError(Exception):
|
|
39
|
+
"""Raised when generated document provenance references an unavailable claim."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DocumentUnavailableError(Exception):
|
|
43
|
+
"""Raised when a requested project document has not been generated."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DocumentVersionUnavailableError(Exception):
|
|
47
|
+
"""Raised when a requested document version does not exist."""
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generates documentation artifacts from active project knowledge.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from quilchoom.application.create_document_version import create_document_version
|
|
6
|
+
from quilchoom.application.errors import InvalidGeneratedClaimError, NoActiveClaimsError
|
|
7
|
+
from quilchoom.domain.document import Document, DocumentVersion, DocumentVersionOrigin
|
|
8
|
+
from quilchoom.domain.knowledge_claim import ClaimStatus
|
|
9
|
+
from quilchoom.domain.project import Project
|
|
10
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
11
|
+
DocumentRepository,
|
|
12
|
+
DocumentVersionRepository,
|
|
13
|
+
KnowledgeClaimRepository,
|
|
14
|
+
)
|
|
15
|
+
from quilchoom.interfaces.document_generator import (
|
|
16
|
+
DocumentGenerationContext,
|
|
17
|
+
DocumentGenerator,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def generate_document(
|
|
22
|
+
project: Project,
|
|
23
|
+
key: str,
|
|
24
|
+
kind: str,
|
|
25
|
+
generator: DocumentGenerator,
|
|
26
|
+
document_repository: DocumentRepository,
|
|
27
|
+
version_repository: DocumentVersionRepository,
|
|
28
|
+
claim_repository: KnowledgeClaimRepository,
|
|
29
|
+
) -> DocumentVersion:
|
|
30
|
+
existing_document = document_repository.get_by_key(project.id, key)
|
|
31
|
+
|
|
32
|
+
if existing_document is None:
|
|
33
|
+
document = Document(
|
|
34
|
+
project_id=project.id,
|
|
35
|
+
key=key,
|
|
36
|
+
kind=kind,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
else:
|
|
40
|
+
document = existing_document
|
|
41
|
+
|
|
42
|
+
claims = claim_repository.list_for_project(project.id)
|
|
43
|
+
active_claims = [claim for claim in claims if claim.status == ClaimStatus.ACTIVE]
|
|
44
|
+
|
|
45
|
+
if not active_claims:
|
|
46
|
+
raise NoActiveClaimsError(
|
|
47
|
+
f"No active knowledge claims available for project: {project.id}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
context = DocumentGenerationContext(
|
|
51
|
+
project=project,
|
|
52
|
+
document=document,
|
|
53
|
+
claims=active_claims,
|
|
54
|
+
)
|
|
55
|
+
result = generator.generate(context)
|
|
56
|
+
|
|
57
|
+
allowed_claim_ids = {claim.id for claim in active_claims}
|
|
58
|
+
|
|
59
|
+
for claim_id in result.claim_ids:
|
|
60
|
+
if claim_id not in allowed_claim_ids:
|
|
61
|
+
raise InvalidGeneratedClaimError(
|
|
62
|
+
f"Generated document references unavailable claim: {claim_id}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if existing_document is None:
|
|
66
|
+
version = DocumentVersion(
|
|
67
|
+
document_id=document.id,
|
|
68
|
+
version_number=1,
|
|
69
|
+
content=result.content,
|
|
70
|
+
origin=DocumentVersionOrigin.GENERATED,
|
|
71
|
+
claim_ids=result.claim_ids,
|
|
72
|
+
)
|
|
73
|
+
document_repository.save_with_initial_version(document, version)
|
|
74
|
+
|
|
75
|
+
return version
|
|
76
|
+
|
|
77
|
+
return create_document_version(
|
|
78
|
+
document_id=document.id,
|
|
79
|
+
content=result.content,
|
|
80
|
+
origin=DocumentVersionOrigin.GENERATED,
|
|
81
|
+
claim_ids=result.claim_ids,
|
|
82
|
+
document_repository=document_repository,
|
|
83
|
+
version_repository=version_repository,
|
|
84
|
+
)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Retrieves a requested version of a project document.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from quilchoom.application.errors import (
|
|
6
|
+
DocumentUnavailableError,
|
|
7
|
+
DocumentVersionUnavailableError,
|
|
8
|
+
)
|
|
9
|
+
from quilchoom.domain.document import DocumentVersion
|
|
10
|
+
from quilchoom.domain.project import Project
|
|
11
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
12
|
+
DocumentRepository,
|
|
13
|
+
DocumentVersionRepository,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_document_version(
|
|
18
|
+
project: Project,
|
|
19
|
+
key: str,
|
|
20
|
+
version_number: int | None,
|
|
21
|
+
document_repository: DocumentRepository,
|
|
22
|
+
version_repository: DocumentVersionRepository,
|
|
23
|
+
) -> DocumentVersion:
|
|
24
|
+
"""Retrieves the latest or requested version of a project document."""
|
|
25
|
+
|
|
26
|
+
document = document_repository.get_by_key(project_id=project.id, key=key)
|
|
27
|
+
|
|
28
|
+
if document is None:
|
|
29
|
+
raise DocumentUnavailableError(f"Document not found: {key}")
|
|
30
|
+
|
|
31
|
+
if version_number is None:
|
|
32
|
+
version = version_repository.get_latest(document_id=document.id)
|
|
33
|
+
|
|
34
|
+
else:
|
|
35
|
+
version = version_repository.get_by_version_number(
|
|
36
|
+
document_id=document.id, version_number=version_number
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if version is None:
|
|
40
|
+
if version_number is None:
|
|
41
|
+
raise DocumentVersionUnavailableError("Latest document version not found")
|
|
42
|
+
|
|
43
|
+
else:
|
|
44
|
+
raise DocumentVersionUnavailableError(
|
|
45
|
+
f"Document version not found: {version_number}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return version
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Determines the current local workflow status of a Quilchoom project.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from quilchoom.application.check_document_staleness import (
|
|
11
|
+
is_document_version_stale,
|
|
12
|
+
)
|
|
13
|
+
from quilchoom.application.check_knowledge_status import count_pending_evidence
|
|
14
|
+
from quilchoom.domain.knowledge_claim import ClaimStatus
|
|
15
|
+
from quilchoom.domain.project import Project
|
|
16
|
+
from quilchoom.infrastructure.database.repositories import (
|
|
17
|
+
DocumentRepository,
|
|
18
|
+
DocumentVersionRepository,
|
|
19
|
+
EvidenceRepository,
|
|
20
|
+
InterpretationRunRepository,
|
|
21
|
+
KnowledgeClaimRepository,
|
|
22
|
+
)
|
|
23
|
+
from quilchoom.infrastructure.git.repository import list_commit_shas
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DocumentState(str, Enum):
|
|
27
|
+
"""Defines the freshness state of a generated project document."""
|
|
28
|
+
|
|
29
|
+
CURRENT = "current"
|
|
30
|
+
STALE = "stale"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DocumentStatus(BaseModel):
|
|
34
|
+
"""Represents the current status of a generated project document."""
|
|
35
|
+
|
|
36
|
+
key: str
|
|
37
|
+
kind: str
|
|
38
|
+
version_number: int
|
|
39
|
+
state: DocumentState
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ProjectStatus(BaseModel):
|
|
43
|
+
"""Represents the current local workflow status of a Quilchoom project."""
|
|
44
|
+
|
|
45
|
+
total_commits: int
|
|
46
|
+
pending_commits: int
|
|
47
|
+
pending_evidence: int
|
|
48
|
+
active_claims: int
|
|
49
|
+
documents: list[DocumentStatus]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_project_status(
|
|
53
|
+
project: Project,
|
|
54
|
+
repository_root: Path,
|
|
55
|
+
evidence_repository: EvidenceRepository,
|
|
56
|
+
run_repository: InterpretationRunRepository,
|
|
57
|
+
claim_repository: KnowledgeClaimRepository,
|
|
58
|
+
document_repository: DocumentRepository,
|
|
59
|
+
version_repository: DocumentVersionRepository,
|
|
60
|
+
) -> ProjectStatus:
|
|
61
|
+
"""Determines the current local workflow status of a project."""
|
|
62
|
+
|
|
63
|
+
commit_shas = list_commit_shas(repository_root)
|
|
64
|
+
|
|
65
|
+
evidence = evidence_repository.list_for_project(project.id)
|
|
66
|
+
|
|
67
|
+
captured_commit_shas = {item.reference for item in evidence if item.source == "git"}
|
|
68
|
+
|
|
69
|
+
total_commits = len(commit_shas)
|
|
70
|
+
pending_commits = sum(1 for sha in commit_shas if sha not in captured_commit_shas)
|
|
71
|
+
|
|
72
|
+
pending_evidence = count_pending_evidence(
|
|
73
|
+
project=project,
|
|
74
|
+
evidence_repository=evidence_repository,
|
|
75
|
+
run_repository=run_repository,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
claims = claim_repository.list_for_project(project.id)
|
|
79
|
+
active_claims = sum(1 for claim in claims if claim.status == ClaimStatus.ACTIVE)
|
|
80
|
+
|
|
81
|
+
documents = document_repository.list_for_project(project.id)
|
|
82
|
+
|
|
83
|
+
document_statuses: list[DocumentStatus] = []
|
|
84
|
+
|
|
85
|
+
for document in documents:
|
|
86
|
+
latest_version = version_repository.get_latest(document.id)
|
|
87
|
+
|
|
88
|
+
if latest_version is None:
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
stale = is_document_version_stale(
|
|
92
|
+
version=latest_version,
|
|
93
|
+
claim_repository=claim_repository,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
document_status = DocumentStatus(
|
|
97
|
+
key=document.key,
|
|
98
|
+
kind=document.kind,
|
|
99
|
+
version_number=latest_version.version_number,
|
|
100
|
+
state=(DocumentState.STALE if stale else DocumentState.CURRENT),
|
|
101
|
+
)
|
|
102
|
+
document_statuses.append(document_status)
|
|
103
|
+
|
|
104
|
+
return ProjectStatus(
|
|
105
|
+
total_commits=total_commits,
|
|
106
|
+
pending_commits=pending_commits,
|
|
107
|
+
pending_evidence=pending_evidence,
|
|
108
|
+
active_claims=active_claims,
|
|
109
|
+
documents=document_statuses,
|
|
110
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Initializes a Quilchoom project from a Git repository.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from quilchoom.application.errors import ProjectInitializationError
|
|
8
|
+
from quilchoom.domain.project import Project
|
|
9
|
+
from quilchoom.infrastructure.config import write_default_config
|
|
10
|
+
from quilchoom.infrastructure.database.connection import create_database_engine
|
|
11
|
+
from quilchoom.infrastructure.database.migrations import upgrade_database
|
|
12
|
+
from quilchoom.infrastructure.database.repositories import ProjectRepository
|
|
13
|
+
from quilchoom.infrastructure.filesystem.workspace import initialize_workspace
|
|
14
|
+
from quilchoom.infrastructure.git.repository import find_repository_root
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def initialize_project(repository_path: Path | None = None) -> Project:
|
|
18
|
+
repo_root = find_repository_root(repository_path)
|
|
19
|
+
|
|
20
|
+
if repo_root is None:
|
|
21
|
+
raise ProjectInitializationError()
|
|
22
|
+
|
|
23
|
+
workspace = initialize_workspace(repo_root)
|
|
24
|
+
|
|
25
|
+
config_path = workspace / "config.toml"
|
|
26
|
+
write_default_config(config_path)
|
|
27
|
+
|
|
28
|
+
db_path = workspace / "quilchoom.db"
|
|
29
|
+
upgrade_database(db_path)
|
|
30
|
+
|
|
31
|
+
db_engine = create_database_engine(db_path)
|
|
32
|
+
|
|
33
|
+
project_repository = ProjectRepository(db_engine)
|
|
34
|
+
existing_project = project_repository.get()
|
|
35
|
+
|
|
36
|
+
if existing_project is not None:
|
|
37
|
+
if existing_project.repository_path == repo_root:
|
|
38
|
+
return existing_project
|
|
39
|
+
|
|
40
|
+
updated_project = project_repository.update_repository_path(
|
|
41
|
+
existing_project.id,
|
|
42
|
+
repo_root,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return updated_project
|
|
46
|
+
|
|
47
|
+
project = Project(
|
|
48
|
+
name=repo_root.name,
|
|
49
|
+
repository_path=repo_root,
|
|
50
|
+
)
|
|
51
|
+
project_repository.save(project)
|
|
52
|
+
|
|
53
|
+
return project
|