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,427 @@
|
|
|
1
|
+
"""CLI integration for the new report abstraction layer.
|
|
2
|
+
|
|
3
|
+
This module shows how the CLI can be refactored to use the new
|
|
4
|
+
report generation abstraction layer while maintaining backward compatibility.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from .base import ReportData, ReportMetadata
|
|
13
|
+
from .factory import ReportBuilder, ReportFactory
|
|
14
|
+
from .interfaces import ReportFormat, ReportType
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ReportCLIAdapter:
|
|
20
|
+
"""Adapter to integrate the new report abstraction with the existing CLI.
|
|
21
|
+
|
|
22
|
+
This class bridges the gap between the existing CLI code and the new
|
|
23
|
+
report abstraction layer, allowing gradual migration.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, config: Dict[str, Any]):
|
|
27
|
+
"""Initialize the CLI adapter.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
config: Configuration dictionary from CLI
|
|
31
|
+
"""
|
|
32
|
+
self.config = config
|
|
33
|
+
self.factory = ReportFactory()
|
|
34
|
+
|
|
35
|
+
# Set default configuration for all generators
|
|
36
|
+
self.factory.set_default_config({
|
|
37
|
+
"anonymize": config.get("anonymize", False),
|
|
38
|
+
"exclude_authors": config.get("analysis", {}).get("exclude_authors", []),
|
|
39
|
+
"identity_resolver": config.get("identity_resolver")
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
def prepare_report_data(
|
|
43
|
+
self,
|
|
44
|
+
commits: List[Dict[str, Any]],
|
|
45
|
+
prs: List[Dict[str, Any]],
|
|
46
|
+
developer_stats: List[Dict[str, Any]],
|
|
47
|
+
activity_data: List[Dict[str, Any]] = None,
|
|
48
|
+
focus_data: List[Dict[str, Any]] = None,
|
|
49
|
+
insights_data: List[Dict[str, Any]] = None,
|
|
50
|
+
ticket_analysis: Dict[str, Any] = None,
|
|
51
|
+
pr_metrics: Dict[str, Any] = None,
|
|
52
|
+
dora_metrics: Dict[str, Any] = None,
|
|
53
|
+
branch_health_metrics: List[Dict[str, Any]] = None,
|
|
54
|
+
pm_data: Dict[str, Any] = None,
|
|
55
|
+
qualitative_results: List[Dict[str, Any]] = None,
|
|
56
|
+
chatgpt_summary: str = None,
|
|
57
|
+
start_date: datetime = None,
|
|
58
|
+
end_date: datetime = None,
|
|
59
|
+
weeks: int = 12
|
|
60
|
+
) -> ReportData:
|
|
61
|
+
"""Prepare standardized report data from CLI data.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
Various data components from CLI analysis
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Standardized ReportData instance
|
|
68
|
+
"""
|
|
69
|
+
# Calculate metadata
|
|
70
|
+
metadata = ReportMetadata(
|
|
71
|
+
analysis_period_weeks=weeks,
|
|
72
|
+
start_date=start_date,
|
|
73
|
+
end_date=end_date,
|
|
74
|
+
total_commits=len(commits) if commits else 0,
|
|
75
|
+
total_developers=len(developer_stats) if developer_stats else 0,
|
|
76
|
+
source_repositories=self._extract_repositories(commits),
|
|
77
|
+
excluded_authors=self.config.get("analysis", {}).get("exclude_authors", [])
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Create ReportData
|
|
81
|
+
return ReportData(
|
|
82
|
+
commits=commits or [],
|
|
83
|
+
pull_requests=prs or [],
|
|
84
|
+
developer_stats=developer_stats or [],
|
|
85
|
+
activity_data=activity_data or [],
|
|
86
|
+
focus_data=focus_data or [],
|
|
87
|
+
insights_data=insights_data or [],
|
|
88
|
+
ticket_analysis=ticket_analysis or {},
|
|
89
|
+
pr_metrics=pr_metrics or {},
|
|
90
|
+
dora_metrics=dora_metrics or {},
|
|
91
|
+
branch_health_metrics=branch_health_metrics or [],
|
|
92
|
+
pm_data=pm_data,
|
|
93
|
+
story_points_data=self._extract_story_points(commits, prs),
|
|
94
|
+
qualitative_results=qualitative_results or [],
|
|
95
|
+
chatgpt_summary=chatgpt_summary,
|
|
96
|
+
metadata=metadata,
|
|
97
|
+
config=self.config
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def generate_reports(
|
|
101
|
+
self,
|
|
102
|
+
report_data: ReportData,
|
|
103
|
+
output_dir: Path,
|
|
104
|
+
formats: List[str] = None
|
|
105
|
+
) -> Dict[str, Any]:
|
|
106
|
+
"""Generate all configured reports.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
report_data: Standardized report data
|
|
110
|
+
output_dir: Output directory for reports
|
|
111
|
+
formats: List of format strings (csv, markdown, json, etc.)
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Dictionary of report results
|
|
115
|
+
"""
|
|
116
|
+
if formats is None:
|
|
117
|
+
formats = self.config.get("output", {}).get("formats", ["csv", "markdown"])
|
|
118
|
+
|
|
119
|
+
results = {}
|
|
120
|
+
timestamp = datetime.now().strftime("%Y%m%d")
|
|
121
|
+
|
|
122
|
+
# Use ReportBuilder to configure and generate reports
|
|
123
|
+
builder = ReportBuilder(self.factory)
|
|
124
|
+
builder.with_data(report_data)
|
|
125
|
+
builder.with_output_dir(output_dir)
|
|
126
|
+
|
|
127
|
+
# Add reports based on configured formats
|
|
128
|
+
if "csv" in formats:
|
|
129
|
+
self._add_csv_reports(builder)
|
|
130
|
+
|
|
131
|
+
if "markdown" in formats:
|
|
132
|
+
builder.add_report(ReportType.NARRATIVE, ReportFormat.MARKDOWN)
|
|
133
|
+
|
|
134
|
+
if "json" in formats:
|
|
135
|
+
builder.add_report(ReportType.COMPREHENSIVE, ReportFormat.JSON)
|
|
136
|
+
|
|
137
|
+
if "html" in formats:
|
|
138
|
+
builder.add_report(ReportType.COMPREHENSIVE, ReportFormat.HTML)
|
|
139
|
+
|
|
140
|
+
# Generate all reports
|
|
141
|
+
try:
|
|
142
|
+
outputs = builder.generate()
|
|
143
|
+
|
|
144
|
+
# Process outputs
|
|
145
|
+
if isinstance(outputs, list):
|
|
146
|
+
for i, output in enumerate(outputs):
|
|
147
|
+
if output.success:
|
|
148
|
+
report_name = f"report_{i}"
|
|
149
|
+
if output.file_path:
|
|
150
|
+
report_name = output.file_path.stem
|
|
151
|
+
results[report_name] = {
|
|
152
|
+
"success": True,
|
|
153
|
+
"path": str(output.file_path) if output.file_path else None,
|
|
154
|
+
"size": output.size_bytes
|
|
155
|
+
}
|
|
156
|
+
else:
|
|
157
|
+
results[f"report_{i}"] = {
|
|
158
|
+
"success": False,
|
|
159
|
+
"errors": output.errors
|
|
160
|
+
}
|
|
161
|
+
else:
|
|
162
|
+
# Single output
|
|
163
|
+
results["report"] = {
|
|
164
|
+
"success": outputs.success,
|
|
165
|
+
"path": str(outputs.file_path) if outputs.file_path else None,
|
|
166
|
+
"errors": outputs.errors if not outputs.success else []
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error(f"Error generating reports: {e}")
|
|
171
|
+
results["error"] = str(e)
|
|
172
|
+
|
|
173
|
+
return results
|
|
174
|
+
|
|
175
|
+
def generate_legacy_reports(
|
|
176
|
+
self,
|
|
177
|
+
commits: List[Dict[str, Any]],
|
|
178
|
+
prs: List[Dict[str, Any]],
|
|
179
|
+
developer_stats: List[Dict[str, Any]],
|
|
180
|
+
output_dir: Path,
|
|
181
|
+
**kwargs
|
|
182
|
+
) -> Dict[str, Path]:
|
|
183
|
+
"""Generate reports using legacy method for backward compatibility.
|
|
184
|
+
|
|
185
|
+
This method maintains the exact same interface as the existing CLI
|
|
186
|
+
report generation code.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
commits: Commit data
|
|
190
|
+
prs: Pull request data
|
|
191
|
+
developer_stats: Developer statistics
|
|
192
|
+
output_dir: Output directory
|
|
193
|
+
**kwargs: Additional data components
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Dictionary mapping report names to file paths
|
|
197
|
+
"""
|
|
198
|
+
generated_reports = {}
|
|
199
|
+
timestamp = datetime.now().strftime("%Y%m%d")
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
# Create legacy generators with backward-compatible initialization
|
|
203
|
+
from .analytics_writer import AnalyticsReportGenerator
|
|
204
|
+
from .csv_writer import CSVReportGenerator
|
|
205
|
+
from .narrative_writer import NarrativeReportGenerator
|
|
206
|
+
|
|
207
|
+
# CSV Reports
|
|
208
|
+
csv_gen = CSVReportGenerator(
|
|
209
|
+
anonymize=self.config.get("anonymize", False),
|
|
210
|
+
exclude_authors=self.config.get("analysis", {}).get("exclude_authors", []),
|
|
211
|
+
identity_resolver=kwargs.get("identity_resolver")
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Weekly report
|
|
215
|
+
weekly_path = output_dir / f"weekly_metrics_{timestamp}.csv"
|
|
216
|
+
csv_gen.generate_weekly_report(commits, developer_stats, weekly_path)
|
|
217
|
+
generated_reports["weekly_metrics"] = weekly_path
|
|
218
|
+
|
|
219
|
+
# Developer report
|
|
220
|
+
dev_path = output_dir / f"developer_stats_{timestamp}.csv"
|
|
221
|
+
csv_gen.generate_developer_report(developer_stats, dev_path)
|
|
222
|
+
generated_reports["developer_stats"] = dev_path
|
|
223
|
+
|
|
224
|
+
# Analytics Reports
|
|
225
|
+
analytics_gen = AnalyticsReportGenerator(
|
|
226
|
+
anonymize=self.config.get("anonymize", False),
|
|
227
|
+
exclude_authors=self.config.get("analysis", {}).get("exclude_authors", []),
|
|
228
|
+
identity_resolver=kwargs.get("identity_resolver")
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Activity distribution
|
|
232
|
+
if kwargs.get("activity_data"):
|
|
233
|
+
activity_path = output_dir / f"activity_distribution_{timestamp}.csv"
|
|
234
|
+
analytics_gen.generate_activity_distribution_report(
|
|
235
|
+
commits, developer_stats, activity_path
|
|
236
|
+
)
|
|
237
|
+
generated_reports["activity_distribution"] = activity_path
|
|
238
|
+
|
|
239
|
+
# Developer focus
|
|
240
|
+
if kwargs.get("focus_data"):
|
|
241
|
+
focus_path = output_dir / f"developer_focus_{timestamp}.csv"
|
|
242
|
+
analytics_gen.generate_developer_focus_report(
|
|
243
|
+
commits, developer_stats, focus_path,
|
|
244
|
+
kwargs.get("weeks", 12)
|
|
245
|
+
)
|
|
246
|
+
generated_reports["developer_focus"] = focus_path
|
|
247
|
+
|
|
248
|
+
# Narrative Report
|
|
249
|
+
if "markdown" in self.config.get("output", {}).get("formats", []):
|
|
250
|
+
narrative_gen = NarrativeReportGenerator()
|
|
251
|
+
narrative_path = output_dir / f"narrative_report_{timestamp}.md"
|
|
252
|
+
|
|
253
|
+
narrative_gen.generate_narrative_report(
|
|
254
|
+
commits,
|
|
255
|
+
prs,
|
|
256
|
+
developer_stats,
|
|
257
|
+
kwargs.get("activity_data", []),
|
|
258
|
+
kwargs.get("focus_data", []),
|
|
259
|
+
kwargs.get("insights_data", []),
|
|
260
|
+
kwargs.get("ticket_analysis", {}),
|
|
261
|
+
kwargs.get("pr_metrics", {}),
|
|
262
|
+
narrative_path,
|
|
263
|
+
kwargs.get("weeks", 12),
|
|
264
|
+
kwargs.get("pm_data"),
|
|
265
|
+
kwargs.get("chatgpt_summary"),
|
|
266
|
+
kwargs.get("branch_health_metrics"),
|
|
267
|
+
self.config.get("analysis", {}).get("exclude_authors", [])
|
|
268
|
+
)
|
|
269
|
+
generated_reports["narrative"] = narrative_path
|
|
270
|
+
|
|
271
|
+
# JSON Export
|
|
272
|
+
if "json" in self.config.get("output", {}).get("formats", []):
|
|
273
|
+
from .json_exporter import ComprehensiveJSONExporter
|
|
274
|
+
|
|
275
|
+
json_exporter = ComprehensiveJSONExporter(
|
|
276
|
+
anonymize=self.config.get("anonymize", False)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
json_path = output_dir / f"comprehensive_export_{timestamp}.json"
|
|
280
|
+
json_exporter.export_comprehensive_data(
|
|
281
|
+
commits,
|
|
282
|
+
prs,
|
|
283
|
+
developer_stats,
|
|
284
|
+
kwargs.get("project_metrics", {}),
|
|
285
|
+
kwargs.get("dora_metrics", {}),
|
|
286
|
+
json_path,
|
|
287
|
+
kwargs.get("weeks", 12),
|
|
288
|
+
kwargs.get("pm_data"),
|
|
289
|
+
kwargs.get("qualitative_data")
|
|
290
|
+
)
|
|
291
|
+
generated_reports["json_export"] = json_path
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
logger.error(f"Error generating legacy reports: {e}")
|
|
295
|
+
raise
|
|
296
|
+
|
|
297
|
+
return generated_reports
|
|
298
|
+
|
|
299
|
+
def _add_csv_reports(self, builder: ReportBuilder) -> None:
|
|
300
|
+
"""Add CSV report types to builder.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
builder: Report builder instance
|
|
304
|
+
"""
|
|
305
|
+
csv_reports = [
|
|
306
|
+
ReportType.WEEKLY_METRICS,
|
|
307
|
+
ReportType.DEVELOPER_STATS,
|
|
308
|
+
ReportType.ACTIVITY_DISTRIBUTION,
|
|
309
|
+
ReportType.DEVELOPER_FOCUS,
|
|
310
|
+
ReportType.QUALITATIVE_INSIGHTS
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
for report_type in csv_reports:
|
|
314
|
+
builder.add_report(report_type, ReportFormat.CSV)
|
|
315
|
+
|
|
316
|
+
def _extract_repositories(self, commits: List[Dict[str, Any]]) -> List[str]:
|
|
317
|
+
"""Extract unique repository names from commits.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
commits: List of commit data
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of unique repository names
|
|
324
|
+
"""
|
|
325
|
+
repos = set()
|
|
326
|
+
for commit in commits or []:
|
|
327
|
+
if "repository" in commit:
|
|
328
|
+
repos.add(commit["repository"])
|
|
329
|
+
elif "project_key" in commit:
|
|
330
|
+
repos.add(commit["project_key"])
|
|
331
|
+
return list(repos)
|
|
332
|
+
|
|
333
|
+
def _extract_story_points(
|
|
334
|
+
self,
|
|
335
|
+
commits: List[Dict[str, Any]],
|
|
336
|
+
prs: List[Dict[str, Any]]
|
|
337
|
+
) -> Dict[str, Any]:
|
|
338
|
+
"""Extract story points data from commits and PRs.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
commits: Commit data
|
|
342
|
+
prs: Pull request data
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Story points summary
|
|
346
|
+
"""
|
|
347
|
+
total_points = 0.0
|
|
348
|
+
commits_with_points = 0
|
|
349
|
+
|
|
350
|
+
for commit in commits or []:
|
|
351
|
+
points = commit.get("story_points", 0) or 0
|
|
352
|
+
if points > 0:
|
|
353
|
+
total_points += points
|
|
354
|
+
commits_with_points += 1
|
|
355
|
+
|
|
356
|
+
for pr in prs or []:
|
|
357
|
+
points = pr.get("story_points", 0) or 0
|
|
358
|
+
if points > 0:
|
|
359
|
+
total_points += points
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
"total_story_points": total_points,
|
|
363
|
+
"commits_with_points": commits_with_points,
|
|
364
|
+
"coverage_percentage": (commits_with_points / len(commits) * 100) if commits else 0
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def integrate_with_cli(cli_config: Dict[str, Any]) -> ReportCLIAdapter:
|
|
369
|
+
"""Create a CLI adapter for report generation.
|
|
370
|
+
|
|
371
|
+
This function can be called from the existing CLI to use the new
|
|
372
|
+
report abstraction layer.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
cli_config: Configuration from CLI
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Configured ReportCLIAdapter instance
|
|
379
|
+
"""
|
|
380
|
+
return ReportCLIAdapter(cli_config)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# Example usage in CLI:
|
|
384
|
+
"""
|
|
385
|
+
# In cli.py, replace direct report generation with:
|
|
386
|
+
|
|
387
|
+
from .reports.cli_integration import integrate_with_cli
|
|
388
|
+
|
|
389
|
+
# ... existing CLI code ...
|
|
390
|
+
|
|
391
|
+
# Create adapter
|
|
392
|
+
report_adapter = integrate_with_cli(cfg)
|
|
393
|
+
|
|
394
|
+
# Prepare standardized data
|
|
395
|
+
report_data = report_adapter.prepare_report_data(
|
|
396
|
+
commits=all_commits,
|
|
397
|
+
prs=all_prs,
|
|
398
|
+
developer_stats=developer_stats,
|
|
399
|
+
activity_data=activity_data,
|
|
400
|
+
focus_data=focus_data,
|
|
401
|
+
insights_data=insights_data,
|
|
402
|
+
ticket_analysis=ticket_analysis,
|
|
403
|
+
pr_metrics=pr_metrics,
|
|
404
|
+
dora_metrics=dora_metrics,
|
|
405
|
+
branch_health_metrics=branch_health_metrics,
|
|
406
|
+
pm_data=aggregated_pm_data,
|
|
407
|
+
qualitative_results=qualitative_results,
|
|
408
|
+
chatgpt_summary=chatgpt_summary,
|
|
409
|
+
start_date=start_date,
|
|
410
|
+
end_date=end_date,
|
|
411
|
+
weeks=weeks
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Generate reports using new abstraction
|
|
415
|
+
results = report_adapter.generate_reports(report_data, output_dir)
|
|
416
|
+
|
|
417
|
+
# Or use legacy method for gradual migration
|
|
418
|
+
legacy_reports = report_adapter.generate_legacy_reports(
|
|
419
|
+
all_commits,
|
|
420
|
+
all_prs,
|
|
421
|
+
developer_stats,
|
|
422
|
+
output_dir,
|
|
423
|
+
activity_data=activity_data,
|
|
424
|
+
focus_data=focus_data,
|
|
425
|
+
# ... other kwargs
|
|
426
|
+
)
|
|
427
|
+
"""
|