gitflow-analytics 1.0.1__py3-none-any.whl → 1.0.3__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 (57) hide show
  1. gitflow_analytics/__init__.py +11 -11
  2. gitflow_analytics/_version.py +2 -2
  3. gitflow_analytics/cli.py +612 -258
  4. gitflow_analytics/cli_rich.py +353 -0
  5. gitflow_analytics/config.py +251 -141
  6. gitflow_analytics/core/analyzer.py +140 -103
  7. gitflow_analytics/core/branch_mapper.py +132 -132
  8. gitflow_analytics/core/cache.py +240 -169
  9. gitflow_analytics/core/identity.py +210 -173
  10. gitflow_analytics/extractors/base.py +13 -11
  11. gitflow_analytics/extractors/story_points.py +70 -59
  12. gitflow_analytics/extractors/tickets.py +101 -87
  13. gitflow_analytics/integrations/github_integration.py +84 -77
  14. gitflow_analytics/integrations/jira_integration.py +116 -104
  15. gitflow_analytics/integrations/orchestrator.py +86 -85
  16. gitflow_analytics/metrics/dora.py +181 -177
  17. gitflow_analytics/models/database.py +190 -53
  18. gitflow_analytics/qualitative/__init__.py +30 -0
  19. gitflow_analytics/qualitative/classifiers/__init__.py +13 -0
  20. gitflow_analytics/qualitative/classifiers/change_type.py +468 -0
  21. gitflow_analytics/qualitative/classifiers/domain_classifier.py +399 -0
  22. gitflow_analytics/qualitative/classifiers/intent_analyzer.py +436 -0
  23. gitflow_analytics/qualitative/classifiers/risk_analyzer.py +412 -0
  24. gitflow_analytics/qualitative/core/__init__.py +13 -0
  25. gitflow_analytics/qualitative/core/llm_fallback.py +653 -0
  26. gitflow_analytics/qualitative/core/nlp_engine.py +373 -0
  27. gitflow_analytics/qualitative/core/pattern_cache.py +457 -0
  28. gitflow_analytics/qualitative/core/processor.py +540 -0
  29. gitflow_analytics/qualitative/models/__init__.py +25 -0
  30. gitflow_analytics/qualitative/models/schemas.py +272 -0
  31. gitflow_analytics/qualitative/utils/__init__.py +13 -0
  32. gitflow_analytics/qualitative/utils/batch_processor.py +326 -0
  33. gitflow_analytics/qualitative/utils/cost_tracker.py +343 -0
  34. gitflow_analytics/qualitative/utils/metrics.py +347 -0
  35. gitflow_analytics/qualitative/utils/text_processing.py +243 -0
  36. gitflow_analytics/reports/analytics_writer.py +11 -4
  37. gitflow_analytics/reports/csv_writer.py +51 -31
  38. gitflow_analytics/reports/narrative_writer.py +16 -14
  39. gitflow_analytics/tui/__init__.py +5 -0
  40. gitflow_analytics/tui/app.py +721 -0
  41. gitflow_analytics/tui/screens/__init__.py +8 -0
  42. gitflow_analytics/tui/screens/analysis_progress_screen.py +487 -0
  43. gitflow_analytics/tui/screens/configuration_screen.py +547 -0
  44. gitflow_analytics/tui/screens/loading_screen.py +358 -0
  45. gitflow_analytics/tui/screens/main_screen.py +304 -0
  46. gitflow_analytics/tui/screens/results_screen.py +698 -0
  47. gitflow_analytics/tui/widgets/__init__.py +7 -0
  48. gitflow_analytics/tui/widgets/data_table.py +257 -0
  49. gitflow_analytics/tui/widgets/export_modal.py +301 -0
  50. gitflow_analytics/tui/widgets/progress_widget.py +192 -0
  51. {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/METADATA +31 -4
  52. gitflow_analytics-1.0.3.dist-info/RECORD +62 -0
  53. gitflow_analytics-1.0.1.dist-info/RECORD +0 -31
  54. {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/WHEEL +0 -0
  55. {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/entry_points.txt +0 -0
  56. {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/licenses/LICENSE +0 -0
  57. {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  """Integration orchestrator for multiple platforms."""
2
+
2
3
  import json
3
4
  from datetime import datetime
4
- from typing import Any, Dict, List
5
+ from typing import Any, Union
5
6
 
6
7
  from ..core.cache import GitAnalysisCache
7
8
  from .github_integration import GitHubIntegration
@@ -10,110 +11,110 @@ from .jira_integration import JIRAIntegration
10
11
 
11
12
  class IntegrationOrchestrator:
12
13
  """Orchestrate integrations with multiple platforms."""
13
-
14
+
14
15
  def __init__(self, config: Any, cache: GitAnalysisCache):
15
16
  """Initialize integration orchestrator."""
16
17
  self.config = config
17
18
  self.cache = cache
18
- self.integrations = {}
19
-
19
+ self.integrations: dict[str, Union[GitHubIntegration, JIRAIntegration]] = {}
20
+
20
21
  # Initialize available integrations
21
22
  if config.github and config.github.token:
22
- self.integrations['github'] = GitHubIntegration(
23
+ self.integrations["github"] = GitHubIntegration(
23
24
  config.github.token,
24
25
  cache,
25
26
  config.github.max_retries,
26
27
  config.github.backoff_factor,
27
- allowed_ticket_platforms=getattr(config.analysis, 'ticket_platforms', None)
28
+ allowed_ticket_platforms=getattr(config.analysis, "ticket_platforms", None),
28
29
  )
29
-
30
+
30
31
  # Initialize JIRA integration if configured
31
32
  if config.jira and config.jira.access_user and config.jira.access_token:
32
33
  # Get JIRA specific settings if available
33
- jira_settings = getattr(config, 'jira_integration', {})
34
- if hasattr(jira_settings, 'enabled') and jira_settings.enabled:
35
- base_url = getattr(config.jira, 'base_url', None)
34
+ jira_settings = getattr(config, "jira_integration", {})
35
+ if hasattr(jira_settings, "enabled") and jira_settings.enabled:
36
+ base_url = getattr(config.jira, "base_url", None)
36
37
  if base_url:
37
- self.integrations['jira'] = JIRAIntegration(
38
+ self.integrations["jira"] = JIRAIntegration(
38
39
  base_url,
39
40
  config.jira.access_user,
40
41
  config.jira.access_token,
41
42
  cache,
42
- story_point_fields=getattr(jira_settings, 'story_point_fields', None)
43
+ story_point_fields=getattr(jira_settings, "story_point_fields", None),
43
44
  )
44
-
45
- def enrich_repository_data(self, repo_config: Any, commits: List[Dict[str, Any]],
46
- since: datetime) -> Dict[str, Any]:
45
+
46
+ def enrich_repository_data(
47
+ self, repo_config: Any, commits: list[dict[str, Any]], since: datetime
48
+ ) -> dict[str, Any]:
47
49
  """Enrich repository data from all available integrations."""
48
- enrichment = {
49
- 'prs': [],
50
- 'issues': [],
51
- 'pr_metrics': {}
52
- }
53
-
50
+ enrichment: dict[str, Any] = {"prs": [], "issues": [], "pr_metrics": {}}
51
+
54
52
  # GitHub enrichment
55
- if 'github' in self.integrations and repo_config.github_repo:
56
- github = self.integrations['github']
57
-
58
- try:
59
- # Get PR data
60
- prs = github.enrich_repository_with_prs(
61
- repo_config.github_repo, commits, since
62
- )
63
- enrichment['prs'] = prs
64
-
65
- # Calculate PR metrics
66
- if prs:
67
- enrichment['pr_metrics'] = github.calculate_pr_metrics(prs)
68
-
69
- except Exception as e:
70
- print(f" ⚠️ GitHub enrichment failed: {e}")
71
-
53
+ if "github" in self.integrations and repo_config.github_repo:
54
+ github_integration = self.integrations["github"]
55
+ if isinstance(github_integration, GitHubIntegration):
56
+ try:
57
+ # Get PR data
58
+ prs = github_integration.enrich_repository_with_prs(
59
+ repo_config.github_repo, commits, since
60
+ )
61
+ enrichment["prs"] = prs
62
+
63
+ # Calculate PR metrics
64
+ if prs:
65
+ enrichment["pr_metrics"] = github_integration.calculate_pr_metrics(prs)
66
+
67
+ except Exception as e:
68
+ print(f" ⚠️ GitHub enrichment failed: {e}")
69
+
72
70
  # JIRA enrichment for story points
73
- if 'jira' in self.integrations:
74
- jira = self.integrations['jira']
75
- try:
76
- # Enrich commits with JIRA story points
77
- jira.enrich_commits_with_jira_data(commits)
78
-
79
- # Enrich PRs with JIRA story points
80
- if enrichment['prs']:
81
- jira.enrich_prs_with_jira_data(enrichment['prs'])
82
-
83
- except Exception as e:
84
- print(f" ⚠️ JIRA enrichment failed: {e}")
85
-
71
+ if "jira" in self.integrations:
72
+ jira_integration = self.integrations["jira"]
73
+ if isinstance(jira_integration, JIRAIntegration):
74
+ try:
75
+ # Enrich commits with JIRA story points
76
+ jira_integration.enrich_commits_with_jira_data(commits)
77
+
78
+ # Enrich PRs with JIRA story points
79
+ if enrichment["prs"]:
80
+ jira_integration.enrich_prs_with_jira_data(enrichment["prs"])
81
+
82
+ except Exception as e:
83
+ print(f" ⚠️ JIRA enrichment failed: {e}")
84
+
86
85
  return enrichment
87
-
88
- def get_platform_issues(self, project_key: str, since: datetime) -> List[Dict[str, Any]]:
86
+
87
+ def get_platform_issues(self, project_key: str, since: datetime) -> list[dict[str, Any]]:
89
88
  """Get issues from all configured platforms."""
90
- all_issues = []
91
-
89
+ all_issues: list[dict[str, Any]] = []
90
+
92
91
  # Check cache first
93
92
  cached_issues = []
94
- for platform in ['github', 'jira', 'clickup', 'linear']:
93
+ for platform in ["github", "jira", "clickup", "linear"]:
95
94
  cached = self.cache.get_cached_issues(platform, project_key)
96
95
  cached_issues.extend(cached)
97
-
96
+
98
97
  if cached_issues:
99
98
  return cached_issues
100
-
99
+
101
100
  # Future: Fetch from APIs if not cached
102
101
  # This is where we'd add actual API calls to each platform
103
-
102
+
104
103
  return all_issues
105
-
106
- def export_to_json(self,
107
- commits: List[Dict[str, Any]],
108
- prs: List[Dict[str, Any]],
109
- developer_stats: List[Dict[str, Any]],
110
- project_metrics: Dict[str, Any],
111
- dora_metrics: Dict[str, Any],
112
- output_path: str) -> str:
104
+
105
+ def export_to_json(
106
+ self,
107
+ commits: list[dict[str, Any]],
108
+ prs: list[dict[str, Any]],
109
+ developer_stats: list[dict[str, Any]],
110
+ project_metrics: dict[str, Any],
111
+ dora_metrics: dict[str, Any],
112
+ output_path: str,
113
+ ) -> str:
113
114
  """Export all data to JSON format for API consumption."""
114
-
115
+
115
116
  # Prepare data for JSON serialization
116
- def serialize_dates(obj):
117
+ def serialize_dates(obj: Any) -> Any:
117
118
  """Convert datetime objects to ISO format strings."""
118
119
  if isinstance(obj, datetime):
119
120
  return obj.isoformat()
@@ -122,24 +123,24 @@ class IntegrationOrchestrator:
122
123
  elif isinstance(obj, list):
123
124
  return [serialize_dates(item) for item in obj]
124
125
  return obj
125
-
126
+
126
127
  export_data = {
127
- 'metadata': {
128
- 'generated_at': datetime.now().isoformat(),
129
- 'version': '1.0',
130
- 'total_commits': len(commits),
131
- 'total_prs': len(prs),
132
- 'total_developers': len(developer_stats)
128
+ "metadata": {
129
+ "generated_at": datetime.now().isoformat(),
130
+ "version": "1.0",
131
+ "total_commits": len(commits),
132
+ "total_prs": len(prs),
133
+ "total_developers": len(developer_stats),
133
134
  },
134
- 'commits': serialize_dates(commits),
135
- 'pull_requests': serialize_dates(prs),
136
- 'developers': serialize_dates(developer_stats),
137
- 'project_metrics': serialize_dates(project_metrics),
138
- 'dora_metrics': serialize_dates(dora_metrics)
135
+ "commits": serialize_dates(commits),
136
+ "pull_requests": serialize_dates(prs),
137
+ "developers": serialize_dates(developer_stats),
138
+ "project_metrics": serialize_dates(project_metrics),
139
+ "dora_metrics": serialize_dates(dora_metrics),
139
140
  }
140
-
141
+
141
142
  # Write JSON file
142
- with open(output_path, 'w') as f:
143
+ with open(output_path, "w") as f:
143
144
  json.dump(export_data, f, indent=2)
144
-
145
- return output_path
145
+
146
+ return output_path