monitorify 1.0.1__tar.gz → 1.0.2__tar.gz
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.
- monitorify-1.0.2/PKG-INFO +62 -0
- monitorify-1.0.2/README.md +50 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/pyproject.toml +1 -1
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/__init__.py +1 -1
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/collector/manager.py +2 -2
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/collector/worker.py +14 -3
- monitorify-1.0.2/src/monitorify/config/__init__.py +29 -0
- monitorify-1.0.2/src/monitorify/config/configManager.py +47 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/config.py +3 -0
- monitorify-1.0.2/src/monitorify/config_manager.py +47 -0
- monitorify-1.0.2/src/monitorify/daemon.py +46 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/main.py +1 -2
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/components/menu.py +29 -2
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/tui.py +36 -5
- monitorify-1.0.2/src/monitorify.egg-info/PKG-INFO +62 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify.egg-info/SOURCES.txt +3 -0
- monitorify-1.0.1/PKG-INFO +0 -52
- monitorify-1.0.1/README.md +0 -40
- monitorify-1.0.1/src/monitorify/daemon.py +0 -32
- monitorify-1.0.1/src/monitorify.egg-info/PKG-INFO +0 -52
- {monitorify-1.0.1 → monitorify-1.0.2}/setup.cfg +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/__main__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/collector/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/collector/schema.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/remote/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/remote/connector.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/DiskStats.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/cpuStats.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/networkStats.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/programmList.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/stats/ramStats.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/storage/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/storage/db.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/components/widgetManager.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/css/tui.css +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/__init__.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/brailleGraph.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/cpuWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/diskWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/networkWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/procInfoWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/programmListWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/ramWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify/ui/tui/widgets/statusWidget.py +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify.egg-info/dependency_links.txt +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify.egg-info/entry_points.txt +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify.egg-info/requires.txt +0 -0
- {monitorify-1.0.1 → monitorify-1.0.2}/src/monitorify.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: monitorify
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A lightweight Linux terminal system monitor built with Textual
|
|
5
|
+
Author: Leon
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: textual>=0.80.0
|
|
10
|
+
Requires-Dist: platformdirs>=4.0.0
|
|
11
|
+
Requires-Dist: rich>=13.0.0
|
|
12
|
+
|
|
13
|
+
# Monitorify
|
|
14
|
+
### A terminal montioring tool for linux
|
|
15
|
+
|
|
16
|
+
- Features
|
|
17
|
+
- CPU monitoring
|
|
18
|
+
- RAM monitoring
|
|
19
|
+
- Network monitoring
|
|
20
|
+
- Disk monitoring (I/O and storage capacity)
|
|
21
|
+
- Process monitoring
|
|
22
|
+
- Background daemon
|
|
23
|
+
- Customization menu (toggle widgets, set intervals, and change history window)
|
|
24
|
+
- Historical graphs
|
|
25
|
+
## Installation
|
|
26
|
+
### Using pipx
|
|
27
|
+
```bash
|
|
28
|
+
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Using pip
|
|
32
|
+
```bash
|
|
33
|
+
pip install git+https://github.com/leonst036/Monitorify.git
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Using source
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/leonst036/Monitorify.git
|
|
39
|
+
cd Monitorify
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
```bash
|
|
45
|
+
monitorify
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Tech Stack
|
|
49
|
+
|
|
50
|
+
* **Language & TUI:** Python, Textual
|
|
51
|
+
* **Database:** SQLite
|
|
52
|
+
* **Target OS:** Linux
|
|
53
|
+
|
|
54
|
+
## Remote
|
|
55
|
+
|
|
56
|
+
To use Monitorify remotely over SSH on your server, you can set the following parameters:
|
|
57
|
+
|
|
58
|
+
- `--ip` – Set the target IP address.
|
|
59
|
+
- `--port` – Set the SSH port.
|
|
60
|
+
- `--username` – Set the username for the remote host.
|
|
61
|
+
- `--password` – Set the password for the SSH server.
|
|
62
|
+
- `--key_filename` – Set the path to an SSH key *(optional)*.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Monitorify
|
|
2
|
+
### A terminal montioring tool for linux
|
|
3
|
+
|
|
4
|
+
- Features
|
|
5
|
+
- CPU monitoring
|
|
6
|
+
- RAM monitoring
|
|
7
|
+
- Network monitoring
|
|
8
|
+
- Disk monitoring (I/O and storage capacity)
|
|
9
|
+
- Process monitoring
|
|
10
|
+
- Background daemon
|
|
11
|
+
- Customization menu (toggle widgets, set intervals, and change history window)
|
|
12
|
+
- Historical graphs
|
|
13
|
+
## Installation
|
|
14
|
+
### Using pipx
|
|
15
|
+
```bash
|
|
16
|
+
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Using pip
|
|
20
|
+
```bash
|
|
21
|
+
pip install git+https://github.com/leonst036/Monitorify.git
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Using source
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/leonst036/Monitorify.git
|
|
27
|
+
cd Monitorify
|
|
28
|
+
pip install .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
```bash
|
|
33
|
+
monitorify
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Tech Stack
|
|
37
|
+
|
|
38
|
+
* **Language & TUI:** Python, Textual
|
|
39
|
+
* **Database:** SQLite
|
|
40
|
+
* **Target OS:** Linux
|
|
41
|
+
|
|
42
|
+
## Remote
|
|
43
|
+
|
|
44
|
+
To use Monitorify remotely over SSH on your server, you can set the following parameters:
|
|
45
|
+
|
|
46
|
+
- `--ip` – Set the target IP address.
|
|
47
|
+
- `--port` – Set the SSH port.
|
|
48
|
+
- `--username` – Set the username for the remote host.
|
|
49
|
+
- `--password` – Set the password for the SSH server.
|
|
50
|
+
- `--key_filename` – Set the path to an SSH key *(optional)*.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import List, Optional, Any, Callable
|
|
1
|
+
from typing import List, Optional, Any, Callable, Union
|
|
2
2
|
from monitorify.collector.worker import Worker
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Manager:
|
|
6
|
-
def __init__(self, db: Optional[Any] = None, interval: float = 1.0):
|
|
6
|
+
def __init__(self, db: Optional[Any] = None, interval: Union[float, Callable[[], float]] = 1.0):
|
|
7
7
|
self._workers: List[Worker] = []
|
|
8
8
|
self._interval = interval
|
|
9
9
|
if db is None:
|
|
@@ -3,10 +3,10 @@ from threading import Thread
|
|
|
3
3
|
from monitorify.stats import cpuStats, ramStats, networkStats, programmList, DiskStats
|
|
4
4
|
from monitorify.collector.schema import MetricSnapshot
|
|
5
5
|
|
|
6
|
-
from typing import Callable, Optional
|
|
6
|
+
from typing import Callable, Optional, Union
|
|
7
7
|
|
|
8
8
|
class Worker:
|
|
9
|
-
def __init__(self, interval: float = 1.0, on_data: Optional[Callable[[MetricSnapshot], None]] = None):
|
|
9
|
+
def __init__(self, interval: Union[float, Callable[[], float]] = 1.0, on_data: Optional[Callable[[MetricSnapshot], None]] = None):
|
|
10
10
|
self._interval = interval
|
|
11
11
|
self._on_data = on_data
|
|
12
12
|
self._running = False
|
|
@@ -22,6 +22,17 @@ class Worker:
|
|
|
22
22
|
if self._thread:
|
|
23
23
|
self._thread.join()
|
|
24
24
|
|
|
25
|
+
def _get_interval(self) -> float:
|
|
26
|
+
if callable(self._interval):
|
|
27
|
+
try:
|
|
28
|
+
return float(self._interval())
|
|
29
|
+
except Exception:
|
|
30
|
+
return 1.0
|
|
31
|
+
try:
|
|
32
|
+
return float(self._interval)
|
|
33
|
+
except Exception:
|
|
34
|
+
return 1.0
|
|
35
|
+
|
|
25
36
|
def _worker_loop(self):
|
|
26
37
|
while self._running:
|
|
27
38
|
snapshot = self.collect()
|
|
@@ -30,7 +41,7 @@ class Worker:
|
|
|
30
41
|
self._on_data(snapshot)
|
|
31
42
|
except Exception as e:
|
|
32
43
|
print(f"Error handling metric snapshot: {e}")
|
|
33
|
-
time.sleep(self.
|
|
44
|
+
time.sleep(max(0.1, self._get_interval()))
|
|
34
45
|
|
|
35
46
|
def collect(self) -> MetricSnapshot:
|
|
36
47
|
return MetricSnapshot(
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from platformdirs import user_data_dir, user_runtime_dir
|
|
4
|
+
|
|
5
|
+
# Data directory for database storage
|
|
6
|
+
DATA_DIR = Path(user_data_dir("monitorify", appauthor=False))
|
|
7
|
+
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
8
|
+
|
|
9
|
+
# Runtime directory for PID files
|
|
10
|
+
try:
|
|
11
|
+
RUNTIME_DIR = Path(user_runtime_dir("monitorify", appauthor=False))
|
|
12
|
+
RUNTIME_DIR.mkdir(parents=True, exist_ok=True)
|
|
13
|
+
except Exception:
|
|
14
|
+
RUNTIME_DIR = DATA_DIR
|
|
15
|
+
|
|
16
|
+
# Database settings
|
|
17
|
+
DB_PATH = os.getenv("MONITORIFY_DB_PATH", str(DATA_DIR / "monitorify.db"))
|
|
18
|
+
|
|
19
|
+
# User configuration file path
|
|
20
|
+
CONFIG_PATH = Path(os.getenv("MONITORIFY_CONFIG_PATH", str(DATA_DIR / "config.json")))
|
|
21
|
+
|
|
22
|
+
# Daemon PID file path
|
|
23
|
+
DAEMON_PIDFILE = Path(os.getenv("MONITORIFY_PID_PATH", str(RUNTIME_DIR / "daemon.pid")))
|
|
24
|
+
|
|
25
|
+
# Collection settings (seconds)
|
|
26
|
+
DEFAULT_COLLECTION_INTERVAL = float(os.getenv("MONITORIFY_INTERVAL", "1.0"))
|
|
27
|
+
|
|
28
|
+
# Retention settings (default: 7 days in seconds)
|
|
29
|
+
RETENTION_SECONDS = float(os.getenv("MONITORIFY_RETENTION", str(7 * 24 * 3600)))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class configManager:
|
|
6
|
+
def __init__(self, config_path: str):
|
|
7
|
+
self.config_path = config_path
|
|
8
|
+
self.config_data = {}
|
|
9
|
+
self.load_config()
|
|
10
|
+
|
|
11
|
+
def load_config(self) -> dict:
|
|
12
|
+
path = Path(self.config_path)
|
|
13
|
+
if not path.exists():
|
|
14
|
+
self.config_data = {}
|
|
15
|
+
return self.config_data
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
with path.open("r", encoding="utf-8") as file:
|
|
19
|
+
data = json.load(file)
|
|
20
|
+
if isinstance(data, dict):
|
|
21
|
+
self.config_data = data
|
|
22
|
+
else:
|
|
23
|
+
self.config_data = {}
|
|
24
|
+
except (json.JSONDecodeError, OSError):
|
|
25
|
+
self.config_data = {}
|
|
26
|
+
|
|
27
|
+
return self.config_data
|
|
28
|
+
|
|
29
|
+
def save_config(self, config_data: dict):
|
|
30
|
+
try:
|
|
31
|
+
path = Path(self.config_path)
|
|
32
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
with path.open("w", encoding="utf-8") as file:
|
|
34
|
+
json.dump(config_data, file, indent=4)
|
|
35
|
+
self.config_data = config_data
|
|
36
|
+
except Exception as e:
|
|
37
|
+
print(f"Error saving configuration: {e}")
|
|
38
|
+
|
|
39
|
+
def get(self, key: str, default=None):
|
|
40
|
+
return self.config_data.get(key, default)
|
|
41
|
+
|
|
42
|
+
def get_connfig(self, key: str):
|
|
43
|
+
return self.get(key, None)
|
|
44
|
+
|
|
45
|
+
def set(self, key: str, value):
|
|
46
|
+
self.config_data[key] = value
|
|
47
|
+
self.save_config(self.config_data)
|
|
@@ -16,6 +16,9 @@ except Exception:
|
|
|
16
16
|
# Database settings
|
|
17
17
|
DB_PATH = os.getenv("MONITORIFY_DB_PATH", str(DATA_DIR / "monitorify.db"))
|
|
18
18
|
|
|
19
|
+
# User configuration file path
|
|
20
|
+
CONFIG_PATH = Path(os.getenv("MONITORIFY_CONFIG_PATH", str(DATA_DIR / "config.json")))
|
|
21
|
+
|
|
19
22
|
# Daemon PID file path
|
|
20
23
|
DAEMON_PIDFILE = Path(os.getenv("MONITORIFY_PID_PATH", str(RUNTIME_DIR / "daemon.pid")))
|
|
21
24
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class configManager:
|
|
6
|
+
def __init__(self, config_path: str):
|
|
7
|
+
self.config_path = config_path
|
|
8
|
+
self.config_data = {}
|
|
9
|
+
self.load_config()
|
|
10
|
+
|
|
11
|
+
def load_config(self) -> dict:
|
|
12
|
+
path = Path(self.config_path)
|
|
13
|
+
if not path.exists():
|
|
14
|
+
self.config_data = {}
|
|
15
|
+
return self.config_data
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
with path.open("r", encoding="utf-8") as file:
|
|
19
|
+
data = json.load(file)
|
|
20
|
+
if isinstance(data, dict):
|
|
21
|
+
self.config_data = data
|
|
22
|
+
else:
|
|
23
|
+
self.config_data = {}
|
|
24
|
+
except (json.JSONDecodeError, OSError):
|
|
25
|
+
self.config_data = {}
|
|
26
|
+
|
|
27
|
+
return self.config_data
|
|
28
|
+
|
|
29
|
+
def save_config(self, config_data: dict):
|
|
30
|
+
try:
|
|
31
|
+
path = Path(self.config_path)
|
|
32
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
with path.open("w", encoding="utf-8") as file:
|
|
34
|
+
json.dump(config_data, file, indent=4)
|
|
35
|
+
self.config_data = config_data
|
|
36
|
+
except Exception as e:
|
|
37
|
+
print(f"Error saving configuration: {e}")
|
|
38
|
+
|
|
39
|
+
def get(self, key: str, default=None):
|
|
40
|
+
return self.config_data.get(key, default)
|
|
41
|
+
|
|
42
|
+
def get_connfig(self, key: str):
|
|
43
|
+
return self.get(key, None)
|
|
44
|
+
|
|
45
|
+
def set(self, key: str, value):
|
|
46
|
+
self.config_data[key] = value
|
|
47
|
+
self.save_config(self.config_data)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import signal
|
|
3
|
+
import sys
|
|
4
|
+
from monitorify.collector import Manager
|
|
5
|
+
from monitorify.storage import Database
|
|
6
|
+
from monitorify import config
|
|
7
|
+
from monitorify.config.configManager import configManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_collection_interval() -> float:
|
|
11
|
+
try:
|
|
12
|
+
cfg = configManager(str(config.CONFIG_PATH))
|
|
13
|
+
for key in ("update_interval", "collection_interval", "interval"):
|
|
14
|
+
val = cfg.get(key)
|
|
15
|
+
if val is not None:
|
|
16
|
+
return float(val)
|
|
17
|
+
except Exception:
|
|
18
|
+
pass
|
|
19
|
+
return config.DEFAULT_COLLECTION_INTERVAL
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
print("Starting Monitorify background metrics collector daemon...")
|
|
24
|
+
db = Database(config.DB_PATH)
|
|
25
|
+
interval = get_collection_interval()
|
|
26
|
+
manager = Manager(db=db, interval=get_collection_interval)
|
|
27
|
+
|
|
28
|
+
def signal_handler(sig, frame):
|
|
29
|
+
print("\nStopping Monitorify daemon...")
|
|
30
|
+
manager.stop()
|
|
31
|
+
sys.exit(0)
|
|
32
|
+
|
|
33
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
34
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
35
|
+
|
|
36
|
+
manager.start()
|
|
37
|
+
print(
|
|
38
|
+
f"Metrics collector running (Interval: {interval}s, DB: {config.DB_PATH}). Press Ctrl+C to stop."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
while True:
|
|
42
|
+
time.sleep(1)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -13,10 +13,9 @@ def parse_args():
|
|
|
13
13
|
parser.add_argument("--username", type=str, default=None, help="Username for the remote machine")
|
|
14
14
|
parser.add_argument("--password", type=str, default=None, help="Password for the remote machine")
|
|
15
15
|
parser.add_argument("--key_filename", type=str, default=None, help="Path to the key file for the remote machine")
|
|
16
|
-
parser.add_argument("--command", type=str, default=None, help="Command to run on the remote machine (default: monitorify)")
|
|
17
16
|
args = parser.parse_args()
|
|
18
17
|
|
|
19
|
-
if not args.ip and (args.port is not None or args.username or args.password or args.key_filename
|
|
18
|
+
if not args.ip and (args.port is not None or args.username or args.password or args.key_filename):
|
|
20
19
|
parser.error("--ip is required when specifying remote connection arguments")
|
|
21
20
|
|
|
22
21
|
return args
|
|
@@ -41,6 +41,10 @@ def parse_duration(text: str) -> float | None:
|
|
|
41
41
|
|
|
42
42
|
class Menu(Container):
|
|
43
43
|
|
|
44
|
+
def __init__(self, *args, **kwargs):
|
|
45
|
+
super().__init__(*args, **kwargs)
|
|
46
|
+
self._syncing_menu_state = False
|
|
47
|
+
|
|
44
48
|
def compose(self):
|
|
45
49
|
with Vertical(id="menu"):
|
|
46
50
|
yield Static("[bold #61afef]M[/bold #61afef]enu", id="menu_title")
|
|
@@ -71,16 +75,25 @@ class Menu(Container):
|
|
|
71
75
|
|
|
72
76
|
def populate_menu(self) -> None:
|
|
73
77
|
"""Populates selection list with widget options and updates interval select value."""
|
|
78
|
+
self._syncing_menu_state = True
|
|
74
79
|
try:
|
|
75
80
|
selection_list = self.query_one(SelectionList)
|
|
76
81
|
selection_list.clear_options()
|
|
77
|
-
|
|
82
|
+
saved_visible = self.app.config_manager.get("visible_widgets")
|
|
83
|
+
widgets = self.get_available_widgets()
|
|
84
|
+
for name, widget_id in widgets.items():
|
|
78
85
|
widget = self.app.query_one(f"#{widget_id}")
|
|
86
|
+
if saved_visible is not None:
|
|
87
|
+
initial_state = widget_id in saved_visible
|
|
88
|
+
else:
|
|
89
|
+
initial_state = widget.display
|
|
79
90
|
selection_list.add_option(
|
|
80
|
-
Selection(prompt=name, value=name, initial_state=
|
|
91
|
+
Selection(prompt=name, value=name, initial_state=initial_state)
|
|
81
92
|
)
|
|
82
93
|
except Exception:
|
|
83
94
|
pass
|
|
95
|
+
finally:
|
|
96
|
+
self._syncing_menu_state = False
|
|
84
97
|
|
|
85
98
|
try:
|
|
86
99
|
interval_select = self.query_one("#interval_select", Select)
|
|
@@ -90,6 +103,16 @@ class Menu(Container):
|
|
|
90
103
|
except Exception:
|
|
91
104
|
pass
|
|
92
105
|
|
|
106
|
+
try:
|
|
107
|
+
history_select = self.query_one("#history_select", Select)
|
|
108
|
+
current_history = getattr(self.app, "history_duration", 60.0)
|
|
109
|
+
if history_select.value != current_history:
|
|
110
|
+
history_select.value = current_history
|
|
111
|
+
custom_input = self.query_one("#custom_history_input", Input)
|
|
112
|
+
custom_input.display = history_select.value is None
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
|
|
93
116
|
def on_select_changed(self, event: Select.Changed) -> None:
|
|
94
117
|
"""Handles changes in the update interval and history duration selectors."""
|
|
95
118
|
if event.select.id == "interval_select" and event.value is not Select.BLANK:
|
|
@@ -124,6 +147,8 @@ class Menu(Container):
|
|
|
124
147
|
|
|
125
148
|
def on_selection_list_selected_changed(self, event: SelectionList.SelectedChanged) -> None:
|
|
126
149
|
"""Toggles widget visibility based on selection changes."""
|
|
150
|
+
if self._syncing_menu_state:
|
|
151
|
+
return
|
|
127
152
|
selected_names = event.selection_list.selected
|
|
128
153
|
for name, widget_id in self.get_available_widgets().items():
|
|
129
154
|
try:
|
|
@@ -133,6 +158,8 @@ class Menu(Container):
|
|
|
133
158
|
pass
|
|
134
159
|
if hasattr(self.app, "update_layout"):
|
|
135
160
|
self.app.update_layout()
|
|
161
|
+
if hasattr(self.app, "persist_config"):
|
|
162
|
+
self.app.persist_config()
|
|
136
163
|
|
|
137
164
|
def get_available_widgets(self) -> dict[str, str]:
|
|
138
165
|
"""Returns a map of {widget_name: widget_id} from main_container."""
|
|
@@ -16,6 +16,7 @@ from monitorify.ui.tui.components.widgetManager import WidgetManager
|
|
|
16
16
|
from monitorify.ui.tui.widgets.programmListWidget import ProgrammListWidget
|
|
17
17
|
from monitorify.storage import Database
|
|
18
18
|
from monitorify import config
|
|
19
|
+
from monitorify.config.configManager import configManager
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class MonitorifyApp(App):
|
|
@@ -30,13 +31,30 @@ class MonitorifyApp(App):
|
|
|
30
31
|
|
|
31
32
|
def __init__(self, **kwargs):
|
|
32
33
|
super().__init__(**kwargs)
|
|
34
|
+
self.config_manager = configManager(str(config.CONFIG_PATH))
|
|
33
35
|
# Read-only DB connection
|
|
34
36
|
self.db = Database(config.DB_PATH)
|
|
35
37
|
self.db.connect()
|
|
36
|
-
#
|
|
37
|
-
self.
|
|
38
|
+
# Load saved preferences from config
|
|
39
|
+
self.update_interval = float(self.config_manager.get("update_interval", 1.0))
|
|
40
|
+
self.history_duration: float = float(self.config_manager.get("history_duration", 60.0))
|
|
38
41
|
self._last_graph_refresh: float = 0.0
|
|
39
42
|
|
|
43
|
+
def persist_config(self) -> None:
|
|
44
|
+
visible_widgets = []
|
|
45
|
+
for widget_id in ["cpu_widget", "ram_widget", "programmList_widget", "network_widget", "disk_widget"]:
|
|
46
|
+
try:
|
|
47
|
+
if self.query_one(f"#{widget_id}").display:
|
|
48
|
+
visible_widgets.append(widget_id)
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
self.config_manager.save_config({
|
|
53
|
+
"update_interval": float(self.update_interval),
|
|
54
|
+
"history_duration": float(self.history_duration),
|
|
55
|
+
"visible_widgets": visible_widgets,
|
|
56
|
+
})
|
|
57
|
+
|
|
40
58
|
def compose(self) -> ComposeResult:
|
|
41
59
|
with Container(id="main_container"):
|
|
42
60
|
yield CpuWidget(id="cpu_widget")
|
|
@@ -52,15 +70,25 @@ class MonitorifyApp(App):
|
|
|
52
70
|
def on_mount(self) -> None:
|
|
53
71
|
self.action_get_window_size()
|
|
54
72
|
self.widget_manager = WidgetManager(self.query_one("#main_container"))
|
|
73
|
+
saved_visible = self.config_manager.get("visible_widgets")
|
|
74
|
+
if saved_visible is not None:
|
|
75
|
+
visible_widgets = set(saved_visible)
|
|
76
|
+
else:
|
|
77
|
+
visible_widgets = set()
|
|
78
|
+
for widget_id in ["cpu_widget", "ram_widget", "programmList_widget", "network_widget", "disk_widget"]:
|
|
79
|
+
try:
|
|
80
|
+
widget = self.query_one(f"#{widget_id}")
|
|
81
|
+
widget.display = widget_id in visible_widgets if saved_visible is not None else widget.display
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
55
84
|
self.update_layout()
|
|
56
|
-
self.update_interval = 1.0
|
|
57
85
|
# Load history from DB on startup
|
|
58
86
|
self.call_after_refresh(self._load_initial_history)
|
|
59
87
|
self.update_timer = self.set_interval(self.update_interval, self.update_display)
|
|
60
88
|
|
|
61
89
|
def _load_initial_history(self) -> None:
|
|
62
90
|
"""Load history from DB after the first render so widgets have their sizes."""
|
|
63
|
-
self.set_history_duration(self.history_duration)
|
|
91
|
+
self.set_history_duration(self.history_duration, persist=False)
|
|
64
92
|
|
|
65
93
|
def set_update_interval(self, seconds: float) -> None:
|
|
66
94
|
"""Updates the graph refresh interval."""
|
|
@@ -68,10 +96,13 @@ class MonitorifyApp(App):
|
|
|
68
96
|
if hasattr(self, "update_timer") and self.update_timer:
|
|
69
97
|
self.update_timer.stop()
|
|
70
98
|
self.update_timer = self.set_interval(self.update_interval, self.update_display)
|
|
99
|
+
self.persist_config()
|
|
71
100
|
|
|
72
|
-
def set_history_duration(self, seconds: float) -> None:
|
|
101
|
+
def set_history_duration(self, seconds: float, persist: bool = True) -> None:
|
|
73
102
|
"""Load historical metrics from DB and push them into all graph widgets."""
|
|
74
103
|
self.history_duration = seconds
|
|
104
|
+
if persist:
|
|
105
|
+
self.persist_config()
|
|
75
106
|
start_time = time.time() - seconds
|
|
76
107
|
snapshots = self.db.get_history(limit=10000, start_time=start_time)
|
|
77
108
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: monitorify
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A lightweight Linux terminal system monitor built with Textual
|
|
5
|
+
Author: Leon
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: textual>=0.80.0
|
|
10
|
+
Requires-Dist: platformdirs>=4.0.0
|
|
11
|
+
Requires-Dist: rich>=13.0.0
|
|
12
|
+
|
|
13
|
+
# Monitorify
|
|
14
|
+
### A terminal montioring tool for linux
|
|
15
|
+
|
|
16
|
+
- Features
|
|
17
|
+
- CPU monitoring
|
|
18
|
+
- RAM monitoring
|
|
19
|
+
- Network monitoring
|
|
20
|
+
- Disk monitoring (I/O and storage capacity)
|
|
21
|
+
- Process monitoring
|
|
22
|
+
- Background daemon
|
|
23
|
+
- Customization menu (toggle widgets, set intervals, and change history window)
|
|
24
|
+
- Historical graphs
|
|
25
|
+
## Installation
|
|
26
|
+
### Using pipx
|
|
27
|
+
```bash
|
|
28
|
+
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Using pip
|
|
32
|
+
```bash
|
|
33
|
+
pip install git+https://github.com/leonst036/Monitorify.git
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Using source
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/leonst036/Monitorify.git
|
|
39
|
+
cd Monitorify
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
```bash
|
|
45
|
+
monitorify
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Tech Stack
|
|
49
|
+
|
|
50
|
+
* **Language & TUI:** Python, Textual
|
|
51
|
+
* **Database:** SQLite
|
|
52
|
+
* **Target OS:** Linux
|
|
53
|
+
|
|
54
|
+
## Remote
|
|
55
|
+
|
|
56
|
+
To use Monitorify remotely over SSH on your server, you can set the following parameters:
|
|
57
|
+
|
|
58
|
+
- `--ip` – Set the target IP address.
|
|
59
|
+
- `--port` – Set the SSH port.
|
|
60
|
+
- `--username` – Set the username for the remote host.
|
|
61
|
+
- `--password` – Set the password for the SSH server.
|
|
62
|
+
- `--key_filename` – Set the path to an SSH key *(optional)*.
|
|
@@ -3,6 +3,7 @@ pyproject.toml
|
|
|
3
3
|
src/monitorify/__init__.py
|
|
4
4
|
src/monitorify/__main__.py
|
|
5
5
|
src/monitorify/config.py
|
|
6
|
+
src/monitorify/config_manager.py
|
|
6
7
|
src/monitorify/daemon.py
|
|
7
8
|
src/monitorify/main.py
|
|
8
9
|
src/monitorify.egg-info/PKG-INFO
|
|
@@ -15,6 +16,8 @@ src/monitorify/collector/__init__.py
|
|
|
15
16
|
src/monitorify/collector/manager.py
|
|
16
17
|
src/monitorify/collector/schema.py
|
|
17
18
|
src/monitorify/collector/worker.py
|
|
19
|
+
src/monitorify/config/__init__.py
|
|
20
|
+
src/monitorify/config/configManager.py
|
|
18
21
|
src/monitorify/remote/__init__.py
|
|
19
22
|
src/monitorify/remote/connector.py
|
|
20
23
|
src/monitorify/stats/DiskStats.py
|
monitorify-1.0.1/PKG-INFO
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: monitorify
|
|
3
|
-
Version: 1.0.1
|
|
4
|
-
Summary: A lightweight Linux terminal system monitor built with Textual
|
|
5
|
-
Author: Leon
|
|
6
|
-
License: MIT
|
|
7
|
-
Requires-Python: >=3.10
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: textual>=0.80.0
|
|
10
|
-
Requires-Dist: platformdirs>=4.0.0
|
|
11
|
-
Requires-Dist: rich>=13.0.0
|
|
12
|
-
|
|
13
|
-
# Monitorify
|
|
14
|
-
### A terminal montioring tool for linux
|
|
15
|
-
|
|
16
|
-
## Features
|
|
17
|
-
### CPU monitoring
|
|
18
|
-
### RAM monitoring
|
|
19
|
-
### Network monitoring
|
|
20
|
-
### Disk monitoring (I/O and storage capacity)
|
|
21
|
-
### Process monitoring
|
|
22
|
-
### Background daemon
|
|
23
|
-
### Customization menu (toggle widgets, set intervals, and change history window)
|
|
24
|
-
### Historical graphs
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
### Using pipx
|
|
28
|
-
```bash
|
|
29
|
-
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Using pip
|
|
33
|
-
```bash
|
|
34
|
-
pip install git+https://github.com/leonst036/Monitorify.git
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Using source
|
|
38
|
-
```bash
|
|
39
|
-
git clone https://github.com/leonst036/Monitorify.git
|
|
40
|
-
cd Monitorify
|
|
41
|
-
pip install .
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
```bash
|
|
46
|
-
monitorify
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Tech stack
|
|
50
|
-
### Python | Textual
|
|
51
|
-
### SQLite
|
|
52
|
-
### Linux
|
monitorify-1.0.1/README.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Monitorify
|
|
2
|
-
### A terminal montioring tool for linux
|
|
3
|
-
|
|
4
|
-
## Features
|
|
5
|
-
### CPU monitoring
|
|
6
|
-
### RAM monitoring
|
|
7
|
-
### Network monitoring
|
|
8
|
-
### Disk monitoring (I/O and storage capacity)
|
|
9
|
-
### Process monitoring
|
|
10
|
-
### Background daemon
|
|
11
|
-
### Customization menu (toggle widgets, set intervals, and change history window)
|
|
12
|
-
### Historical graphs
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
### Using pipx
|
|
16
|
-
```bash
|
|
17
|
-
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### Using pip
|
|
21
|
-
```bash
|
|
22
|
-
pip install git+https://github.com/leonst036/Monitorify.git
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### Using source
|
|
26
|
-
```bash
|
|
27
|
-
git clone https://github.com/leonst036/Monitorify.git
|
|
28
|
-
cd Monitorify
|
|
29
|
-
pip install .
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
```bash
|
|
34
|
-
monitorify
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Tech stack
|
|
38
|
-
### Python | Textual
|
|
39
|
-
### SQLite
|
|
40
|
-
### Linux
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import signal
|
|
3
|
-
import sys
|
|
4
|
-
from monitorify.collector import Manager
|
|
5
|
-
from monitorify.storage import Database
|
|
6
|
-
from monitorify import config
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def main():
|
|
10
|
-
print("Starting Monitorify background metrics collector daemon...")
|
|
11
|
-
db = Database(config.DB_PATH)
|
|
12
|
-
manager = Manager(db=db, interval=config.DEFAULT_COLLECTION_INTERVAL)
|
|
13
|
-
|
|
14
|
-
def signal_handler(sig, frame):
|
|
15
|
-
print("\nStopping Monitorify daemon...")
|
|
16
|
-
manager.stop()
|
|
17
|
-
sys.exit(0)
|
|
18
|
-
|
|
19
|
-
signal.signal(signal.SIGINT, signal_handler)
|
|
20
|
-
signal.signal(signal.SIGTERM, signal_handler)
|
|
21
|
-
|
|
22
|
-
manager.start()
|
|
23
|
-
print(
|
|
24
|
-
f"Metrics collector running (Interval: {config.DEFAULT_COLLECTION_INTERVAL}s, DB: {config.DB_PATH}). Press Ctrl+C to stop."
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
while True:
|
|
28
|
-
time.sleep(1)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if __name__ == "__main__":
|
|
32
|
-
main()
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: monitorify
|
|
3
|
-
Version: 1.0.1
|
|
4
|
-
Summary: A lightweight Linux terminal system monitor built with Textual
|
|
5
|
-
Author: Leon
|
|
6
|
-
License: MIT
|
|
7
|
-
Requires-Python: >=3.10
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: textual>=0.80.0
|
|
10
|
-
Requires-Dist: platformdirs>=4.0.0
|
|
11
|
-
Requires-Dist: rich>=13.0.0
|
|
12
|
-
|
|
13
|
-
# Monitorify
|
|
14
|
-
### A terminal montioring tool for linux
|
|
15
|
-
|
|
16
|
-
## Features
|
|
17
|
-
### CPU monitoring
|
|
18
|
-
### RAM monitoring
|
|
19
|
-
### Network monitoring
|
|
20
|
-
### Disk monitoring (I/O and storage capacity)
|
|
21
|
-
### Process monitoring
|
|
22
|
-
### Background daemon
|
|
23
|
-
### Customization menu (toggle widgets, set intervals, and change history window)
|
|
24
|
-
### Historical graphs
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
### Using pipx
|
|
28
|
-
```bash
|
|
29
|
-
pipx install git+https://github.com/leonst036/Monitorify.git
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Using pip
|
|
33
|
-
```bash
|
|
34
|
-
pip install git+https://github.com/leonst036/Monitorify.git
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Using source
|
|
38
|
-
```bash
|
|
39
|
-
git clone https://github.com/leonst036/Monitorify.git
|
|
40
|
-
cd Monitorify
|
|
41
|
-
pip install .
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
```bash
|
|
46
|
-
monitorify
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Tech stack
|
|
50
|
-
### Python | Textual
|
|
51
|
-
### SQLite
|
|
52
|
-
### Linux
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|