@theihtisham/dev-pulse 1.0.0 → 1.1.0
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.
- package/.editorconfig +12 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/ci.yml +33 -0
- package/CODE_OF_CONDUCT.md +27 -0
- package/Dockerfile +8 -0
- package/LICENSE +21 -21
- package/README.md +135 -39
- package/SECURITY.md +22 -0
- package/devpulse/__init__.py +4 -4
- package/devpulse/api/__init__.py +1 -1
- package/devpulse/api/app.py +371 -371
- package/devpulse/cli/__init__.py +1 -1
- package/devpulse/cli/dashboard.py +131 -131
- package/devpulse/cli/main.py +678 -678
- package/devpulse/cli/render.py +175 -175
- package/devpulse/core/__init__.py +34 -34
- package/devpulse/core/analytics.py +487 -487
- package/devpulse/core/config.py +77 -77
- package/devpulse/core/database.py +612 -612
- package/devpulse/core/github_client.py +281 -281
- package/devpulse/core/models.py +142 -142
- package/devpulse/core/report_generator.py +454 -454
- package/devpulse/static/.gitkeep +1 -1
- package/devpulse/templates/report.html +64 -64
- package/package.json +35 -35
- package/pyproject.toml +80 -80
- package/requirements.txt +14 -14
- package/tests/__init__.py +1 -1
- package/tests/conftest.py +208 -208
- package/tests/test_analytics.py +284 -284
- package/tests/test_api.py +313 -313
- package/tests/test_cli.py +204 -204
- package/tests/test_config.py +47 -47
- package/tests/test_database.py +255 -255
- package/tests/test_models.py +107 -107
- package/tests/test_report_generator.py +173 -173
- package/jest.config.js +0 -7
package/devpulse/cli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"""CLI package for DevPulse."""
|
|
1
|
+
"""CLI package for DevPulse."""
|
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
"""Interactive terminal dashboard for DevPulse."""
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
from rich.console import Console
|
|
6
|
-
from rich.table import Table
|
|
7
|
-
from rich.panel import Panel
|
|
8
|
-
from rich.layout import Layout
|
|
9
|
-
from rich.text import Text
|
|
10
|
-
|
|
11
|
-
from devpulse.core.database import Database
|
|
12
|
-
from devpulse.core.analytics import AnalyticsEngine
|
|
13
|
-
from devpulse.core.report_generator import ReportGenerator
|
|
14
|
-
from devpulse.cli.render import render_header, render_heatmap, render_insight_card
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def dashboard_cmd(
|
|
18
|
-
author: Optional[str] = None,
|
|
19
|
-
days: int = 30,
|
|
20
|
-
repo: Optional[str] = None,
|
|
21
|
-
) -> None:
|
|
22
|
-
"""Launch the full interactive terminal dashboard."""
|
|
23
|
-
console = Console()
|
|
24
|
-
db = Database()
|
|
25
|
-
engine = AnalyticsEngine(db=db)
|
|
26
|
-
report_gen = ReportGenerator(db=db)
|
|
27
|
-
|
|
28
|
-
render_header(console)
|
|
29
|
-
|
|
30
|
-
# ── Section 1: Summary ───────────────────────────────────────────
|
|
31
|
-
console.rule("[bold cyan]Overview[/bold cyan]")
|
|
32
|
-
|
|
33
|
-
daily = report_gen.daily_report(author=author, repo=repo)
|
|
34
|
-
console.print(f"\n Date: [bold]{daily['date']}[/bold]")
|
|
35
|
-
console.print(f" {daily['summary']}\n")
|
|
36
|
-
|
|
37
|
-
# ── Section 2: Team Metrics ──────────────────────────────────────
|
|
38
|
-
console.rule("[bold cyan]Team Metrics[/bold cyan]")
|
|
39
|
-
console.print()
|
|
40
|
-
|
|
41
|
-
team = engine.team_metrics(days=days, repo=repo)
|
|
42
|
-
if team:
|
|
43
|
-
table = Table(show_header=True, header_style="bold cyan", expand=True)
|
|
44
|
-
table.add_column("Developer", style="white", min_width=15)
|
|
45
|
-
table.add_column("Commits", justify="right")
|
|
46
|
-
table.add_column("C/D", justify="right", style="dim")
|
|
47
|
-
table.add_column("PRs", justify="right")
|
|
48
|
-
table.add_column("Reviews", justify="right")
|
|
49
|
-
table.add_column("Merge(h)", justify="right")
|
|
50
|
-
table.add_column("+/- Lines", justify="right")
|
|
51
|
-
|
|
52
|
-
for m in team[:10]:
|
|
53
|
-
table.add_row(
|
|
54
|
-
m.author,
|
|
55
|
-
str(m.commits_count),
|
|
56
|
-
str(m.commits_per_day),
|
|
57
|
-
str(m.prs_created),
|
|
58
|
-
str(m.reviews_given),
|
|
59
|
-
str(m.avg_pr_merge_time_hours),
|
|
60
|
-
f"+{m.lines_added}/-{m.lines_removed}",
|
|
61
|
-
)
|
|
62
|
-
console.print(table)
|
|
63
|
-
else:
|
|
64
|
-
console.print("[dim]No team data yet. Run 'devpulse sync' to fetch data.[/dim]")
|
|
65
|
-
|
|
66
|
-
# ── Section 3: Team Health ───────────────────────────────────────
|
|
67
|
-
console.rule("[bold cyan]Team Health[/bold cyan]")
|
|
68
|
-
console.print()
|
|
69
|
-
|
|
70
|
-
health = engine.team_health(days=days, repo=repo)
|
|
71
|
-
score_color = "green" if health.overall_score >= 70 else "yellow" if health.overall_score >= 40 else "red"
|
|
72
|
-
console.print(f" Health Score: [{score_color}]{health.overall_score}/100[/{score_color}]")
|
|
73
|
-
console.print(f" Workload Balance: {health.workload_balance:.0%}")
|
|
74
|
-
console.print(f" Collaboration: {health.collaboration_score:.0%}")
|
|
75
|
-
console.print(f" Velocity: {health.velocity_trend}")
|
|
76
|
-
|
|
77
|
-
if health.burnout_risk:
|
|
78
|
-
console.print("\n [bold]Burnout Risk:[/bold]")
|
|
79
|
-
for name, risk in sorted(health.burnout_risk.items(), key=lambda x: x[1], reverse=True):
|
|
80
|
-
color = "red" if risk >= 0.6 else "yellow" if risk >= 0.3 else "green"
|
|
81
|
-
bar_len = int(risk * 20)
|
|
82
|
-
bar = "#" * bar_len + "-" * (20 - bar_len)
|
|
83
|
-
console.print(f" {name:<15} [{color}]{bar}[/{color}] {risk:.0%}")
|
|
84
|
-
|
|
85
|
-
# ── Section 4: AI Insights ───────────────────────────────────────
|
|
86
|
-
console.rule("[bold cyan]AI Insights[/bold cyan]")
|
|
87
|
-
console.print()
|
|
88
|
-
|
|
89
|
-
insights = engine.generate_insights(days=days, repo=repo)
|
|
90
|
-
for i, insight in enumerate(insights, 1):
|
|
91
|
-
render_insight_card(console, insight, i)
|
|
92
|
-
|
|
93
|
-
# ── Section 5: Goals ─────────────────────────────────────────────
|
|
94
|
-
goals = db.get_goals(status="active")
|
|
95
|
-
if goals:
|
|
96
|
-
console.rule("[bold cyan]Active Goals[/bold cyan]")
|
|
97
|
-
console.print()
|
|
98
|
-
goal_table = Table(show_header=True, header_style="bold cyan")
|
|
99
|
-
goal_table.add_column("Goal", style="white")
|
|
100
|
-
goal_table.add_column("Metric")
|
|
101
|
-
goal_table.add_column("Progress", justify="right")
|
|
102
|
-
goal_table.add_column("Deadline")
|
|
103
|
-
for g in goals:
|
|
104
|
-
pct = (g["current_value"] / g["target_value"] * 100) if g["target_value"] > 0 else 0
|
|
105
|
-
color = "green" if pct >= 75 else "yellow" if pct >= 25 else "red"
|
|
106
|
-
goal_table.add_row(
|
|
107
|
-
g["title"],
|
|
108
|
-
g["metric"],
|
|
109
|
-
f"[{color}]{pct:.0%}[/{color}]",
|
|
110
|
-
g.get("deadline", "-"),
|
|
111
|
-
)
|
|
112
|
-
console.print(goal_table)
|
|
113
|
-
|
|
114
|
-
# ── Section 6: Heatmap ───────────────────────────────────────────
|
|
115
|
-
console.rule("[bold cyan]Activity Heatmap[/bold cyan]")
|
|
116
|
-
console.print()
|
|
117
|
-
|
|
118
|
-
heatmap_data = engine.activity_heatmap(author=author, days=90)
|
|
119
|
-
if heatmap_data:
|
|
120
|
-
render_heatmap(console, heatmap_data, days=90)
|
|
121
|
-
else:
|
|
122
|
-
console.print("[dim]No activity data. Run 'devpulse sync' to fetch commits.[/dim]")
|
|
123
|
-
|
|
124
|
-
# ── Section 7: Recommendations ───────────────────────────────────
|
|
125
|
-
console.rule("[bold cyan]Recommendations[/bold cyan]")
|
|
126
|
-
console.print()
|
|
127
|
-
for rec in health.recommendations:
|
|
128
|
-
console.print(f" -> {rec}")
|
|
129
|
-
|
|
130
|
-
console.print()
|
|
131
|
-
console.print("[dim]Run 'devpulse sync' to refresh data | 'devpulse --help' for all commands[/dim]")
|
|
1
|
+
"""Interactive terminal dashboard for DevPulse."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.layout import Layout
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
from devpulse.core.database import Database
|
|
12
|
+
from devpulse.core.analytics import AnalyticsEngine
|
|
13
|
+
from devpulse.core.report_generator import ReportGenerator
|
|
14
|
+
from devpulse.cli.render import render_header, render_heatmap, render_insight_card
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def dashboard_cmd(
|
|
18
|
+
author: Optional[str] = None,
|
|
19
|
+
days: int = 30,
|
|
20
|
+
repo: Optional[str] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Launch the full interactive terminal dashboard."""
|
|
23
|
+
console = Console()
|
|
24
|
+
db = Database()
|
|
25
|
+
engine = AnalyticsEngine(db=db)
|
|
26
|
+
report_gen = ReportGenerator(db=db)
|
|
27
|
+
|
|
28
|
+
render_header(console)
|
|
29
|
+
|
|
30
|
+
# ── Section 1: Summary ───────────────────────────────────────────
|
|
31
|
+
console.rule("[bold cyan]Overview[/bold cyan]")
|
|
32
|
+
|
|
33
|
+
daily = report_gen.daily_report(author=author, repo=repo)
|
|
34
|
+
console.print(f"\n Date: [bold]{daily['date']}[/bold]")
|
|
35
|
+
console.print(f" {daily['summary']}\n")
|
|
36
|
+
|
|
37
|
+
# ── Section 2: Team Metrics ──────────────────────────────────────
|
|
38
|
+
console.rule("[bold cyan]Team Metrics[/bold cyan]")
|
|
39
|
+
console.print()
|
|
40
|
+
|
|
41
|
+
team = engine.team_metrics(days=days, repo=repo)
|
|
42
|
+
if team:
|
|
43
|
+
table = Table(show_header=True, header_style="bold cyan", expand=True)
|
|
44
|
+
table.add_column("Developer", style="white", min_width=15)
|
|
45
|
+
table.add_column("Commits", justify="right")
|
|
46
|
+
table.add_column("C/D", justify="right", style="dim")
|
|
47
|
+
table.add_column("PRs", justify="right")
|
|
48
|
+
table.add_column("Reviews", justify="right")
|
|
49
|
+
table.add_column("Merge(h)", justify="right")
|
|
50
|
+
table.add_column("+/- Lines", justify="right")
|
|
51
|
+
|
|
52
|
+
for m in team[:10]:
|
|
53
|
+
table.add_row(
|
|
54
|
+
m.author,
|
|
55
|
+
str(m.commits_count),
|
|
56
|
+
str(m.commits_per_day),
|
|
57
|
+
str(m.prs_created),
|
|
58
|
+
str(m.reviews_given),
|
|
59
|
+
str(m.avg_pr_merge_time_hours),
|
|
60
|
+
f"+{m.lines_added}/-{m.lines_removed}",
|
|
61
|
+
)
|
|
62
|
+
console.print(table)
|
|
63
|
+
else:
|
|
64
|
+
console.print("[dim]No team data yet. Run 'devpulse sync' to fetch data.[/dim]")
|
|
65
|
+
|
|
66
|
+
# ── Section 3: Team Health ───────────────────────────────────────
|
|
67
|
+
console.rule("[bold cyan]Team Health[/bold cyan]")
|
|
68
|
+
console.print()
|
|
69
|
+
|
|
70
|
+
health = engine.team_health(days=days, repo=repo)
|
|
71
|
+
score_color = "green" if health.overall_score >= 70 else "yellow" if health.overall_score >= 40 else "red"
|
|
72
|
+
console.print(f" Health Score: [{score_color}]{health.overall_score}/100[/{score_color}]")
|
|
73
|
+
console.print(f" Workload Balance: {health.workload_balance:.0%}")
|
|
74
|
+
console.print(f" Collaboration: {health.collaboration_score:.0%}")
|
|
75
|
+
console.print(f" Velocity: {health.velocity_trend}")
|
|
76
|
+
|
|
77
|
+
if health.burnout_risk:
|
|
78
|
+
console.print("\n [bold]Burnout Risk:[/bold]")
|
|
79
|
+
for name, risk in sorted(health.burnout_risk.items(), key=lambda x: x[1], reverse=True):
|
|
80
|
+
color = "red" if risk >= 0.6 else "yellow" if risk >= 0.3 else "green"
|
|
81
|
+
bar_len = int(risk * 20)
|
|
82
|
+
bar = "#" * bar_len + "-" * (20 - bar_len)
|
|
83
|
+
console.print(f" {name:<15} [{color}]{bar}[/{color}] {risk:.0%}")
|
|
84
|
+
|
|
85
|
+
# ── Section 4: AI Insights ───────────────────────────────────────
|
|
86
|
+
console.rule("[bold cyan]AI Insights[/bold cyan]")
|
|
87
|
+
console.print()
|
|
88
|
+
|
|
89
|
+
insights = engine.generate_insights(days=days, repo=repo)
|
|
90
|
+
for i, insight in enumerate(insights, 1):
|
|
91
|
+
render_insight_card(console, insight, i)
|
|
92
|
+
|
|
93
|
+
# ── Section 5: Goals ─────────────────────────────────────────────
|
|
94
|
+
goals = db.get_goals(status="active")
|
|
95
|
+
if goals:
|
|
96
|
+
console.rule("[bold cyan]Active Goals[/bold cyan]")
|
|
97
|
+
console.print()
|
|
98
|
+
goal_table = Table(show_header=True, header_style="bold cyan")
|
|
99
|
+
goal_table.add_column("Goal", style="white")
|
|
100
|
+
goal_table.add_column("Metric")
|
|
101
|
+
goal_table.add_column("Progress", justify="right")
|
|
102
|
+
goal_table.add_column("Deadline")
|
|
103
|
+
for g in goals:
|
|
104
|
+
pct = (g["current_value"] / g["target_value"] * 100) if g["target_value"] > 0 else 0
|
|
105
|
+
color = "green" if pct >= 75 else "yellow" if pct >= 25 else "red"
|
|
106
|
+
goal_table.add_row(
|
|
107
|
+
g["title"],
|
|
108
|
+
g["metric"],
|
|
109
|
+
f"[{color}]{pct:.0%}[/{color}]",
|
|
110
|
+
g.get("deadline", "-"),
|
|
111
|
+
)
|
|
112
|
+
console.print(goal_table)
|
|
113
|
+
|
|
114
|
+
# ── Section 6: Heatmap ───────────────────────────────────────────
|
|
115
|
+
console.rule("[bold cyan]Activity Heatmap[/bold cyan]")
|
|
116
|
+
console.print()
|
|
117
|
+
|
|
118
|
+
heatmap_data = engine.activity_heatmap(author=author, days=90)
|
|
119
|
+
if heatmap_data:
|
|
120
|
+
render_heatmap(console, heatmap_data, days=90)
|
|
121
|
+
else:
|
|
122
|
+
console.print("[dim]No activity data. Run 'devpulse sync' to fetch commits.[/dim]")
|
|
123
|
+
|
|
124
|
+
# ── Section 7: Recommendations ───────────────────────────────────
|
|
125
|
+
console.rule("[bold cyan]Recommendations[/bold cyan]")
|
|
126
|
+
console.print()
|
|
127
|
+
for rec in health.recommendations:
|
|
128
|
+
console.print(f" -> {rec}")
|
|
129
|
+
|
|
130
|
+
console.print()
|
|
131
|
+
console.print("[dim]Run 'devpulse sync' to refresh data | 'devpulse --help' for all commands[/dim]")
|