greenmining 1.1.5__py3-none-any.whl → 1.1.7__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/__init__.py CHANGED
@@ -9,7 +9,7 @@ from greenmining.gsf_patterns import (
9
9
  is_green_aware,
10
10
  )
11
11
 
12
- __version__ = "1.1.5"
12
+ __version__ = "1.1.7"
13
13
 
14
14
 
15
15
  def fetch_repositories(
@@ -50,7 +50,8 @@ class StatisticalAnalyzer:
50
50
  def temporal_trend_analysis(self, commits_df: pd.DataFrame) -> Dict[str, Any]:
51
51
  # Analyze temporal trends in green awareness.
52
52
  # Prepare time series data
53
- commits_df["date"] = pd.to_datetime(commits_df["date"])
53
+ commits_df["date"] = pd.to_datetime(commits_df["date"], utc=True, errors="coerce")
54
+ commits_df["date"] = commits_df["date"].dt.tz_localize(None)
54
55
  commits_df = commits_df.sort_values("date")
55
56
 
56
57
  # Monthly aggregation
@@ -42,6 +42,14 @@ class EnergyMetrics:
42
42
  start_time: Optional[datetime] = None
43
43
  end_time: Optional[datetime] = None
44
44
 
45
+ @property
46
+ def energy_joules(self) -> float:
47
+ return self.joules
48
+
49
+ @property
50
+ def average_power_watts(self) -> float:
51
+ return self.watts_avg
52
+
45
53
  def to_dict(self) -> Dict[str, Any]:
46
54
  # Convert to dictionary.
47
55
  return {
@@ -470,13 +470,16 @@ class LocalRepoAnalyzer:
470
470
 
471
471
  colored_print(f" Cloning to: {local_path}", "cyan")
472
472
 
473
- # Phase 2.2: Start energy measurement if enabled
473
+ # Phase 2.2: Start energy measurement if enabled (fresh meter per repo)
474
474
  energy_result = None
475
- if self.energy_tracking and self._energy_meter:
475
+ energy_meter = None
476
+ if self.energy_tracking:
476
477
  try:
477
- self._energy_meter.start()
478
- except Exception as e:
479
- colored_print(f" Warning: Energy measurement start failed: {e}", "yellow")
478
+ from greenmining.energy.base import get_energy_meter
479
+ energy_meter = get_energy_meter(self.energy_backend)
480
+ energy_meter.start()
481
+ except Exception:
482
+ energy_meter = None
480
483
 
481
484
  commits_analyzed = []
482
485
  commit_count = 0
@@ -503,11 +506,11 @@ class LocalRepoAnalyzer:
503
506
  colored_print(f" Analyzed {len(commits_analyzed)} commits", "green")
504
507
 
505
508
  # Phase 2.2: Stop energy measurement
506
- if self.energy_tracking and self._energy_meter:
509
+ if energy_meter:
507
510
  try:
508
- energy_result = self._energy_meter.stop()
509
- except Exception as e:
510
- colored_print(f" Warning: Energy measurement stop failed: {e}", "yellow")
511
+ energy_result = energy_meter.stop()
512
+ except Exception:
513
+ pass
511
514
 
512
515
  # Compute process metrics if enabled
513
516
  process_metrics = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: greenmining
3
- Version: 1.1.5
3
+ Version: 1.1.7
4
4
  Summary: An empirical Python library for Mining Software Repositories (MSR) in Green IT research
5
5
  Author-email: Adam Bouafia <a.bouafia@student.vu.nl>
6
6
  License: MIT
@@ -49,8 +49,6 @@ Requires-Dist: twine; extra == "dev"
49
49
  Provides-Extra: energy
50
50
  Requires-Dist: psutil; extra == "energy"
51
51
  Requires-Dist: codecarbon; extra == "energy"
52
- Provides-Extra: dashboard
53
- Requires-Dist: flask; extra == "dashboard"
54
52
  Provides-Extra: docs
55
53
  Requires-Dist: sphinx; extra == "docs"
56
54
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
@@ -80,7 +78,6 @@ An empirical Python library for Mining Software Repositories (MSR) in Green IT r
80
78
  - **Method-level analysis** - Per-method complexity and metrics via Lizard integration
81
79
  - **Version power comparison** - Compare power consumption across software versions
82
80
  - **Generate research datasets** - Statistical analysis, temporal trends, and publication-ready reports
83
- - **Web dashboard** - Flask-based interactive visualization of analysis results
84
81
 
85
82
  Whether you're conducting MSR research, analyzing green software adoption, or measuring the energy footprint of codebases, GreenMining provides the empirical toolkit you need.
86
83
 
@@ -452,15 +449,6 @@ print(f"Spearman: {correlator.spearman}")
452
449
  print(f"Feature importance: {correlator.feature_importance}")
453
450
  ```
454
451
 
455
- #### Web Dashboard
456
-
457
- ```python
458
- from greenmining.dashboard import run_dashboard
459
-
460
- # Launch interactive dashboard (requires pip install greenmining[dashboard])
461
- run_dashboard(data_dir="./data", host="127.0.0.1", port=5000)
462
- ```
463
-
464
452
  #### Pipeline Batch Analysis
465
453
 
466
454
  ```python
@@ -698,7 +686,6 @@ config = Config(
698
686
  - **Full Process Metrics**: All 8 process metrics (ChangeSet, CodeChurn, CommitsCount, ContributorsCount, ContributorsExperience, HistoryComplexity, HunksCount, LinesCount)
699
687
  - **Statistical Analysis**: Correlations, effect sizes, and temporal trends
700
688
  - **Multi-format Output**: Markdown reports, CSV exports, JSON data
701
- - **Web Dashboard**: Flask-based interactive visualization (`pip install greenmining[dashboard]`)
702
689
  - **Docker Support**: Pre-built images for containerized analysis
703
690
 
704
691
  ### Energy Measurement
@@ -857,7 +844,6 @@ ruff check greenmining/ tests/
857
844
 
858
845
  ```bash
859
846
  pip install greenmining[energy] # psutil, codecarbon (energy measurement)
860
- pip install greenmining[dashboard] # flask (web dashboard)
861
847
  pip install greenmining[dev] # pytest, black, ruff, mypy (development)
862
848
  ```
863
849
 
@@ -1,4 +1,4 @@
1
- greenmining/__init__.py,sha256=1TrBIx5HvQhay8_08G-PggpKeGe3zM061Y27Jy_fEcI,3390
1
+ greenmining/__init__.py,sha256=S7eN_NkK3aC68nLMNiVgcPdcVwW-PvbyfY_3ZpHsuQw,3390
2
2
  greenmining/__main__.py,sha256=NYOVS7D4w2XDLn6SyXHXPKE5GrNGOeoWSTb_KazgK5c,590
3
3
  greenmining/__version__.py,sha256=xZc02a8bS3vUJlzh8k9RoxemB1irQmq_SpVVj6Cg5M0,62
4
4
  greenmining/config.py,sha256=M4a7AwM1ErCmOY0n5Vmyoo9HPblSkTZ-HD3k2YHzs4A,8340
@@ -9,15 +9,13 @@ greenmining/analyzers/code_diff_analyzer.py,sha256=1dk68R3O0RZG8gx1cm9B_UlZ1Uwyb
9
9
  greenmining/analyzers/metrics_power_correlator.py,sha256=qMKr4hSTzT0Un3vsGZNkPCp9TxyzdFwrhjw5M1IKOgk,5964
10
10
  greenmining/analyzers/power_regression.py,sha256=5pxs7IoTtGcwwX5KzGeM5hOm2I9Axr-0X4N_4007iMw,7387
11
11
  greenmining/analyzers/qualitative_analyzer.py,sha256=RcjOMLj_DPH869ey9J0uI7JK_krCefMhNkPLOJUDFF8,15391
12
- greenmining/analyzers/statistical_analyzer.py,sha256=DzWAcCyw42Ig3FIxTwPPBikgt2uzMdktxklonOYfnOk,7166
12
+ greenmining/analyzers/statistical_analyzer.py,sha256=rqLsRGuOHhxEMQAFx5dmWMHDgdb6ktL7CY3dAebQvpA,7262
13
13
  greenmining/analyzers/temporal_analyzer.py,sha256=JfTcAoI20oCFMehGrSRnDqhJTXI-RUbdCTMwDOTW9-g,14259
14
14
  greenmining/analyzers/version_power_analyzer.py,sha256=2P6zOqBg-ButtIhF-4cutiwD2Q1geMY49VFUghHXXoI,8119
15
15
  greenmining/controllers/__init__.py,sha256=UiAT6zBvC1z_9cJWfzq1cLA0I4r9b2vURHipj8oDczI,180
16
16
  greenmining/controllers/repository_controller.py,sha256=DM9BabUAwZJARGngCk_4wEYPw2adn8iESCiFQ7Um4LQ,3880
17
- greenmining/dashboard/__init__.py,sha256=Ig_291-hLrH9k3rV0whhQ1EkhiaRR8ciHiJ5s5OCBf4,141
18
- greenmining/dashboard/app.py,sha256=Hk6_i2qmcg6SGW7UzxglEIvUBJiloRA-hMYI-YSORcA,8604
19
17
  greenmining/energy/__init__.py,sha256=GoCYh7hitWBoPMtan1HF1yezCHi7o4sa_YUJgGkeJc8,558
20
- greenmining/energy/base.py,sha256=mVdp-E04KWu3UnHFL61pzrI-OP-KsUshAmXHwgsJkRU,5749
18
+ greenmining/energy/base.py,sha256=3hIPgc4B0Nz9V7DTh2Xd6trDRtmozUBBpa5UWRuWzcw,5918
21
19
  greenmining/energy/carbon_reporter.py,sha256=bKIFlLhHfYzI4DBu_ff4GW1Psz4oSCAF4NmzQb-EShA,8298
22
20
  greenmining/energy/codecarbon_meter.py,sha256=HyQptyEaS1ZMu_qdxg0Tyuly1PCmmbbNwwYX8qYsTs4,4927
23
21
  greenmining/energy/cpu_meter.py,sha256=mhEG3Y7fjz3wV5lojcYeFXvCXXgmelGQaBfN2q7yTNc,5007
@@ -35,10 +33,10 @@ greenmining/services/data_aggregator.py,sha256=TsFT0oGOnnHk0QGZ1tT6ZhKGc5X1H1D1u
35
33
  greenmining/services/data_analyzer.py,sha256=f0nlJkPAclHHCzzTyQW5bjhYrgE0XXiR1x7_o3fJaDs,9732
36
34
  greenmining/services/github_fetcher.py,sha256=sdkS-LhHmX7mgMdlClCwEUVnZrItc0Pt6FVtlWk5iLU,106
37
35
  greenmining/services/github_graphql_fetcher.py,sha256=ZklXdEAc60KeFL83zRYMwW_-2OwMKpfPY7Wrifl0D50,11539
38
- greenmining/services/local_repo_analyzer.py,sha256=N3QT7qLKT7ddvOhygJfUG5eMGcHIGeVevuleHB0oCN8,24918
36
+ greenmining/services/local_repo_analyzer.py,sha256=pnH_Hf3GjCFovurEl2uZgOgD3qjqcsP2K1QnKHddkSY,24903
39
37
  greenmining/services/reports.py,sha256=Vrw_pBNmVw2mTAf1dpcAqjBe6gXv-O4w_XweoVTt7L8,23392
40
- greenmining-1.1.5.dist-info/licenses/LICENSE,sha256=M7ma3JHGeiIZIs3ea0HTcFl_wLFPX2NZElUliYs4bCA,1083
41
- greenmining-1.1.5.dist-info/METADATA,sha256=MMoin-BR4dQGdzPploVH2GIpCQY9NGnDT-I6X_C1mfw,30811
42
- greenmining-1.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
43
- greenmining-1.1.5.dist-info/top_level.txt,sha256=nreXgXxZIWI-42yQknQ0HXtUrFnzZ8N1ra4Mdy2KcsI,12
44
- greenmining-1.1.5.dist-info/RECORD,,
38
+ greenmining-1.1.7.dist-info/licenses/LICENSE,sha256=M7ma3JHGeiIZIs3ea0HTcFl_wLFPX2NZElUliYs4bCA,1083
39
+ greenmining-1.1.7.dist-info/METADATA,sha256=LiRreZ7kVcsr76DZHlkyTcBD7u58IokOvJe4lfDu3vw,30280
40
+ greenmining-1.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
41
+ greenmining-1.1.7.dist-info/top_level.txt,sha256=nreXgXxZIWI-42yQknQ0HXtUrFnzZ8N1ra4Mdy2KcsI,12
42
+ greenmining-1.1.7.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- # Web dashboard module for GreenMining visualization.
2
-
3
- from .app import create_app, run_dashboard
4
-
5
- __all__ = ["create_app", "run_dashboard"]
@@ -1,200 +0,0 @@
1
- # Flask-based web dashboard for GreenMining analysis visualization.
2
- # Provides interactive charts for repository analysis, pattern distribution,
3
- # temporal trends, and energy consumption.
4
-
5
- from __future__ import annotations
6
-
7
- import json
8
- from pathlib import Path
9
- from typing import Any, Dict, List, Optional
10
-
11
-
12
- def create_app(data_dir: str = "./data"):
13
- # Create Flask application for the dashboard.
14
- # Args:
15
- # data_dir: Path to directory containing analysis JSON files
16
- # Returns:
17
- # Flask application instance
18
- try:
19
- from flask import Flask, render_template_string, jsonify, request
20
- except ImportError:
21
- raise ImportError("Flask is required for the dashboard. Install it with: pip install flask")
22
-
23
- app = Flask(__name__)
24
- data_path = Path(data_dir)
25
-
26
- def _load_data(filename: str) -> Dict[str, Any]:
27
- filepath = data_path / filename
28
- if filepath.exists():
29
- with open(filepath, encoding="utf-8") as f:
30
- return json.load(f)
31
- return {}
32
-
33
- @app.route("/")
34
- def index():
35
- return render_template_string(DASHBOARD_HTML)
36
-
37
- @app.route("/api/repositories")
38
- def api_repositories():
39
- data = _load_data("repositories.json")
40
- return jsonify(data)
41
-
42
- @app.route("/api/analysis")
43
- def api_analysis():
44
- data = _load_data("analysis_results.json")
45
- return jsonify(data)
46
-
47
- @app.route("/api/statistics")
48
- def api_statistics():
49
- data = _load_data("aggregated_statistics.json")
50
- return jsonify(data)
51
-
52
- @app.route("/api/energy")
53
- def api_energy():
54
- data = _load_data("energy_report.json")
55
- return jsonify(data)
56
-
57
- @app.route("/api/summary")
58
- def api_summary():
59
- # Build summary from available data
60
- repos = _load_data("repositories.json")
61
- analysis = _load_data("analysis_results.json")
62
-
63
- repo_count = 0
64
- if isinstance(repos, list):
65
- repo_count = len(repos)
66
- elif isinstance(repos, dict):
67
- repo_count = repos.get("total_repositories", len(repos.get("repositories", [])))
68
-
69
- commit_count = 0
70
- green_count = 0
71
- if isinstance(analysis, list):
72
- commit_count = len(analysis)
73
- green_count = sum(1 for a in analysis if a.get("green_aware"))
74
- elif isinstance(analysis, dict):
75
- results = analysis.get("results", [])
76
- for r in results:
77
- commits = r.get("commits", [])
78
- commit_count += len(commits)
79
- green_count += sum(1 for c in commits if c.get("green_aware"))
80
-
81
- green_rate = (green_count / commit_count * 100) if commit_count > 0 else 0
82
-
83
- return jsonify(
84
- {
85
- "repositories": repo_count,
86
- "commits_analyzed": commit_count,
87
- "green_commits": green_count,
88
- "green_rate": round(green_rate, 1),
89
- }
90
- )
91
-
92
- return app
93
-
94
-
95
- def run_dashboard(data_dir: str = "./data", host: str = "127.0.0.1", port: int = 5000):
96
- # Run the dashboard server.
97
- # Args:
98
- # data_dir: Path to analysis data directory
99
- # host: Host to bind to
100
- # port: Port to bind to
101
- app = create_app(data_dir)
102
- print(f"GreenMining Dashboard running at http://{host}:{port}")
103
- print(f"Data directory: {data_dir}")
104
- app.run(host=host, port=port, debug=False)
105
-
106
-
107
- # Dashboard HTML template with embedded JS (no external dependencies)
108
- DASHBOARD_HTML = """
109
- <!DOCTYPE html>
110
- <html lang="en">
111
- <head>
112
- <meta charset="UTF-8">
113
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
114
- <title>GreenMining Dashboard</title>
115
- <style>
116
- * { margin: 0; padding: 0; box-sizing: border-box; }
117
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
118
- background: #f5f7fa; color: #333; }
119
- .header { background: #1a472a; color: white; padding: 20px 40px; }
120
- .header h1 { font-size: 24px; font-weight: 600; }
121
- .header p { font-size: 14px; opacity: 0.8; margin-top: 4px; }
122
- .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
123
- .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
124
- .card { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
125
- .card h3 { font-size: 13px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; }
126
- .card .value { font-size: 32px; font-weight: 700; color: #1a472a; margin-top: 8px; }
127
- .card .subtitle { font-size: 12px; color: #999; margin-top: 4px; }
128
- .section { margin-bottom: 24px; }
129
- .section h2 { font-size: 18px; margin-bottom: 12px; color: #1a472a; }
130
- table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px;
131
- overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
132
- th, td { padding: 12px 16px; text-align: left; border-bottom: 1px solid #eee; }
133
- th { background: #f8f9fa; font-size: 12px; text-transform: uppercase; color: #666; }
134
- td { font-size: 14px; }
135
- .badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; }
136
- .badge-green { background: #d4edda; color: #155724; }
137
- .badge-gray { background: #e9ecef; color: #495057; }
138
- .bar { height: 8px; background: #e9ecef; border-radius: 4px; overflow: hidden; }
139
- .bar-fill { height: 100%; background: #1a472a; border-radius: 4px;
140
- transition: width 0.5s ease; }
141
- .loading { text-align: center; padding: 40px; color: #999; }
142
- </style>
143
- </head>
144
- <body>
145
- <div class="header">
146
- <h1>GreenMining Dashboard</h1>
147
- <p>Mining Software Repositories for Green IT Research</p>
148
- </div>
149
- <div class="container">
150
- <div class="grid" id="summary-cards">
151
- <div class="card"><h3>Repositories</h3><div class="value" id="repo-count">-</div></div>
152
- <div class="card"><h3>Commits Analyzed</h3><div class="value" id="commit-count">-</div></div>
153
- <div class="card"><h3>Green Commits</h3><div class="value" id="green-count">-</div></div>
154
- <div class="card"><h3>Green Rate</h3><div class="value" id="green-rate">-</div></div>
155
- </div>
156
- <div class="section">
157
- <h2>Repositories</h2>
158
- <div id="repo-table"><div class="loading">Loading data...</div></div>
159
- </div>
160
- </div>
161
- <script>
162
- async function loadDashboard() {
163
- try {
164
- const summary = await fetch('/api/summary').then(r => r.json());
165
- document.getElementById('repo-count').textContent = summary.repositories;
166
- document.getElementById('commit-count').textContent = summary.commits_analyzed.toLocaleString();
167
- document.getElementById('green-count').textContent = summary.green_commits.toLocaleString();
168
- document.getElementById('green-rate').textContent = summary.green_rate + '%';
169
- } catch(e) {
170
- console.log('Summary not available:', e);
171
- }
172
-
173
- try {
174
- const repos = await fetch('/api/repositories').then(r => r.json());
175
- const list = repos.repositories || (Array.isArray(repos) ? repos : []);
176
- if (list.length > 0) {
177
- let html = '<table><thead><tr><th>Repository</th><th>Language</th>' +
178
- '<th>Stars</th><th>Description</th></tr></thead><tbody>';
179
- list.slice(0, 50).forEach(r => {
180
- html += '<tr><td><strong>' + (r.full_name || r.name) + '</strong></td>' +
181
- '<td><span class="badge badge-green">' + (r.language || '-') + '</span></td>' +
182
- '<td>' + (r.stars || 0).toLocaleString() + '</td>' +
183
- '<td>' + (r.description || '-').substring(0, 80) + '</td></tr>';
184
- });
185
- html += '</tbody></table>';
186
- document.getElementById('repo-table').innerHTML = html;
187
- } else {
188
- document.getElementById('repo-table').innerHTML =
189
- '<div class="card">No repository data found. Run an analysis first.</div>';
190
- }
191
- } catch(e) {
192
- document.getElementById('repo-table').innerHTML =
193
- '<div class="card">Run an analysis to populate the dashboard.</div>';
194
- }
195
- }
196
- loadDashboard();
197
- </script>
198
- </body>
199
- </html>
200
- """