devpulse-tui 0.1.0__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.
devpulse/__init__.py ADDED
File without changes
devpulse/activity.py ADDED
@@ -0,0 +1,50 @@
1
+ import subprocess
2
+ import time
3
+
4
+
5
+ CODING_KEYWORDS = [
6
+ "code",
7
+ "nvim",
8
+ "vim",
9
+ "pycharm",
10
+ "idea",
11
+ "terminal",
12
+ "konsole",
13
+ "kitty",
14
+ "github",
15
+ ]
16
+
17
+
18
+ session_start = time.time()
19
+
20
+
21
+ def get_active_window():
22
+ try:
23
+ window_name = subprocess.check_output(
24
+ ["xdotool", "getactivewindow", "getwindowname"],
25
+ text=True
26
+ ).strip()
27
+
28
+ return window_name
29
+
30
+ except Exception:
31
+ return "Unknown"
32
+
33
+
34
+ def is_coding(window_name):
35
+ window_name = window_name.lower()
36
+
37
+ return any(
38
+ keyword in window_name
39
+ for keyword in CODING_KEYWORDS
40
+ )
41
+
42
+
43
+ def get_session_duration():
44
+ seconds = int(time.time() - session_start)
45
+
46
+ hours = seconds // 3600
47
+ minutes = (seconds % 3600) // 60
48
+ secs = seconds % 60
49
+
50
+ return f"{hours:02}:{minutes:02}:{secs:02}"
devpulse/database.py ADDED
@@ -0,0 +1,75 @@
1
+ from datetime import datetime
2
+ from sqlalchemy import (
3
+ create_engine,
4
+ Column,
5
+ Integer,
6
+ Float,
7
+ String,
8
+ )
9
+
10
+ from sqlalchemy.orm import (
11
+ declarative_base,
12
+ sessionmaker,
13
+ )
14
+
15
+ Base = declarative_base()
16
+
17
+
18
+ class SystemStat(Base):
19
+ __tablename__ = "system_stats"
20
+
21
+ id = Column(Integer, primary_key=True)
22
+ timestamp = Column(
23
+ String,
24
+ default=lambda: datetime.now().isoformat()
25
+ )
26
+ cpu = Column(Float)
27
+ ram = Column(Float)
28
+ disk = Column(Float)
29
+
30
+ gpu = Column(String)
31
+
32
+ upload = Column(Float)
33
+ download = Column(Float)
34
+
35
+ repo = Column(String)
36
+ branch = Column(String)
37
+
38
+
39
+ engine = create_engine(
40
+ "sqlite:///devpulse.db"
41
+ )
42
+
43
+ SessionLocal = sessionmaker(bind=engine)
44
+
45
+
46
+ def init_db():
47
+ Base.metadata.create_all(engine)
48
+
49
+ def log_system_stats(
50
+ cpu,
51
+ ram,
52
+ disk,
53
+ gpu,
54
+ upload,
55
+ download,
56
+ repo,
57
+ branch,
58
+ ):
59
+ session = SessionLocal()
60
+
61
+ stat = SystemStat(
62
+ cpu=cpu,
63
+ ram=ram,
64
+ disk=disk,
65
+ gpu=gpu,
66
+ upload=upload,
67
+ download=download,
68
+ repo=repo,
69
+ branch=branch,
70
+ )
71
+
72
+ session.add(stat)
73
+ session.commit()
74
+
75
+ session.close()
devpulse/devinfo.py ADDED
@@ -0,0 +1,240 @@
1
+ import os
2
+ import subprocess
3
+ from pathlib import Path
4
+ from datetime import datetime
5
+ from git import Repo
6
+
7
+
8
+ def find_git_repo():
9
+ current = Path.cwd()
10
+
11
+ for parent in [current] + list(current.parents):
12
+ if (parent / ".git").exists():
13
+ return parent
14
+
15
+ return None
16
+
17
+
18
+ def get_git_info():
19
+ try:
20
+ repo = subprocess.check_output(
21
+ ["git", "rev-parse", "--show-toplevel"],
22
+ stderr=subprocess.DEVNULL
23
+ ).decode().strip().split("/")[-1]
24
+
25
+ branch = subprocess.check_output(
26
+ ["git", "branch", "--show-current"],
27
+ stderr=subprocess.DEVNULL
28
+ ).decode().strip()
29
+
30
+ status_output = subprocess.check_output(
31
+ ["git", "status", "--short"],
32
+ stderr=subprocess.DEVNULL
33
+ ).decode().splitlines()
34
+
35
+ commit_msg = subprocess.check_output(
36
+ ["git", "log", "-1", "--pretty=%s"],
37
+ stderr=subprocess.DEVNULL
38
+ ).decode().strip()
39
+
40
+ author = subprocess.check_output(
41
+ ["git", "log", "-1", "--pretty=%an"],
42
+ stderr=subprocess.DEVNULL
43
+ ).decode().strip()
44
+
45
+ commit_age = subprocess.check_output(
46
+ ["git", "log", "-1", "--pretty=%cr"],
47
+ stderr=subprocess.DEVNULL
48
+ ).decode().strip()
49
+
50
+ insertions = 0
51
+ deletions = 0
52
+ untracked = 0
53
+
54
+ for line in status_output:
55
+ if line.startswith("??"):
56
+ untracked += 1
57
+
58
+ diff_stats = subprocess.check_output(
59
+ ["git", "diff", "--shortstat"],
60
+ stderr=subprocess.DEVNULL
61
+ ).decode().strip()
62
+
63
+ if "insertion" in diff_stats:
64
+ insertions = diff_stats.split("insertion")[0].split()[-1]
65
+
66
+ if "deletion" in diff_stats:
67
+ deletions = diff_stats.split("deletion")[0].split()[-1]
68
+
69
+ return {
70
+ "repo": repo,
71
+ "branch": branch,
72
+ "status": "DIRTY" if status_output else "CLEAN",
73
+ "untracked": str(untracked),
74
+ "insertions": f"+{insertions}",
75
+ "deletions": f"-{deletions}",
76
+ "author": author,
77
+ "commit_age": commit_age,
78
+ "last_commit": commit_msg,
79
+ "files_changed": str(len(status_output))
80
+ }
81
+
82
+ except Exception:
83
+ return {
84
+ "repo": "Unknown",
85
+ "branch": "Unknown",
86
+ "status": "Unknown",
87
+ "untracked": "0",
88
+ "insertions": "0",
89
+ "deletions": "0",
90
+ "author": "Unknown",
91
+ "commit_age": "Unknown",
92
+ "last_commit": "Unknown",
93
+ "files_changed": "0"
94
+ }
95
+
96
+ def get_shell():
97
+ return os.environ.get("SHELL", "Unknown")
98
+
99
+
100
+ def get_terminal():
101
+ return os.environ.get("TERM", "Unknown")
102
+
103
+
104
+ def get_current_directory():
105
+ return str(Path.cwd())
106
+
107
+ def get_last_commit():
108
+ repo_path = find_git_repo()
109
+
110
+ if not repo_path:
111
+ return "No Repo"
112
+
113
+ try:
114
+ repo = Repo(repo_path)
115
+
116
+ commit = repo.head.commit.message.strip()
117
+
118
+ return commit[:40]
119
+
120
+ except Exception:
121
+ return "Unknown"
122
+
123
+
124
+ def get_changed_files_count():
125
+ repo_path = find_git_repo()
126
+
127
+ if not repo_path:
128
+ return 0
129
+
130
+ try:
131
+ repo = Repo(repo_path)
132
+
133
+ changed_files = repo.index.diff(None)
134
+
135
+ return len(changed_files)
136
+
137
+ except Exception:
138
+ return 0
139
+
140
+
141
+ def get_repo_status():
142
+ repo_path = find_git_repo()
143
+
144
+ if not repo_path:
145
+ return "NO REPO"
146
+
147
+ try:
148
+ repo = Repo(repo_path)
149
+
150
+ return "DIRTY" if repo.is_dirty() else "CLEAN"
151
+
152
+ except Exception:
153
+ return "UNKNOWN"
154
+
155
+
156
+ def get_untracked_files_count():
157
+ repo_path = find_git_repo()
158
+
159
+ if not repo_path:
160
+ return 0
161
+
162
+ try:
163
+ repo = Repo(repo_path)
164
+
165
+ return len(repo.untracked_files)
166
+
167
+ except Exception:
168
+ return 0
169
+
170
+
171
+ def get_insertions_deletions():
172
+ repo_path = find_git_repo()
173
+
174
+ if not repo_path:
175
+ return 0, 0
176
+
177
+ try:
178
+ repo = Repo(repo_path)
179
+
180
+ diff = repo.git.diff("--shortstat")
181
+
182
+ insertions = 0
183
+ deletions = 0
184
+
185
+ import re
186
+
187
+ insert_match = re.search(r'(\d+) insertion', diff)
188
+ delete_match = re.search(r'(\d+) deletion', diff)
189
+
190
+ if insert_match:
191
+ insertions = int(insert_match.group(1))
192
+
193
+ if delete_match:
194
+ deletions = int(delete_match.group(1))
195
+
196
+ return insertions, deletions
197
+
198
+ except Exception:
199
+ return 0, 0
200
+
201
+
202
+ def get_last_commit_author():
203
+ repo_path = find_git_repo()
204
+
205
+ if not repo_path:
206
+ return "Unknown"
207
+
208
+ try:
209
+ repo = Repo(repo_path)
210
+
211
+ return repo.head.commit.author.name
212
+
213
+ except Exception:
214
+ return "Unknown"
215
+
216
+
217
+ def get_last_commit_age():
218
+ repo_path = find_git_repo()
219
+
220
+ if not repo_path:
221
+ return "Unknown"
222
+
223
+ try:
224
+ repo = Repo(repo_path)
225
+
226
+ commit_time = datetime.fromtimestamp(
227
+ repo.head.commit.committed_date
228
+ )
229
+
230
+ delta = datetime.now() - commit_time
231
+
232
+ hours = delta.seconds // 3600
233
+
234
+ if delta.days > 0:
235
+ return f"{delta.days}d ago"
236
+
237
+ return f"{hours}h ago"
238
+
239
+ except Exception:
240
+ return "Unknown"
devpulse/doctor.py ADDED
@@ -0,0 +1,71 @@
1
+ import os
2
+ import sqlite3
3
+ import importlib.util
4
+
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+
8
+
9
+ console = Console()
10
+
11
+
12
+ def check_package(package_name):
13
+ return importlib.util.find_spec(package_name) is not None
14
+
15
+
16
+ def run_doctor():
17
+ table = Table(title="DevPulse Doctor")
18
+
19
+ table.add_column("Check", style="cyan")
20
+ table.add_column("Status", style="green")
21
+
22
+ # Database check
23
+ db_exists = os.path.exists("devpulse.db")
24
+
25
+ table.add_row(
26
+ "SQLite Database",
27
+ "OK" if db_exists else "MISSING"
28
+ )
29
+
30
+ # SQLite readable
31
+ try:
32
+ conn = sqlite3.connect("devpulse.db")
33
+ conn.execute("SELECT 1")
34
+ conn.close()
35
+
36
+ table.add_row(
37
+ "Database Access",
38
+ "OK"
39
+ )
40
+
41
+ except Exception:
42
+ table.add_row(
43
+ "Database Access",
44
+ "FAILED"
45
+ )
46
+
47
+ # Dependencies
48
+ dependencies = [
49
+ "psutil",
50
+ "rich",
51
+ "sqlalchemy",
52
+ "git",
53
+ ]
54
+
55
+ for dep in dependencies:
56
+ installed = check_package(dep)
57
+
58
+ table.add_row(
59
+ f"Dependency: {dep}",
60
+ "OK" if installed else "MISSING"
61
+ )
62
+
63
+ # NVIDIA support
64
+ nvidia_available = check_package("py3nvml")
65
+
66
+ table.add_row(
67
+ "NVIDIA Support",
68
+ "ENABLED" if nvidia_available else "DISABLED"
69
+ )
70
+
71
+ console.print(table)