elspais 0.11.1__py3-none-any.whl → 0.43.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.
- elspais/__init__.py +2 -11
- elspais/{sponsors/__init__.py → associates.py} +102 -58
- elspais/cli.py +395 -79
- elspais/commands/__init__.py +9 -3
- elspais/commands/analyze.py +121 -173
- elspais/commands/changed.py +15 -30
- elspais/commands/config_cmd.py +13 -16
- elspais/commands/edit.py +60 -44
- elspais/commands/example_cmd.py +319 -0
- elspais/commands/hash_cmd.py +167 -183
- elspais/commands/health.py +1177 -0
- elspais/commands/index.py +98 -114
- elspais/commands/init.py +103 -26
- elspais/commands/reformat_cmd.py +41 -444
- elspais/commands/rules_cmd.py +7 -3
- elspais/commands/trace.py +444 -321
- elspais/commands/validate.py +195 -415
- elspais/config/__init__.py +799 -5
- elspais/{core/content_rules.py → content_rules.py} +20 -3
- elspais/docs/cli/assertions.md +67 -0
- elspais/docs/cli/commands.md +304 -0
- elspais/docs/cli/config.md +262 -0
- elspais/docs/cli/format.md +66 -0
- elspais/docs/cli/git.md +45 -0
- elspais/docs/cli/health.md +190 -0
- elspais/docs/cli/hierarchy.md +60 -0
- elspais/docs/cli/ignore.md +72 -0
- elspais/docs/cli/mcp.md +245 -0
- elspais/docs/cli/quickstart.md +58 -0
- elspais/docs/cli/traceability.md +89 -0
- elspais/docs/cli/validation.md +96 -0
- elspais/graph/GraphNode.py +383 -0
- elspais/graph/__init__.py +40 -0
- elspais/graph/annotators.py +927 -0
- elspais/graph/builder.py +1886 -0
- elspais/graph/deserializer.py +248 -0
- elspais/graph/factory.py +284 -0
- elspais/graph/metrics.py +127 -0
- elspais/graph/mutations.py +161 -0
- elspais/graph/parsers/__init__.py +156 -0
- elspais/graph/parsers/code.py +213 -0
- elspais/graph/parsers/comments.py +112 -0
- elspais/graph/parsers/config_helpers.py +29 -0
- elspais/graph/parsers/heredocs.py +225 -0
- elspais/graph/parsers/journey.py +131 -0
- elspais/graph/parsers/remainder.py +79 -0
- elspais/graph/parsers/requirement.py +347 -0
- elspais/graph/parsers/results/__init__.py +6 -0
- elspais/graph/parsers/results/junit_xml.py +229 -0
- elspais/graph/parsers/results/pytest_json.py +313 -0
- elspais/graph/parsers/test.py +305 -0
- elspais/graph/relations.py +78 -0
- elspais/graph/serialize.py +216 -0
- elspais/html/__init__.py +8 -0
- elspais/html/generator.py +731 -0
- elspais/html/templates/trace_view.html.j2 +2151 -0
- elspais/mcp/__init__.py +47 -29
- elspais/mcp/__main__.py +5 -1
- elspais/mcp/file_mutations.py +138 -0
- elspais/mcp/server.py +2016 -247
- elspais/testing/__init__.py +4 -4
- elspais/testing/config.py +3 -0
- elspais/testing/mapper.py +1 -1
- elspais/testing/result_parser.py +25 -21
- elspais/testing/scanner.py +301 -12
- elspais/utilities/__init__.py +1 -0
- elspais/utilities/docs_loader.py +115 -0
- elspais/utilities/git.py +607 -0
- elspais/{core → utilities}/hasher.py +8 -22
- elspais/utilities/md_renderer.py +189 -0
- elspais/{core → utilities}/patterns.py +58 -57
- elspais/utilities/reference_config.py +626 -0
- elspais/validation/__init__.py +19 -0
- elspais/validation/format.py +264 -0
- {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
- elspais-0.43.5.dist-info/RECORD +80 -0
- elspais/config/defaults.py +0 -173
- elspais/config/loader.py +0 -494
- elspais/core/__init__.py +0 -21
- elspais/core/git.py +0 -352
- elspais/core/models.py +0 -320
- elspais/core/parser.py +0 -640
- elspais/core/rules.py +0 -514
- elspais/mcp/context.py +0 -171
- elspais/mcp/serializers.py +0 -112
- elspais/reformat/__init__.py +0 -50
- elspais/reformat/detector.py +0 -119
- elspais/reformat/hierarchy.py +0 -246
- elspais/reformat/line_breaks.py +0 -220
- elspais/reformat/prompts.py +0 -123
- elspais/reformat/transformer.py +0 -264
- elspais/trace_view/__init__.py +0 -54
- elspais/trace_view/coverage.py +0 -183
- elspais/trace_view/generators/__init__.py +0 -12
- elspais/trace_view/generators/base.py +0 -329
- elspais/trace_view/generators/csv.py +0 -122
- elspais/trace_view/generators/markdown.py +0 -175
- elspais/trace_view/html/__init__.py +0 -31
- elspais/trace_view/html/generator.py +0 -1006
- elspais/trace_view/html/templates/base.html +0 -283
- elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
- elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
- elspais/trace_view/html/templates/components/legend_modal.html +0 -69
- elspais/trace_view/html/templates/components/review_panel.html +0 -118
- elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
- elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
- elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
- elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
- elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
- elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
- elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
- elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
- elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
- elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
- elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
- elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
- elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
- elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
- elspais/trace_view/html/templates/partials/scripts.js +0 -1741
- elspais/trace_view/html/templates/partials/styles.css +0 -1756
- elspais/trace_view/models.py +0 -353
- elspais/trace_view/review/__init__.py +0 -60
- elspais/trace_view/review/branches.py +0 -1149
- elspais/trace_view/review/models.py +0 -1205
- elspais/trace_view/review/position.py +0 -609
- elspais/trace_view/review/server.py +0 -1056
- elspais/trace_view/review/status.py +0 -470
- elspais/trace_view/review/storage.py +0 -1367
- elspais/trace_view/scanning.py +0 -213
- elspais/trace_view/specs/README.md +0 -84
- elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
- elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
- elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
- elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
- elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
- elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
- elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
- elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
- elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
- elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
- elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
- elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
- elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
- elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
- elspais-0.11.1.dist-info/RECORD +0 -101
- {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
- {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
- {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/licenses/LICENSE +0 -0
elspais/trace_view/models.py
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
# Implements: REQ-int-d00004 (Model Adapter)
|
|
2
|
-
"""
|
|
3
|
-
elspais.trace_view.models - Data models for trace-view.
|
|
4
|
-
|
|
5
|
-
Provides TraceViewRequirement which wraps core Requirement with:
|
|
6
|
-
- Git state tracking (uncommitted, modified, moved)
|
|
7
|
-
- Test coverage information
|
|
8
|
-
- Implementation file references
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from dataclasses import dataclass, field
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Dict, List, Optional, Set, Tuple
|
|
14
|
-
|
|
15
|
-
from elspais.core.models import Requirement as CoreRequirement
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class TestInfo:
|
|
20
|
-
"""Represents test coverage for a requirement.
|
|
21
|
-
|
|
22
|
-
Attributes:
|
|
23
|
-
test_count: Number of automated tests
|
|
24
|
-
manual_test_count: Number of manual tests
|
|
25
|
-
test_status: Status ('not_tested', 'passed', 'failed', 'error', 'skipped')
|
|
26
|
-
test_details: List of test result details
|
|
27
|
-
notes: Additional notes about testing
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
test_count: int = 0
|
|
31
|
-
manual_test_count: int = 0
|
|
32
|
-
test_status: str = "not_tested"
|
|
33
|
-
test_details: List[Dict] = field(default_factory=list)
|
|
34
|
-
notes: str = ""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@dataclass
|
|
38
|
-
class GitChangeInfo:
|
|
39
|
-
"""Container for git change state.
|
|
40
|
-
|
|
41
|
-
Injected into TraceViewRequirement rather than using a global singleton.
|
|
42
|
-
This allows for better testing and explicit dependency management.
|
|
43
|
-
|
|
44
|
-
Attributes:
|
|
45
|
-
uncommitted_files: Set of spec-relative paths with uncommitted changes
|
|
46
|
-
untracked_files: Set of spec-relative paths that are untracked (new)
|
|
47
|
-
branch_changed_files: Set of spec-relative paths changed vs main branch
|
|
48
|
-
committed_req_locations: Map of requirement ID to committed file path
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
uncommitted_files: Set[str] = field(default_factory=set)
|
|
52
|
-
untracked_files: Set[str] = field(default_factory=set)
|
|
53
|
-
branch_changed_files: Set[str] = field(default_factory=set)
|
|
54
|
-
committed_req_locations: Dict[str, str] = field(default_factory=dict)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass
|
|
58
|
-
class TraceViewRequirement:
|
|
59
|
-
"""Requirement enriched with trace-view data.
|
|
60
|
-
|
|
61
|
-
Wraps a core Requirement and adds:
|
|
62
|
-
- Git state properties (is_uncommitted, is_branch_changed, is_moved)
|
|
63
|
-
- Test coverage information
|
|
64
|
-
- Implementation file references
|
|
65
|
-
|
|
66
|
-
Implements: REQ-int-d00004-A (wraps elspais.core.models.Requirement)
|
|
67
|
-
Implements: REQ-int-d00004-B (git state injected, not global)
|
|
68
|
-
Implements: REQ-int-d00004-C (implementation files stored per-requirement)
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
core: CoreRequirement
|
|
72
|
-
test_info: Optional[TestInfo] = None
|
|
73
|
-
implementation_files: List[Tuple[str, int]] = field(default_factory=list)
|
|
74
|
-
git_info: Optional[GitChangeInfo] = None
|
|
75
|
-
external_spec_path: Optional[str] = None
|
|
76
|
-
|
|
77
|
-
# --- Delegated core properties ---
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def id(self) -> str:
|
|
81
|
-
"""Requirement ID without REQ- prefix for display."""
|
|
82
|
-
return self.core.id.replace("REQ-", "")
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def full_id(self) -> str:
|
|
86
|
-
"""Full requirement ID including REQ- prefix."""
|
|
87
|
-
return self.core.id
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def title(self) -> str:
|
|
91
|
-
return self.core.title
|
|
92
|
-
|
|
93
|
-
@property
|
|
94
|
-
def level(self) -> str:
|
|
95
|
-
"""Level normalized to uppercase (PRD, OPS, DEV)."""
|
|
96
|
-
return self.core.level.upper()
|
|
97
|
-
|
|
98
|
-
@property
|
|
99
|
-
def status(self) -> str:
|
|
100
|
-
return self.core.status
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def implements(self) -> List[str]:
|
|
104
|
-
return self.core.implements
|
|
105
|
-
|
|
106
|
-
@property
|
|
107
|
-
def file_path(self) -> Path:
|
|
108
|
-
return self.core.file_path or Path("unknown")
|
|
109
|
-
|
|
110
|
-
@property
|
|
111
|
-
def line_number(self) -> int:
|
|
112
|
-
return self.core.line_number or 0
|
|
113
|
-
|
|
114
|
-
@property
|
|
115
|
-
def hash(self) -> str:
|
|
116
|
-
return self.core.hash or ""
|
|
117
|
-
|
|
118
|
-
@property
|
|
119
|
-
def body(self) -> str:
|
|
120
|
-
return self.core.body
|
|
121
|
-
|
|
122
|
-
@property
|
|
123
|
-
def rationale(self) -> str:
|
|
124
|
-
return self.core.rationale or ""
|
|
125
|
-
|
|
126
|
-
@property
|
|
127
|
-
def is_roadmap(self) -> bool:
|
|
128
|
-
return self.core.is_roadmap
|
|
129
|
-
|
|
130
|
-
@property
|
|
131
|
-
def is_conflict(self) -> bool:
|
|
132
|
-
return self.core.is_conflict
|
|
133
|
-
|
|
134
|
-
@property
|
|
135
|
-
def conflict_with(self) -> str:
|
|
136
|
-
return self.core.conflict_with
|
|
137
|
-
|
|
138
|
-
@property
|
|
139
|
-
def subdir(self) -> str:
|
|
140
|
-
return self.core.subdir
|
|
141
|
-
|
|
142
|
-
# --- Computed properties ---
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def repo_prefix(self) -> Optional[str]:
|
|
146
|
-
"""Extract repo prefix from ID (e.g., 'CAL-d00005' → 'CAL').
|
|
147
|
-
|
|
148
|
-
Associated repo requirements have format PREFIX-{level}{number}.
|
|
149
|
-
Core repo requirements have format {level}{number} (returns None).
|
|
150
|
-
"""
|
|
151
|
-
import re
|
|
152
|
-
|
|
153
|
-
# Match: optional prefix (uppercase letters), then level (p/o/d) and 5 digits
|
|
154
|
-
match = re.match(r"^([A-Z]+-)?([pod]\d{5})$", self.id, re.IGNORECASE)
|
|
155
|
-
if match and match.group(1):
|
|
156
|
-
return match.group(1).rstrip("-")
|
|
157
|
-
return None
|
|
158
|
-
|
|
159
|
-
@property
|
|
160
|
-
def display_filename(self) -> str:
|
|
161
|
-
"""Get displayable filename with repo prefix for external repos.
|
|
162
|
-
|
|
163
|
-
Returns 'CAL/filename.md' for external repos, 'filename.md' for core.
|
|
164
|
-
"""
|
|
165
|
-
prefix = self.repo_prefix
|
|
166
|
-
if prefix:
|
|
167
|
-
return f"{prefix}/{self.file_path.name}"
|
|
168
|
-
return self.file_path.name
|
|
169
|
-
|
|
170
|
-
# --- Git state properties ---
|
|
171
|
-
|
|
172
|
-
def _get_spec_relative_path(self) -> str:
|
|
173
|
-
"""Get the spec-relative path for this requirement's file."""
|
|
174
|
-
if self.is_roadmap:
|
|
175
|
-
return f"spec/roadmap/{self.file_path.name}"
|
|
176
|
-
if self.subdir:
|
|
177
|
-
return f"spec/{self.subdir}/{self.file_path.name}"
|
|
178
|
-
return f"spec/{self.file_path.name}"
|
|
179
|
-
|
|
180
|
-
def _is_in_untracked_file(self) -> bool:
|
|
181
|
-
"""Check if requirement is in an untracked (new) file."""
|
|
182
|
-
if not self.git_info:
|
|
183
|
-
return False
|
|
184
|
-
rel_path = self._get_spec_relative_path()
|
|
185
|
-
return rel_path in self.git_info.untracked_files
|
|
186
|
-
|
|
187
|
-
def _check_modified_in_fileset(self, file_set: Set[str]) -> bool:
|
|
188
|
-
"""Check if requirement is modified based on a set of changed files."""
|
|
189
|
-
if not self.git_info:
|
|
190
|
-
return False
|
|
191
|
-
|
|
192
|
-
rel_path = self._get_spec_relative_path()
|
|
193
|
-
|
|
194
|
-
# Check if file is untracked (new) - all REQs in new files are considered modified
|
|
195
|
-
if rel_path in self.git_info.untracked_files:
|
|
196
|
-
return True
|
|
197
|
-
|
|
198
|
-
# Check if file is in the modified set
|
|
199
|
-
return rel_path in file_set
|
|
200
|
-
|
|
201
|
-
@property
|
|
202
|
-
def is_uncommitted(self) -> bool:
|
|
203
|
-
"""Check if requirement has uncommitted changes."""
|
|
204
|
-
if not self.git_info:
|
|
205
|
-
return False
|
|
206
|
-
return self._check_modified_in_fileset(self.git_info.uncommitted_files)
|
|
207
|
-
|
|
208
|
-
@property
|
|
209
|
-
def is_branch_changed(self) -> bool:
|
|
210
|
-
"""Check if requirement changed vs main branch."""
|
|
211
|
-
if not self.git_info:
|
|
212
|
-
return False
|
|
213
|
-
return self._check_modified_in_fileset(self.git_info.branch_changed_files)
|
|
214
|
-
|
|
215
|
-
@property
|
|
216
|
-
def is_new(self) -> bool:
|
|
217
|
-
"""Check if requirement is in a new (untracked) file."""
|
|
218
|
-
return self._is_in_untracked_file()
|
|
219
|
-
|
|
220
|
-
@property
|
|
221
|
-
def is_modified(self) -> bool:
|
|
222
|
-
"""Check if requirement has modified content but is not in a new file."""
|
|
223
|
-
if self._is_in_untracked_file():
|
|
224
|
-
return False # New files are "new", not "modified"
|
|
225
|
-
return self.is_uncommitted
|
|
226
|
-
|
|
227
|
-
@property
|
|
228
|
-
def is_moved(self) -> bool:
|
|
229
|
-
"""Check if requirement was moved from a different file.
|
|
230
|
-
|
|
231
|
-
A requirement is considered moved if:
|
|
232
|
-
- It exists in the committed state in a different file, OR
|
|
233
|
-
- It's in a new file but has a non-TBD hash (suggesting it was copied/moved)
|
|
234
|
-
"""
|
|
235
|
-
if not self.git_info:
|
|
236
|
-
return False
|
|
237
|
-
|
|
238
|
-
current_path = self._get_spec_relative_path()
|
|
239
|
-
committed_path = self.git_info.committed_req_locations.get(self.id)
|
|
240
|
-
|
|
241
|
-
if committed_path is not None:
|
|
242
|
-
# REQ existed in committed state - check if path changed
|
|
243
|
-
return committed_path != current_path
|
|
244
|
-
|
|
245
|
-
# REQ doesn't exist in committed state
|
|
246
|
-
# If it's in a new file but has a real hash, it was likely moved/copied
|
|
247
|
-
if self._is_in_untracked_file() and self.hash and self.hash != "TBD":
|
|
248
|
-
return True
|
|
249
|
-
|
|
250
|
-
return False
|
|
251
|
-
|
|
252
|
-
# --- Factory methods ---
|
|
253
|
-
|
|
254
|
-
@classmethod
|
|
255
|
-
def from_core(
|
|
256
|
-
cls,
|
|
257
|
-
core_req: CoreRequirement,
|
|
258
|
-
git_info: Optional[GitChangeInfo] = None,
|
|
259
|
-
) -> "TraceViewRequirement":
|
|
260
|
-
"""Create TraceViewRequirement from core Requirement.
|
|
261
|
-
|
|
262
|
-
Args:
|
|
263
|
-
core_req: The core Requirement to wrap
|
|
264
|
-
git_info: Optional git change state (inject for testability)
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
TraceViewRequirement instance
|
|
268
|
-
"""
|
|
269
|
-
# Detect external repo paths (absolute paths)
|
|
270
|
-
external_spec_path = None
|
|
271
|
-
if core_req.file_path and core_req.file_path.is_absolute():
|
|
272
|
-
external_spec_path = str(core_req.file_path)
|
|
273
|
-
|
|
274
|
-
return cls(
|
|
275
|
-
core=core_req,
|
|
276
|
-
git_info=git_info,
|
|
277
|
-
external_spec_path=external_spec_path,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
@classmethod
|
|
281
|
-
def from_dict(
|
|
282
|
-
cls,
|
|
283
|
-
req_id: str,
|
|
284
|
-
data: Dict,
|
|
285
|
-
git_info: Optional[GitChangeInfo] = None,
|
|
286
|
-
) -> "TraceViewRequirement":
|
|
287
|
-
"""Create TraceViewRequirement from elspais validate --json output.
|
|
288
|
-
|
|
289
|
-
This provides backward compatibility with code that expects to create
|
|
290
|
-
requirements from JSON data.
|
|
291
|
-
|
|
292
|
-
Args:
|
|
293
|
-
req_id: Full requirement ID (e.g., 'REQ-d00027')
|
|
294
|
-
data: Dict from elspais JSON output
|
|
295
|
-
git_info: Optional git change state
|
|
296
|
-
|
|
297
|
-
Returns:
|
|
298
|
-
TraceViewRequirement instance
|
|
299
|
-
"""
|
|
300
|
-
# Map level to uppercase for consistency
|
|
301
|
-
level_map = {
|
|
302
|
-
"PRD": "PRD",
|
|
303
|
-
"Ops": "OPS",
|
|
304
|
-
"Dev": "DEV",
|
|
305
|
-
"prd": "PRD",
|
|
306
|
-
"ops": "OPS",
|
|
307
|
-
"dev": "DEV",
|
|
308
|
-
}
|
|
309
|
-
level = data.get("level", "")
|
|
310
|
-
subdir = data.get("subdir", "")
|
|
311
|
-
|
|
312
|
-
# Create core requirement
|
|
313
|
-
file_path_str = data.get("filePath", data.get("file", ""))
|
|
314
|
-
file_path = Path(file_path_str) if file_path_str else None
|
|
315
|
-
|
|
316
|
-
core_req = CoreRequirement(
|
|
317
|
-
id=req_id,
|
|
318
|
-
title=data.get("title", ""),
|
|
319
|
-
level=level_map.get(level, level.upper()),
|
|
320
|
-
status=data.get("status", "Active"),
|
|
321
|
-
body=data.get("body", ""),
|
|
322
|
-
implements=data.get("implements", []),
|
|
323
|
-
rationale=data.get("rationale"),
|
|
324
|
-
hash=data.get("hash"),
|
|
325
|
-
file_path=file_path,
|
|
326
|
-
line_number=data.get("line", 0),
|
|
327
|
-
subdir=subdir,
|
|
328
|
-
is_conflict=data.get("isConflict", False),
|
|
329
|
-
conflict_with=data.get("conflictWith", "") or "",
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
# Create trace-view requirement
|
|
333
|
-
tv_req = cls.from_core(core_req, git_info=git_info)
|
|
334
|
-
|
|
335
|
-
# Add test info if provided
|
|
336
|
-
test_count = data.get("test_count", 0)
|
|
337
|
-
if test_count > 0:
|
|
338
|
-
test_passed = data.get("test_passed", 0)
|
|
339
|
-
test_status = "passed" if test_passed == test_count else "failed"
|
|
340
|
-
tv_req.test_info = TestInfo(
|
|
341
|
-
test_count=test_count,
|
|
342
|
-
manual_test_count=0,
|
|
343
|
-
test_status=test_status,
|
|
344
|
-
test_details=data.get("test_result_files", []),
|
|
345
|
-
notes="",
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
return tv_req
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
# Backward compatibility aliases
|
|
352
|
-
Requirement = TraceViewRequirement
|
|
353
|
-
TraceabilityRequirement = TraceViewRequirement
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Implements: REQ-int-d00002-C (Review server requires flask)
|
|
2
|
-
"""
|
|
3
|
-
elspais.trace_view.review - Collaborative review system.
|
|
4
|
-
|
|
5
|
-
Requires: pip install elspais[trace-review]
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
def _check_flask():
|
|
9
|
-
try:
|
|
10
|
-
import flask # noqa: F401
|
|
11
|
-
return True
|
|
12
|
-
except ImportError:
|
|
13
|
-
return False
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
FLASK_AVAILABLE = _check_flask()
|
|
17
|
-
|
|
18
|
-
# Models are always available (no flask dependency)
|
|
19
|
-
from elspais.trace_view.review.models import (
|
|
20
|
-
Comment,
|
|
21
|
-
CommentPosition,
|
|
22
|
-
Thread,
|
|
23
|
-
ReviewFlag,
|
|
24
|
-
StatusRequest,
|
|
25
|
-
Approval,
|
|
26
|
-
ReviewSession,
|
|
27
|
-
ReviewConfig,
|
|
28
|
-
ReviewPackage,
|
|
29
|
-
PositionType,
|
|
30
|
-
RequestState,
|
|
31
|
-
ApprovalDecision,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
__all__ = [
|
|
35
|
-
"FLASK_AVAILABLE",
|
|
36
|
-
# Models (always available)
|
|
37
|
-
"Comment",
|
|
38
|
-
"CommentPosition",
|
|
39
|
-
"Thread",
|
|
40
|
-
"ReviewFlag",
|
|
41
|
-
"StatusRequest",
|
|
42
|
-
"Approval",
|
|
43
|
-
"ReviewSession",
|
|
44
|
-
"ReviewConfig",
|
|
45
|
-
"ReviewPackage",
|
|
46
|
-
"PositionType",
|
|
47
|
-
"RequestState",
|
|
48
|
-
"ApprovalDecision",
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
if FLASK_AVAILABLE:
|
|
52
|
-
from elspais.trace_view.review.server import create_app
|
|
53
|
-
__all__.append("create_app")
|
|
54
|
-
else:
|
|
55
|
-
def create_app(*args, **kwargs):
|
|
56
|
-
"""Placeholder when flask is not installed."""
|
|
57
|
-
raise ImportError(
|
|
58
|
-
"Review server requires Flask. "
|
|
59
|
-
"Install with: pip install elspais[trace-review]"
|
|
60
|
-
)
|