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.
- gitflow_analytics/__init__.py +11 -11
- gitflow_analytics/_version.py +2 -2
- gitflow_analytics/cli.py +612 -258
- gitflow_analytics/cli_rich.py +353 -0
- gitflow_analytics/config.py +251 -141
- gitflow_analytics/core/analyzer.py +140 -103
- gitflow_analytics/core/branch_mapper.py +132 -132
- gitflow_analytics/core/cache.py +240 -169
- gitflow_analytics/core/identity.py +210 -173
- gitflow_analytics/extractors/base.py +13 -11
- gitflow_analytics/extractors/story_points.py +70 -59
- gitflow_analytics/extractors/tickets.py +101 -87
- gitflow_analytics/integrations/github_integration.py +84 -77
- gitflow_analytics/integrations/jira_integration.py +116 -104
- gitflow_analytics/integrations/orchestrator.py +86 -85
- gitflow_analytics/metrics/dora.py +181 -177
- gitflow_analytics/models/database.py +190 -53
- gitflow_analytics/qualitative/__init__.py +30 -0
- gitflow_analytics/qualitative/classifiers/__init__.py +13 -0
- gitflow_analytics/qualitative/classifiers/change_type.py +468 -0
- gitflow_analytics/qualitative/classifiers/domain_classifier.py +399 -0
- gitflow_analytics/qualitative/classifiers/intent_analyzer.py +436 -0
- gitflow_analytics/qualitative/classifiers/risk_analyzer.py +412 -0
- gitflow_analytics/qualitative/core/__init__.py +13 -0
- gitflow_analytics/qualitative/core/llm_fallback.py +653 -0
- gitflow_analytics/qualitative/core/nlp_engine.py +373 -0
- gitflow_analytics/qualitative/core/pattern_cache.py +457 -0
- gitflow_analytics/qualitative/core/processor.py +540 -0
- gitflow_analytics/qualitative/models/__init__.py +25 -0
- gitflow_analytics/qualitative/models/schemas.py +272 -0
- gitflow_analytics/qualitative/utils/__init__.py +13 -0
- gitflow_analytics/qualitative/utils/batch_processor.py +326 -0
- gitflow_analytics/qualitative/utils/cost_tracker.py +343 -0
- gitflow_analytics/qualitative/utils/metrics.py +347 -0
- gitflow_analytics/qualitative/utils/text_processing.py +243 -0
- gitflow_analytics/reports/analytics_writer.py +11 -4
- gitflow_analytics/reports/csv_writer.py +51 -31
- gitflow_analytics/reports/narrative_writer.py +16 -14
- gitflow_analytics/tui/__init__.py +5 -0
- gitflow_analytics/tui/app.py +721 -0
- gitflow_analytics/tui/screens/__init__.py +8 -0
- gitflow_analytics/tui/screens/analysis_progress_screen.py +487 -0
- gitflow_analytics/tui/screens/configuration_screen.py +547 -0
- gitflow_analytics/tui/screens/loading_screen.py +358 -0
- gitflow_analytics/tui/screens/main_screen.py +304 -0
- gitflow_analytics/tui/screens/results_screen.py +698 -0
- gitflow_analytics/tui/widgets/__init__.py +7 -0
- gitflow_analytics/tui/widgets/data_table.py +257 -0
- gitflow_analytics/tui/widgets/export_modal.py +301 -0
- gitflow_analytics/tui/widgets/progress_widget.py +192 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/METADATA +31 -4
- gitflow_analytics-1.0.3.dist-info/RECORD +62 -0
- gitflow_analytics-1.0.1.dist-info/RECORD +0 -31
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/WHEEL +0 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/entry_points.txt +0 -0
- {gitflow_analytics-1.0.1.dist-info → gitflow_analytics-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {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,
|
|
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[
|
|
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,
|
|
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,
|
|
34
|
-
if hasattr(jira_settings,
|
|
35
|
-
base_url = getattr(config.jira,
|
|
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[
|
|
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,
|
|
43
|
+
story_point_fields=getattr(jira_settings, "story_point_fields", None),
|
|
43
44
|
)
|
|
44
|
-
|
|
45
|
-
def enrich_repository_data(
|
|
46
|
-
|
|
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
|
-
|
|
50
|
-
'issues': [],
|
|
51
|
-
'pr_metrics': {}
|
|
52
|
-
}
|
|
53
|
-
|
|
50
|
+
enrichment: dict[str, Any] = {"prs": [], "issues": [], "pr_metrics": {}}
|
|
51
|
+
|
|
54
52
|
# GitHub enrichment
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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) ->
|
|
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 [
|
|
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(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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,
|
|
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
|