gitflow-analytics 1.0.1__py3-none-any.whl → 1.3.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.
- gitflow_analytics/__init__.py +11 -11
- gitflow_analytics/_version.py +2 -2
- gitflow_analytics/classification/__init__.py +31 -0
- gitflow_analytics/classification/batch_classifier.py +752 -0
- gitflow_analytics/classification/classifier.py +464 -0
- gitflow_analytics/classification/feature_extractor.py +725 -0
- gitflow_analytics/classification/linguist_analyzer.py +574 -0
- gitflow_analytics/classification/model.py +455 -0
- gitflow_analytics/cli.py +4490 -378
- gitflow_analytics/cli_rich.py +503 -0
- gitflow_analytics/config/__init__.py +43 -0
- gitflow_analytics/config/errors.py +261 -0
- gitflow_analytics/config/loader.py +904 -0
- gitflow_analytics/config/profiles.py +264 -0
- gitflow_analytics/config/repository.py +124 -0
- gitflow_analytics/config/schema.py +441 -0
- gitflow_analytics/config/validator.py +154 -0
- gitflow_analytics/config.py +44 -398
- gitflow_analytics/core/analyzer.py +1320 -172
- gitflow_analytics/core/branch_mapper.py +132 -132
- gitflow_analytics/core/cache.py +1554 -175
- gitflow_analytics/core/data_fetcher.py +1193 -0
- gitflow_analytics/core/identity.py +571 -185
- gitflow_analytics/core/metrics_storage.py +526 -0
- gitflow_analytics/core/progress.py +372 -0
- gitflow_analytics/core/schema_version.py +269 -0
- gitflow_analytics/extractors/base.py +13 -11
- gitflow_analytics/extractors/ml_tickets.py +1100 -0
- gitflow_analytics/extractors/story_points.py +77 -59
- gitflow_analytics/extractors/tickets.py +841 -89
- gitflow_analytics/identity_llm/__init__.py +6 -0
- gitflow_analytics/identity_llm/analysis_pass.py +231 -0
- gitflow_analytics/identity_llm/analyzer.py +464 -0
- gitflow_analytics/identity_llm/models.py +76 -0
- gitflow_analytics/integrations/github_integration.py +258 -87
- gitflow_analytics/integrations/jira_integration.py +572 -123
- gitflow_analytics/integrations/orchestrator.py +206 -82
- gitflow_analytics/metrics/activity_scoring.py +322 -0
- gitflow_analytics/metrics/branch_health.py +470 -0
- gitflow_analytics/metrics/dora.py +542 -179
- gitflow_analytics/models/database.py +986 -59
- gitflow_analytics/pm_framework/__init__.py +115 -0
- gitflow_analytics/pm_framework/adapters/__init__.py +50 -0
- gitflow_analytics/pm_framework/adapters/jira_adapter.py +1845 -0
- gitflow_analytics/pm_framework/base.py +406 -0
- gitflow_analytics/pm_framework/models.py +211 -0
- gitflow_analytics/pm_framework/orchestrator.py +652 -0
- gitflow_analytics/pm_framework/registry.py +333 -0
- gitflow_analytics/qualitative/__init__.py +29 -0
- gitflow_analytics/qualitative/chatgpt_analyzer.py +259 -0
- gitflow_analytics/qualitative/classifiers/__init__.py +13 -0
- gitflow_analytics/qualitative/classifiers/change_type.py +742 -0
- gitflow_analytics/qualitative/classifiers/domain_classifier.py +506 -0
- gitflow_analytics/qualitative/classifiers/intent_analyzer.py +535 -0
- gitflow_analytics/qualitative/classifiers/llm/__init__.py +35 -0
- gitflow_analytics/qualitative/classifiers/llm/base.py +193 -0
- gitflow_analytics/qualitative/classifiers/llm/batch_processor.py +383 -0
- gitflow_analytics/qualitative/classifiers/llm/cache.py +479 -0
- gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py +435 -0
- gitflow_analytics/qualitative/classifiers/llm/openai_client.py +403 -0
- gitflow_analytics/qualitative/classifiers/llm/prompts.py +373 -0
- gitflow_analytics/qualitative/classifiers/llm/response_parser.py +287 -0
- gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py +607 -0
- gitflow_analytics/qualitative/classifiers/risk_analyzer.py +438 -0
- gitflow_analytics/qualitative/core/__init__.py +13 -0
- gitflow_analytics/qualitative/core/llm_fallback.py +657 -0
- gitflow_analytics/qualitative/core/nlp_engine.py +382 -0
- gitflow_analytics/qualitative/core/pattern_cache.py +479 -0
- gitflow_analytics/qualitative/core/processor.py +673 -0
- gitflow_analytics/qualitative/enhanced_analyzer.py +2236 -0
- gitflow_analytics/qualitative/example_enhanced_usage.py +420 -0
- gitflow_analytics/qualitative/models/__init__.py +25 -0
- gitflow_analytics/qualitative/models/schemas.py +306 -0
- gitflow_analytics/qualitative/utils/__init__.py +13 -0
- gitflow_analytics/qualitative/utils/batch_processor.py +339 -0
- gitflow_analytics/qualitative/utils/cost_tracker.py +345 -0
- gitflow_analytics/qualitative/utils/metrics.py +361 -0
- gitflow_analytics/qualitative/utils/text_processing.py +285 -0
- gitflow_analytics/reports/__init__.py +100 -0
- gitflow_analytics/reports/analytics_writer.py +550 -18
- gitflow_analytics/reports/base.py +648 -0
- gitflow_analytics/reports/branch_health_writer.py +322 -0
- gitflow_analytics/reports/classification_writer.py +924 -0
- gitflow_analytics/reports/cli_integration.py +427 -0
- gitflow_analytics/reports/csv_writer.py +1700 -216
- gitflow_analytics/reports/data_models.py +504 -0
- gitflow_analytics/reports/database_report_generator.py +427 -0
- gitflow_analytics/reports/example_usage.py +344 -0
- gitflow_analytics/reports/factory.py +499 -0
- gitflow_analytics/reports/formatters.py +698 -0
- gitflow_analytics/reports/html_generator.py +1116 -0
- gitflow_analytics/reports/interfaces.py +489 -0
- gitflow_analytics/reports/json_exporter.py +2770 -0
- gitflow_analytics/reports/narrative_writer.py +2289 -158
- gitflow_analytics/reports/story_point_correlation.py +1144 -0
- gitflow_analytics/reports/weekly_trends_writer.py +389 -0
- gitflow_analytics/training/__init__.py +5 -0
- gitflow_analytics/training/model_loader.py +377 -0
- gitflow_analytics/training/pipeline.py +550 -0
- gitflow_analytics/tui/__init__.py +5 -0
- gitflow_analytics/tui/app.py +724 -0
- gitflow_analytics/tui/screens/__init__.py +8 -0
- gitflow_analytics/tui/screens/analysis_progress_screen.py +496 -0
- gitflow_analytics/tui/screens/configuration_screen.py +523 -0
- gitflow_analytics/tui/screens/loading_screen.py +348 -0
- gitflow_analytics/tui/screens/main_screen.py +321 -0
- gitflow_analytics/tui/screens/results_screen.py +722 -0
- gitflow_analytics/tui/widgets/__init__.py +7 -0
- gitflow_analytics/tui/widgets/data_table.py +255 -0
- gitflow_analytics/tui/widgets/export_modal.py +301 -0
- gitflow_analytics/tui/widgets/progress_widget.py +187 -0
- gitflow_analytics-1.3.6.dist-info/METADATA +1015 -0
- gitflow_analytics-1.3.6.dist-info/RECORD +122 -0
- gitflow_analytics-1.0.1.dist-info/METADATA +0 -463
- gitflow_analytics-1.0.1.dist-info/RECORD +0 -31
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.3.6.dist-info}/WHEEL +0 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.3.6.dist-info}/entry_points.txt +0 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.3.6.dist-info}/licenses/LICENSE +0 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.3.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"""Report interfaces and data contracts for GitFlow Analytics.
|
|
2
|
+
|
|
3
|
+
This module defines the interfaces and contracts that all report generators
|
|
4
|
+
must adhere to, ensuring consistency and interoperability.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any, Callable, Dict, List, Optional, Protocol, Union
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ReportFormat(Enum):
|
|
14
|
+
"""Supported report format types."""
|
|
15
|
+
|
|
16
|
+
CSV = "csv"
|
|
17
|
+
MARKDOWN = "markdown"
|
|
18
|
+
JSON = "json"
|
|
19
|
+
HTML = "html"
|
|
20
|
+
PDF = "pdf"
|
|
21
|
+
XML = "xml"
|
|
22
|
+
YAML = "yaml"
|
|
23
|
+
EXCEL = "excel"
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_string(cls, value: str) -> "ReportFormat":
|
|
27
|
+
"""Create format from string value.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
value: String representation of format
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
ReportFormat enum value
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ValueError: If format is not recognized
|
|
37
|
+
"""
|
|
38
|
+
value_upper = value.upper()
|
|
39
|
+
if value_upper in cls.__members__:
|
|
40
|
+
return cls[value_upper]
|
|
41
|
+
|
|
42
|
+
# Try by value
|
|
43
|
+
for member in cls:
|
|
44
|
+
if member.value == value.lower():
|
|
45
|
+
return member
|
|
46
|
+
|
|
47
|
+
raise ValueError(f"Unknown report format: {value}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ReportType(Enum):
|
|
51
|
+
"""Types of reports that can be generated."""
|
|
52
|
+
|
|
53
|
+
WEEKLY_METRICS = "weekly_metrics"
|
|
54
|
+
DEVELOPER_STATS = "developer_stats"
|
|
55
|
+
ACTIVITY_DISTRIBUTION = "activity_distribution"
|
|
56
|
+
DEVELOPER_FOCUS = "developer_focus"
|
|
57
|
+
QUALITATIVE_INSIGHTS = "qualitative_insights"
|
|
58
|
+
NARRATIVE = "narrative"
|
|
59
|
+
DORA_METRICS = "dora_metrics"
|
|
60
|
+
BRANCH_HEALTH = "branch_health"
|
|
61
|
+
STORY_POINTS = "story_points"
|
|
62
|
+
UNTRACKED_COMMITS = "untracked_commits"
|
|
63
|
+
WEEKLY_TRENDS = "weekly_trends"
|
|
64
|
+
PR_ANALYSIS = "pr_analysis"
|
|
65
|
+
COMPREHENSIVE = "comprehensive"
|
|
66
|
+
CUSTOM = "custom"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ReportField:
|
|
71
|
+
"""Definition of a report field."""
|
|
72
|
+
|
|
73
|
+
name: str
|
|
74
|
+
field_type: type
|
|
75
|
+
required: bool = False
|
|
76
|
+
default: Any = None
|
|
77
|
+
description: str = ""
|
|
78
|
+
validator: Optional[Callable[[Any], bool]] = None
|
|
79
|
+
transformer: Optional[Callable[[Any], Any]] = None
|
|
80
|
+
|
|
81
|
+
def validate(self, value: Any) -> bool:
|
|
82
|
+
"""Validate a field value.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
value: Value to validate
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
True if valid, False otherwise
|
|
89
|
+
"""
|
|
90
|
+
if value is None:
|
|
91
|
+
return not self.required
|
|
92
|
+
|
|
93
|
+
if not isinstance(value, self.field_type):
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
if self.validator:
|
|
97
|
+
return self.validator(value)
|
|
98
|
+
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def transform(self, value: Any) -> Any:
|
|
102
|
+
"""Transform a field value.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
value: Value to transform
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Transformed value
|
|
109
|
+
"""
|
|
110
|
+
if self.transformer:
|
|
111
|
+
return self.transformer(value)
|
|
112
|
+
return value
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class ReportSchema:
|
|
117
|
+
"""Schema definition for a report."""
|
|
118
|
+
|
|
119
|
+
name: str
|
|
120
|
+
version: str
|
|
121
|
+
fields: List[ReportField]
|
|
122
|
+
description: str = ""
|
|
123
|
+
|
|
124
|
+
def validate(self, data: Dict[str, Any]) -> bool:
|
|
125
|
+
"""Validate data against the schema.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
data: Data to validate
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if valid, False otherwise
|
|
132
|
+
"""
|
|
133
|
+
for field in self.fields:
|
|
134
|
+
if field.required and field.name not in data:
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
if field.name in data:
|
|
138
|
+
if not field.validate(data[field.name]):
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
def transform(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
144
|
+
"""Transform data according to the schema.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
data: Data to transform
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Transformed data
|
|
151
|
+
"""
|
|
152
|
+
result = {}
|
|
153
|
+
|
|
154
|
+
for field in self.fields:
|
|
155
|
+
if field.name in data:
|
|
156
|
+
result[field.name] = field.transform(data[field.name])
|
|
157
|
+
elif field.default is not None:
|
|
158
|
+
result[field.name] = field.default
|
|
159
|
+
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ReportGenerator(Protocol):
|
|
164
|
+
"""Protocol defining the interface for report generators."""
|
|
165
|
+
|
|
166
|
+
def generate(self, data: Any, output_path: Optional[Any] = None) -> Any:
|
|
167
|
+
"""Generate a report from the provided data."""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
def validate_data(self, data: Any) -> bool:
|
|
171
|
+
"""Validate input data."""
|
|
172
|
+
...
|
|
173
|
+
|
|
174
|
+
def get_required_fields(self) -> List[str]:
|
|
175
|
+
"""Get list of required fields."""
|
|
176
|
+
...
|
|
177
|
+
|
|
178
|
+
def get_format_type(self) -> str:
|
|
179
|
+
"""Get the format type produced by this generator."""
|
|
180
|
+
...
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class ReportProcessor(Protocol):
|
|
184
|
+
"""Protocol for report processors that transform data."""
|
|
185
|
+
|
|
186
|
+
def process(self, data: Any) -> Any:
|
|
187
|
+
"""Process data for report generation."""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class ReportFormatter(Protocol):
|
|
192
|
+
"""Protocol for report formatters."""
|
|
193
|
+
|
|
194
|
+
def format(self, data: Any) -> str:
|
|
195
|
+
"""Format data for output."""
|
|
196
|
+
...
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ReportWriter(Protocol):
|
|
200
|
+
"""Protocol for report writers."""
|
|
201
|
+
|
|
202
|
+
def write(self, content: Union[str, bytes], path: Any) -> None:
|
|
203
|
+
"""Write report content to storage."""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class IReportFactory(ABC):
|
|
208
|
+
"""Interface for report generator factories."""
|
|
209
|
+
|
|
210
|
+
@abstractmethod
|
|
211
|
+
def create_generator(
|
|
212
|
+
self,
|
|
213
|
+
report_type: ReportType,
|
|
214
|
+
format_type: ReportFormat,
|
|
215
|
+
**kwargs
|
|
216
|
+
) -> ReportGenerator:
|
|
217
|
+
"""Create a report generator.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
report_type: Type of report to generate
|
|
221
|
+
format_type: Format for the report
|
|
222
|
+
**kwargs: Additional configuration
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Report generator instance
|
|
226
|
+
"""
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
@abstractmethod
|
|
230
|
+
def register_generator(
|
|
231
|
+
self,
|
|
232
|
+
report_type: ReportType,
|
|
233
|
+
format_type: ReportFormat,
|
|
234
|
+
generator_class: type
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Register a report generator class.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
report_type: Type of report
|
|
240
|
+
format_type: Format type
|
|
241
|
+
generator_class: Generator class to register
|
|
242
|
+
"""
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
@abstractmethod
|
|
246
|
+
def get_supported_formats(self, report_type: ReportType) -> List[ReportFormat]:
|
|
247
|
+
"""Get supported formats for a report type.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
report_type: Type of report
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
List of supported formats
|
|
254
|
+
"""
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
@abstractmethod
|
|
258
|
+
def get_supported_reports(self) -> List[ReportType]:
|
|
259
|
+
"""Get list of supported report types.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
List of supported report types
|
|
263
|
+
"""
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class IReportTemplate(ABC):
|
|
268
|
+
"""Interface for report templates."""
|
|
269
|
+
|
|
270
|
+
@abstractmethod
|
|
271
|
+
def render(self, context: Dict[str, Any]) -> str:
|
|
272
|
+
"""Render the template with the given context.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
context: Template context data
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Rendered template string
|
|
279
|
+
"""
|
|
280
|
+
pass
|
|
281
|
+
|
|
282
|
+
@abstractmethod
|
|
283
|
+
def get_required_context(self) -> List[str]:
|
|
284
|
+
"""Get list of required context variables.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
List of required variable names
|
|
288
|
+
"""
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
@abstractmethod
|
|
292
|
+
def validate_context(self, context: Dict[str, Any]) -> bool:
|
|
293
|
+
"""Validate template context.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
context: Context to validate
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
True if valid, False otherwise
|
|
300
|
+
"""
|
|
301
|
+
pass
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class IReportAggregator(ABC):
|
|
305
|
+
"""Interface for report aggregators that combine multiple reports."""
|
|
306
|
+
|
|
307
|
+
@abstractmethod
|
|
308
|
+
def add_report(self, report_id: str, report_data: Any) -> None:
|
|
309
|
+
"""Add a report to the aggregation.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
report_id: Unique identifier for the report
|
|
313
|
+
report_data: Report data to add
|
|
314
|
+
"""
|
|
315
|
+
pass
|
|
316
|
+
|
|
317
|
+
@abstractmethod
|
|
318
|
+
def aggregate(self) -> Any:
|
|
319
|
+
"""Aggregate all added reports.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
Aggregated report data
|
|
323
|
+
"""
|
|
324
|
+
pass
|
|
325
|
+
|
|
326
|
+
@abstractmethod
|
|
327
|
+
def clear(self) -> None:
|
|
328
|
+
"""Clear all aggregated reports."""
|
|
329
|
+
pass
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class IReportCache(ABC):
|
|
333
|
+
"""Interface for report caching."""
|
|
334
|
+
|
|
335
|
+
@abstractmethod
|
|
336
|
+
def get(self, key: str) -> Optional[Any]:
|
|
337
|
+
"""Get cached report data.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
key: Cache key
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Cached data if exists, None otherwise
|
|
344
|
+
"""
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
@abstractmethod
|
|
348
|
+
def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
|
|
349
|
+
"""Cache report data.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
key: Cache key
|
|
353
|
+
value: Data to cache
|
|
354
|
+
ttl: Time to live in seconds
|
|
355
|
+
"""
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
@abstractmethod
|
|
359
|
+
def invalidate(self, key: str) -> None:
|
|
360
|
+
"""Invalidate cached data.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
key: Cache key to invalidate
|
|
364
|
+
"""
|
|
365
|
+
pass
|
|
366
|
+
|
|
367
|
+
@abstractmethod
|
|
368
|
+
def clear(self) -> None:
|
|
369
|
+
"""Clear all cached data."""
|
|
370
|
+
pass
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class IReportValidator(ABC):
|
|
374
|
+
"""Interface for report validators."""
|
|
375
|
+
|
|
376
|
+
@abstractmethod
|
|
377
|
+
def validate(self, report_data: Any, schema: Optional[ReportSchema] = None) -> bool:
|
|
378
|
+
"""Validate report data.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
report_data: Data to validate
|
|
382
|
+
schema: Optional schema to validate against
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
True if valid, False otherwise
|
|
386
|
+
"""
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
@abstractmethod
|
|
390
|
+
def get_errors(self) -> List[str]:
|
|
391
|
+
"""Get validation errors.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
List of error messages
|
|
395
|
+
"""
|
|
396
|
+
pass
|
|
397
|
+
|
|
398
|
+
@abstractmethod
|
|
399
|
+
def get_warnings(self) -> List[str]:
|
|
400
|
+
"""Get validation warnings.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
List of warning messages
|
|
404
|
+
"""
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class IReportExporter(ABC):
|
|
409
|
+
"""Interface for report exporters."""
|
|
410
|
+
|
|
411
|
+
@abstractmethod
|
|
412
|
+
def export(
|
|
413
|
+
self,
|
|
414
|
+
report_data: Any,
|
|
415
|
+
format_type: ReportFormat,
|
|
416
|
+
output_path: Optional[Any] = None
|
|
417
|
+
) -> Any:
|
|
418
|
+
"""Export report data to specified format.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
report_data: Data to export
|
|
422
|
+
format_type: Target format
|
|
423
|
+
output_path: Optional output path
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Exported data or path
|
|
427
|
+
"""
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
@abstractmethod
|
|
431
|
+
def supports_format(self, format_type: ReportFormat) -> bool:
|
|
432
|
+
"""Check if format is supported.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
format_type: Format to check
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
True if supported, False otherwise
|
|
439
|
+
"""
|
|
440
|
+
pass
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class IReportTransformer(ABC):
|
|
444
|
+
"""Interface for report data transformers."""
|
|
445
|
+
|
|
446
|
+
@abstractmethod
|
|
447
|
+
def transform(self, data: Any, target_schema: ReportSchema) -> Any:
|
|
448
|
+
"""Transform data to match target schema.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
data: Input data
|
|
452
|
+
target_schema: Target schema
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Transformed data
|
|
456
|
+
"""
|
|
457
|
+
pass
|
|
458
|
+
|
|
459
|
+
@abstractmethod
|
|
460
|
+
def can_transform(self, source_type: type, target_schema: ReportSchema) -> bool:
|
|
461
|
+
"""Check if transformation is possible.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
source_type: Type of source data
|
|
465
|
+
target_schema: Target schema
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
True if transformation is possible
|
|
469
|
+
"""
|
|
470
|
+
pass
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# Report configuration protocol
|
|
474
|
+
class ReportConfig(Protocol):
|
|
475
|
+
"""Protocol for report configuration."""
|
|
476
|
+
|
|
477
|
+
format: ReportFormat
|
|
478
|
+
output_path: Optional[str]
|
|
479
|
+
include_metadata: bool
|
|
480
|
+
anonymize: bool
|
|
481
|
+
exclude_authors: List[str]
|
|
482
|
+
|
|
483
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
484
|
+
"""Convert configuration to dictionary."""
|
|
485
|
+
...
|
|
486
|
+
|
|
487
|
+
def validate(self) -> bool:
|
|
488
|
+
"""Validate configuration."""
|
|
489
|
+
...
|