gitflow-analytics 1.0.3__py3-none-any.whl → 1.3.11__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.
Files changed (116) hide show
  1. gitflow_analytics/_version.py +1 -1
  2. gitflow_analytics/classification/__init__.py +31 -0
  3. gitflow_analytics/classification/batch_classifier.py +752 -0
  4. gitflow_analytics/classification/classifier.py +464 -0
  5. gitflow_analytics/classification/feature_extractor.py +725 -0
  6. gitflow_analytics/classification/linguist_analyzer.py +574 -0
  7. gitflow_analytics/classification/model.py +455 -0
  8. gitflow_analytics/cli.py +4158 -350
  9. gitflow_analytics/cli_rich.py +198 -48
  10. gitflow_analytics/config/__init__.py +43 -0
  11. gitflow_analytics/config/errors.py +261 -0
  12. gitflow_analytics/config/loader.py +905 -0
  13. gitflow_analytics/config/profiles.py +264 -0
  14. gitflow_analytics/config/repository.py +124 -0
  15. gitflow_analytics/config/schema.py +444 -0
  16. gitflow_analytics/config/validator.py +154 -0
  17. gitflow_analytics/config.py +44 -508
  18. gitflow_analytics/core/analyzer.py +1209 -98
  19. gitflow_analytics/core/cache.py +1337 -29
  20. gitflow_analytics/core/data_fetcher.py +1285 -0
  21. gitflow_analytics/core/identity.py +363 -14
  22. gitflow_analytics/core/metrics_storage.py +526 -0
  23. gitflow_analytics/core/progress.py +372 -0
  24. gitflow_analytics/core/schema_version.py +269 -0
  25. gitflow_analytics/extractors/ml_tickets.py +1100 -0
  26. gitflow_analytics/extractors/story_points.py +8 -1
  27. gitflow_analytics/extractors/tickets.py +749 -11
  28. gitflow_analytics/identity_llm/__init__.py +6 -0
  29. gitflow_analytics/identity_llm/analysis_pass.py +231 -0
  30. gitflow_analytics/identity_llm/analyzer.py +464 -0
  31. gitflow_analytics/identity_llm/models.py +76 -0
  32. gitflow_analytics/integrations/github_integration.py +175 -11
  33. gitflow_analytics/integrations/jira_integration.py +461 -24
  34. gitflow_analytics/integrations/orchestrator.py +124 -1
  35. gitflow_analytics/metrics/activity_scoring.py +322 -0
  36. gitflow_analytics/metrics/branch_health.py +470 -0
  37. gitflow_analytics/metrics/dora.py +379 -20
  38. gitflow_analytics/models/database.py +843 -53
  39. gitflow_analytics/pm_framework/__init__.py +115 -0
  40. gitflow_analytics/pm_framework/adapters/__init__.py +50 -0
  41. gitflow_analytics/pm_framework/adapters/jira_adapter.py +1845 -0
  42. gitflow_analytics/pm_framework/base.py +406 -0
  43. gitflow_analytics/pm_framework/models.py +211 -0
  44. gitflow_analytics/pm_framework/orchestrator.py +652 -0
  45. gitflow_analytics/pm_framework/registry.py +333 -0
  46. gitflow_analytics/qualitative/__init__.py +9 -10
  47. gitflow_analytics/qualitative/chatgpt_analyzer.py +259 -0
  48. gitflow_analytics/qualitative/classifiers/__init__.py +3 -3
  49. gitflow_analytics/qualitative/classifiers/change_type.py +518 -244
  50. gitflow_analytics/qualitative/classifiers/domain_classifier.py +272 -165
  51. gitflow_analytics/qualitative/classifiers/intent_analyzer.py +321 -222
  52. gitflow_analytics/qualitative/classifiers/llm/__init__.py +35 -0
  53. gitflow_analytics/qualitative/classifiers/llm/base.py +193 -0
  54. gitflow_analytics/qualitative/classifiers/llm/batch_processor.py +383 -0
  55. gitflow_analytics/qualitative/classifiers/llm/cache.py +479 -0
  56. gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py +435 -0
  57. gitflow_analytics/qualitative/classifiers/llm/openai_client.py +403 -0
  58. gitflow_analytics/qualitative/classifiers/llm/prompts.py +373 -0
  59. gitflow_analytics/qualitative/classifiers/llm/response_parser.py +287 -0
  60. gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py +607 -0
  61. gitflow_analytics/qualitative/classifiers/risk_analyzer.py +215 -189
  62. gitflow_analytics/qualitative/core/__init__.py +4 -4
  63. gitflow_analytics/qualitative/core/llm_fallback.py +239 -235
  64. gitflow_analytics/qualitative/core/nlp_engine.py +157 -148
  65. gitflow_analytics/qualitative/core/pattern_cache.py +214 -192
  66. gitflow_analytics/qualitative/core/processor.py +381 -248
  67. gitflow_analytics/qualitative/enhanced_analyzer.py +2236 -0
  68. gitflow_analytics/qualitative/example_enhanced_usage.py +420 -0
  69. gitflow_analytics/qualitative/models/__init__.py +7 -7
  70. gitflow_analytics/qualitative/models/schemas.py +155 -121
  71. gitflow_analytics/qualitative/utils/__init__.py +4 -4
  72. gitflow_analytics/qualitative/utils/batch_processor.py +136 -123
  73. gitflow_analytics/qualitative/utils/cost_tracker.py +142 -140
  74. gitflow_analytics/qualitative/utils/metrics.py +172 -158
  75. gitflow_analytics/qualitative/utils/text_processing.py +146 -104
  76. gitflow_analytics/reports/__init__.py +100 -0
  77. gitflow_analytics/reports/analytics_writer.py +539 -14
  78. gitflow_analytics/reports/base.py +648 -0
  79. gitflow_analytics/reports/branch_health_writer.py +322 -0
  80. gitflow_analytics/reports/classification_writer.py +924 -0
  81. gitflow_analytics/reports/cli_integration.py +427 -0
  82. gitflow_analytics/reports/csv_writer.py +1676 -212
  83. gitflow_analytics/reports/data_models.py +504 -0
  84. gitflow_analytics/reports/database_report_generator.py +427 -0
  85. gitflow_analytics/reports/example_usage.py +344 -0
  86. gitflow_analytics/reports/factory.py +499 -0
  87. gitflow_analytics/reports/formatters.py +698 -0
  88. gitflow_analytics/reports/html_generator.py +1116 -0
  89. gitflow_analytics/reports/interfaces.py +489 -0
  90. gitflow_analytics/reports/json_exporter.py +2770 -0
  91. gitflow_analytics/reports/narrative_writer.py +2287 -158
  92. gitflow_analytics/reports/story_point_correlation.py +1144 -0
  93. gitflow_analytics/reports/weekly_trends_writer.py +389 -0
  94. gitflow_analytics/training/__init__.py +5 -0
  95. gitflow_analytics/training/model_loader.py +377 -0
  96. gitflow_analytics/training/pipeline.py +550 -0
  97. gitflow_analytics/tui/__init__.py +1 -1
  98. gitflow_analytics/tui/app.py +129 -126
  99. gitflow_analytics/tui/screens/__init__.py +3 -3
  100. gitflow_analytics/tui/screens/analysis_progress_screen.py +188 -179
  101. gitflow_analytics/tui/screens/configuration_screen.py +154 -178
  102. gitflow_analytics/tui/screens/loading_screen.py +100 -110
  103. gitflow_analytics/tui/screens/main_screen.py +89 -72
  104. gitflow_analytics/tui/screens/results_screen.py +305 -281
  105. gitflow_analytics/tui/widgets/__init__.py +2 -2
  106. gitflow_analytics/tui/widgets/data_table.py +67 -69
  107. gitflow_analytics/tui/widgets/export_modal.py +76 -76
  108. gitflow_analytics/tui/widgets/progress_widget.py +41 -46
  109. gitflow_analytics-1.3.11.dist-info/METADATA +1015 -0
  110. gitflow_analytics-1.3.11.dist-info/RECORD +122 -0
  111. gitflow_analytics-1.0.3.dist-info/METADATA +0 -490
  112. gitflow_analytics-1.0.3.dist-info/RECORD +0 -62
  113. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.11.dist-info}/WHEEL +0 -0
  114. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.11.dist-info}/entry_points.txt +0 -0
  115. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.11.dist-info}/licenses/LICENSE +0 -0
  116. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.11.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
+ """