greenmining 0.1.4__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.
greenmining/main.py ADDED
@@ -0,0 +1,37 @@
1
+ """Main entry point for Green Microservices Mining CLI."""
2
+
3
+ import sys
4
+
5
+ from cli import cli
6
+
7
+ from greenmining.utils import colored_print, print_banner
8
+
9
+
10
+ def main():
11
+ """Main entry point with error handling."""
12
+ try:
13
+ print_banner("🌱 Green Microservices Mining Tool")
14
+ colored_print("Analyze GitHub repositories for sustainability practices\n", "cyan")
15
+
16
+ cli(obj={})
17
+
18
+ except KeyboardInterrupt:
19
+ colored_print("\n\nāš ļø Operation cancelled by user", "yellow")
20
+ sys.exit(130)
21
+
22
+ except Exception as e:
23
+ colored_print(f"\nāŒ Unexpected error: {e}", "red")
24
+
25
+ if "--verbose" in sys.argv or "-v" in sys.argv:
26
+ import traceback
27
+
28
+ colored_print("\nFull traceback:", "red")
29
+ traceback.print_exc()
30
+ else:
31
+ colored_print("Run with --verbose for detailed error information", "yellow")
32
+
33
+ sys.exit(1)
34
+
35
+
36
+ if __name__ == "__main__":
37
+ main()
@@ -0,0 +1,12 @@
1
+ """
2
+ Models Package - Data models and entities for green microservices mining.
3
+
4
+ This package contains all data structures and domain models following MCP architecture.
5
+ """
6
+
7
+ from .aggregated_stats import AggregatedStats
8
+ from .analysis_result import AnalysisResult
9
+ from .commit import Commit
10
+ from .repository import Repository
11
+
12
+ __all__ = ["Repository", "Commit", "AnalysisResult", "AggregatedStats"]
@@ -0,0 +1,30 @@
1
+ """Aggregated Statistics Model - Represents aggregated analysis data."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class AggregatedStats:
9
+ """Data model for aggregated statistics."""
10
+
11
+ summary: dict = field(default_factory=dict)
12
+ known_patterns: dict = field(default_factory=dict)
13
+ repositories: list[dict] = field(default_factory=list)
14
+ languages: dict = field(default_factory=dict)
15
+ timestamp: Optional[str] = None
16
+
17
+ def to_dict(self) -> dict:
18
+ """Convert to dictionary."""
19
+ return {
20
+ "summary": self.summary,
21
+ "known_patterns": self.known_patterns,
22
+ "repositories": self.repositories,
23
+ "languages": self.languages,
24
+ "timestamp": self.timestamp,
25
+ }
26
+
27
+ @classmethod
28
+ def from_dict(cls, data: dict) -> "AggregatedStats":
29
+ """Create from dictionary."""
30
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
@@ -0,0 +1,48 @@
1
+ """Analysis Result Model - Represents commit analysis output."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class AnalysisResult:
9
+ """Data model for commit analysis results."""
10
+
11
+ commit_id: str
12
+ repo_name: str
13
+ date: str
14
+ commit_message: str
15
+ green_aware: bool
16
+ green_evidence: Optional[str] = None
17
+ known_pattern: Optional[str] = None
18
+ pattern_confidence: Optional[str] = None
19
+ emergent_pattern: Optional[str] = None
20
+ files_changed: list = None
21
+ lines_added: int = 0
22
+ lines_deleted: int = 0
23
+
24
+ def __post_init__(self):
25
+ if self.files_changed is None:
26
+ self.files_changed = []
27
+
28
+ def to_dict(self) -> dict:
29
+ """Convert to dictionary."""
30
+ return {
31
+ "commit_id": self.commit_id,
32
+ "repo_name": self.repo_name,
33
+ "date": self.date,
34
+ "commit_message": self.commit_message,
35
+ "green_aware": self.green_aware,
36
+ "green_evidence": self.green_evidence,
37
+ "known_pattern": self.known_pattern,
38
+ "pattern_confidence": self.pattern_confidence,
39
+ "emergent_pattern": self.emergent_pattern,
40
+ "files_changed": self.files_changed,
41
+ "lines_added": self.lines_added,
42
+ "lines_deleted": self.lines_deleted,
43
+ }
44
+
45
+ @classmethod
46
+ def from_dict(cls, data: dict) -> "AnalysisResult":
47
+ """Create from dictionary."""
48
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
@@ -0,0 +1,71 @@
1
+ """Commit Model - Represents a Git commit."""
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+
6
+ @dataclass
7
+ class Commit:
8
+ """Data model for a Git commit."""
9
+
10
+ commit_id: str
11
+ repo_name: str
12
+ date: str
13
+ author: str
14
+ author_email: str
15
+ message: str
16
+ files_changed: list[str] = field(default_factory=list)
17
+ lines_added: int = 0
18
+ lines_deleted: int = 0
19
+ insertions: int = 0
20
+ deletions: int = 0
21
+ is_merge: bool = False
22
+ branches: list[str] = field(default_factory=list)
23
+ in_main_branch: bool = True
24
+
25
+ def to_dict(self) -> dict:
26
+ """Convert to dictionary."""
27
+ return {
28
+ "commit_id": self.commit_id,
29
+ "repo_name": self.repo_name,
30
+ "date": self.date,
31
+ "author": self.author,
32
+ "author_email": self.author_email,
33
+ "message": self.message,
34
+ "files_changed": self.files_changed,
35
+ "lines_added": self.lines_added,
36
+ "lines_deleted": self.lines_deleted,
37
+ "insertions": self.insertions,
38
+ "deletions": self.deletions,
39
+ "is_merge": self.is_merge,
40
+ "branches": self.branches,
41
+ "in_main_branch": self.in_main_branch,
42
+ }
43
+
44
+ @classmethod
45
+ def from_dict(cls, data: dict) -> "Commit":
46
+ """Create from dictionary."""
47
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
48
+
49
+ @classmethod
50
+ def from_pydriller_commit(cls, commit, repo_name: str) -> "Commit":
51
+ """Create from PyDriller commit object."""
52
+ return cls(
53
+ commit_id=commit.hash,
54
+ repo_name=repo_name,
55
+ date=(
56
+ commit.committer_date.isoformat()
57
+ if commit.committer_date
58
+ else commit.author_date.isoformat()
59
+ ),
60
+ author=commit.author.name,
61
+ author_email=commit.author.email,
62
+ message=commit.msg,
63
+ files_changed=[f.new_path or f.filename for f in commit.modified_files],
64
+ lines_added=commit.insertions,
65
+ lines_deleted=commit.deletions,
66
+ insertions=commit.insertions,
67
+ deletions=commit.deletions,
68
+ is_merge=commit.merge,
69
+ branches=commit.branches if hasattr(commit, "branches") else [],
70
+ in_main_branch=commit.in_main_branch if hasattr(commit, "in_main_branch") else True,
71
+ )
@@ -0,0 +1,89 @@
1
+ """Repository Model - Represents a GitHub repository."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class Repository:
9
+ """Data model for a GitHub repository."""
10
+
11
+ repo_id: int
12
+ name: str
13
+ owner: str
14
+ full_name: str
15
+ url: str
16
+ clone_url: str
17
+ language: Optional[str]
18
+ stars: int
19
+ forks: int
20
+ watchers: int
21
+ open_issues: int
22
+ last_updated: str
23
+ created_at: str
24
+ description: Optional[str]
25
+ main_branch: str
26
+ topics: list[str] = field(default_factory=list)
27
+ size: int = 0
28
+ has_issues: bool = True
29
+ has_wiki: bool = True
30
+ archived: bool = False
31
+ license: Optional[str] = None
32
+
33
+ def to_dict(self) -> dict:
34
+ """Convert to dictionary."""
35
+ return {
36
+ "repo_id": self.repo_id,
37
+ "name": self.name,
38
+ "owner": self.owner,
39
+ "full_name": self.full_name,
40
+ "url": self.url,
41
+ "clone_url": self.clone_url,
42
+ "language": self.language,
43
+ "stars": self.stars,
44
+ "forks": self.forks,
45
+ "watchers": self.watchers,
46
+ "open_issues": self.open_issues,
47
+ "last_updated": self.last_updated,
48
+ "created_at": self.created_at,
49
+ "description": self.description,
50
+ "main_branch": self.main_branch,
51
+ "topics": self.topics,
52
+ "size": self.size,
53
+ "has_issues": self.has_issues,
54
+ "has_wiki": self.has_wiki,
55
+ "archived": self.archived,
56
+ "license": self.license,
57
+ }
58
+
59
+ @classmethod
60
+ def from_dict(cls, data: dict) -> "Repository":
61
+ """Create from dictionary."""
62
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
63
+
64
+ @classmethod
65
+ def from_github_repo(cls, repo, repo_id: int) -> "Repository":
66
+ """Create from PyGithub repository object."""
67
+ return cls(
68
+ repo_id=repo_id,
69
+ name=repo.name,
70
+ owner=repo.owner.login,
71
+ full_name=repo.full_name,
72
+ url=repo.html_url,
73
+ clone_url=repo.clone_url,
74
+ language=repo.language,
75
+ stars=repo.stargazers_count,
76
+ forks=repo.forks_count,
77
+ watchers=repo.watchers_count,
78
+ open_issues=repo.open_issues_count,
79
+ last_updated=repo.updated_at.isoformat() if repo.updated_at else None,
80
+ created_at=repo.created_at.isoformat() if repo.created_at else None,
81
+ description=repo.description,
82
+ main_branch=repo.default_branch,
83
+ topics=repo.topics or [],
84
+ size=repo.size,
85
+ has_issues=repo.has_issues,
86
+ has_wiki=repo.has_wiki,
87
+ archived=repo.archived,
88
+ license=repo.license.key if repo.license else None,
89
+ )
@@ -0,0 +1,11 @@
1
+ """
2
+ Presenters Package - UI/CLI presentation layer.
3
+
4
+ Presenters handle output formatting and user interaction.
5
+ """
6
+
7
+ from .console_presenter import ConsolePresenter
8
+
9
+ __all__ = [
10
+ "ConsolePresenter",
11
+ ]
@@ -0,0 +1,141 @@
1
+ """Console Presenter - Handles console output formatting."""
2
+
3
+ from typing import Any
4
+
5
+ from tabulate import tabulate
6
+
7
+ from greenmining.utils import colored_print
8
+
9
+
10
+ class ConsolePresenter:
11
+ """Presenter for console/terminal output."""
12
+
13
+ @staticmethod
14
+ def show_banner():
15
+ """Display application banner."""
16
+ banner = """
17
+ ╔══════════════════════════════════════════════════════════╗
18
+ ā•‘ Green Microservices Mining ā•‘
19
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
20
+ """
21
+ colored_print(banner, "green")
22
+
23
+ @staticmethod
24
+ def show_repositories(repositories: list[dict], limit: int = 10):
25
+ """Display repository table."""
26
+ if not repositories:
27
+ colored_print("No repositories to display", "yellow")
28
+ return
29
+
30
+ colored_print(f"\nšŸ“Š Top {min(limit, len(repositories))} Repositories:\n", "cyan")
31
+
32
+ table_data = []
33
+ for repo in repositories[:limit]:
34
+ table_data.append(
35
+ [
36
+ repo.get("full_name", "N/A"),
37
+ repo.get("language", "N/A"),
38
+ f"{repo.get('stars', 0):,}",
39
+ (
40
+ repo.get("description", "")[:50] + "..."
41
+ if len(repo.get("description", "")) > 50
42
+ else repo.get("description", "")
43
+ ),
44
+ ]
45
+ )
46
+
47
+ headers = ["Repository", "Language", "Stars", "Description"]
48
+ print(tabulate(table_data, headers=headers, tablefmt="grid"))
49
+
50
+ @staticmethod
51
+ def show_commit_stats(stats: dict[str, Any]):
52
+ """Display commit statistics."""
53
+ colored_print("\nšŸ“ˆ Commit Statistics:\n", "cyan")
54
+
55
+ table_data = [
56
+ ["Total Commits", f"{stats.get('total_commits', 0):,}"],
57
+ ["Repositories", stats.get("total_repos", 0)],
58
+ ["Avg per Repo", f"{stats.get('avg_per_repo', 0):.1f}"],
59
+ ["Date Range", stats.get("date_range", "N/A")],
60
+ ]
61
+
62
+ print(tabulate(table_data, headers=["Metric", "Value"], tablefmt="grid"))
63
+
64
+ @staticmethod
65
+ def show_analysis_results(results: dict[str, Any]):
66
+ """Display analysis results."""
67
+ colored_print("\nšŸ”¬ Analysis Results:\n", "cyan")
68
+
69
+ summary = results.get("summary", {})
70
+ table_data = [
71
+ ["Total Commits Analyzed", f"{summary.get('total_commits', 0):,}"],
72
+ ["Green-Aware Commits", f"{summary.get('green_commits', 0):,}"],
73
+ ["Green Rate", f"{summary.get('green_commit_rate', 0):.1%}"],
74
+ ["Patterns Detected", len(results.get("known_patterns", {}))],
75
+ ]
76
+
77
+ print(tabulate(table_data, headers=["Metric", "Value"], tablefmt="grid"))
78
+
79
+ @staticmethod
80
+ def show_pattern_distribution(patterns: dict[str, Any], limit: int = 10):
81
+ """Display pattern distribution."""
82
+ if not patterns:
83
+ colored_print("No patterns to display", "yellow")
84
+ return
85
+
86
+ colored_print(f"\nšŸŽÆ Top {limit} Green Patterns:\n", "cyan")
87
+
88
+ # Sort by count
89
+ sorted_patterns = sorted(
90
+ patterns.items(), key=lambda x: x[1].get("count", 0), reverse=True
91
+ )[:limit]
92
+
93
+ table_data = []
94
+ for pattern_name, data in sorted_patterns:
95
+ table_data.append(
96
+ [
97
+ pattern_name,
98
+ data.get("count", 0),
99
+ f"{data.get('percentage', 0):.1f}%",
100
+ data.get("confidence_distribution", {}).get("HIGH", 0),
101
+ ]
102
+ )
103
+
104
+ headers = ["Pattern", "Count", "Percentage", "High Confidence"]
105
+ print(tabulate(table_data, headers=headers, tablefmt="grid"))
106
+
107
+ @staticmethod
108
+ def show_pipeline_status(status: dict[str, Any]):
109
+ """Display pipeline status."""
110
+ colored_print("\nāš™ļø Pipeline Status:\n", "cyan")
111
+
112
+ table_data = []
113
+ for phase, info in status.items():
114
+ status_icon = "āœ…" if info.get("completed") else "ā³"
115
+ table_data.append(
116
+ [status_icon, phase, info.get("file", "N/A"), info.get("size", "N/A")]
117
+ )
118
+
119
+ headers = ["Status", "Phase", "Output File", "Size"]
120
+ print(tabulate(table_data, headers=headers, tablefmt="grid"))
121
+
122
+ @staticmethod
123
+ def show_progress_message(phase: str, current: int, total: int):
124
+ """Display progress message."""
125
+ percentage = (current / total * 100) if total > 0 else 0
126
+ colored_print(f"[{phase}] Progress: {current}/{total} ({percentage:.1f}%)", "cyan")
127
+
128
+ @staticmethod
129
+ def show_error(message: str):
130
+ """Display error message."""
131
+ colored_print(f"āŒ Error: {message}", "red")
132
+
133
+ @staticmethod
134
+ def show_success(message: str):
135
+ """Display success message."""
136
+ colored_print(f"āœ… {message}", "green")
137
+
138
+ @staticmethod
139
+ def show_warning(message: str):
140
+ """Display warning message."""
141
+ colored_print(f"āš ļø Warning: {message}", "yellow")
@@ -0,0 +1,13 @@
1
+ """
2
+ Services Package - Core business logic and data processing services.
3
+
4
+ Services implement the actual mining, extraction, analysis operations.
5
+ """
6
+
7
+ from .commit_extractor import CommitExtractor
8
+ from .data_aggregator import DataAggregator
9
+ from .data_analyzer import DataAnalyzer
10
+ from .github_fetcher import GitHubFetcher
11
+ from .reports import ReportGenerator
12
+
13
+ __all__ = ["GitHubFetcher", "CommitExtractor", "DataAnalyzer", "DataAggregator", "ReportGenerator"]