File size: 6,759 Bytes
3dfb537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from helpers.extension import Extension
import os
import psutil


class SystemResourcesCheck(Extension):
    async def execute(self, banners: list = [], frontend_context: dict = {}, **kwargs):
        try:
            cpu_percent = psutil.cpu_percent(interval=0.1)
        except Exception:
            cpu_percent = None

        try:
            cpu_cores = psutil.cpu_count(logical=True)
        except Exception:
            cpu_cores = None

        load_avg = self._get_load_average()

        try:
            vm = psutil.virtual_memory()
            ram_percent = vm.percent
            ram_used_gb = (vm.total - vm.available) / (1024 ** 3)
            ram_total_gb = vm.total / (1024 ** 3)
        except Exception:
            ram_percent = None

            ram_used_gb = None
            ram_total_gb = None

        disk_percent, disk_used_gb, disk_total_gb, disk_path = self._get_disk_usage()

        try:
            net = psutil.net_io_counters()
            net_sent = self._format_bytes(net.bytes_sent)
            net_recv = self._format_bytes(net.bytes_recv)
        except Exception:
            net_sent = "N/A"
            net_recv = "N/A"

        load_value = "N/A"
        if load_avg:
            la1, la5, la15 = load_avg
            load_value = f"{la1:.2f} / {la5:.2f} / {la15:.2f}"

        if disk_percent is None or disk_used_gb is None or disk_total_gb is None:
            disk_value = "N/A"
        else:
            disk_value = f"{disk_used_gb:.2f}/{disk_total_gb:.2f} GB"

        if cpu_percent is None:
            cpu_value = "N/A"
        else:
            cores_value = "" if cpu_cores is None else f" ({cpu_cores} cores)"
            cpu_value = f"{cpu_percent:.0f}%{cores_value}"

        if ram_percent is None or ram_used_gb is None or ram_total_gb is None:
            ram_value = "N/A"
        else:
            ram_value = f"{ram_used_gb:.2f}/{ram_total_gb:.2f} GB"

        cpu_bar = self._bar_html(cpu_percent)
        ram_bar = self._bar_html(ram_percent)
        disk_bar = self._bar_html(disk_percent)

        banners.append({
            "id": "system-resources",
            "type": "info",
            "priority": 10,
            "title": "System Resources",
            "html": (
                "<div style=\"display:flex;flex-direction:column;gap:14px;padding-top:6px;\">"
                "<div style=\"display:flex;flex-direction:column;gap:12px;\">"
                "<div style=\"display:flex;flex-wrap:wrap;column-gap:12px;row-gap:10px;align-items:center;\">"
                "<div style=\"flex:0 1 140px;min-width:110px;display:flex;flex-direction:column;gap:6px;\">"
                "<div style=\"font-size:12px;letter-spacing:.10em;text-transform:uppercase;opacity:.65;line-height:1.1\">CPU</div>"
                f"<div style=\"font-weight:750;font-variant-numeric:tabular-nums;letter-spacing:.02em;line-height:1.1\">{cpu_value}</div>"
                "</div>"
                f"{cpu_bar}"
                "</div>"
                "<div style=\"display:flex;flex-wrap:wrap;column-gap:12px;row-gap:10px;align-items:center;\">"
                "<div style=\"flex:0 1 140px;min-width:110px;display:flex;flex-direction:column;gap:6px;\">"
                "<div style=\"font-size:12px;letter-spacing:.10em;text-transform:uppercase;opacity:.65;line-height:1.1\">RAM</div>"
                f"<div style=\"font-weight:750;font-variant-numeric:tabular-nums;letter-spacing:.02em;line-height:1.1\">{ram_value}</div>"
                "</div>"
                f"{ram_bar}"
                "</div>"
                "<div style=\"display:flex;flex-wrap:wrap;column-gap:12px;row-gap:10px;align-items:center;\">"
                "<div style=\"flex:0 1 140px;min-width:110px;display:flex;flex-direction:column;gap:6px;\">"
                "<div style=\"font-size:12px;letter-spacing:.10em;text-transform:uppercase;opacity:.65;line-height:1.1\">Disk</div>"
                f"<div style=\"font-weight:750;font-variant-numeric:tabular-nums;letter-spacing:.02em;line-height:1.1\">{disk_value}</div>"
                "</div>"
                f"{disk_bar}"
                "</div>"
                "</div>"
                "<div style=\"height:1px;background:rgba(255,255,255,.08);\"></div>"
                "<div style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px 14px;\">"
                f"<div><div style=\"font-size:12px;letter-spacing:.10em;text-transform:uppercase;opacity:.65;margin-bottom:6px;\">Load (1/5/15)</div><div style=\"font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;opacity:.85;font-variant-numeric:tabular-nums\">{load_value}</div></div>"
                f"<div><div style=\"font-size:12px;letter-spacing:.10em;text-transform:uppercase;opacity:.65;margin-bottom:6px;\">Net (since boot)</div><div style=\"font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;opacity:.85;font-variant-numeric:tabular-nums\">{net_sent} sent / {net_recv} recv</div></div>"
                "</div>"
                "</div>"
            ),
            "dismissible": True,
            "source": "backend",
        })

    def _bar_html(self, percent: float | None) -> str:
        if percent is None:
            return ""

        p = max(0.0, min(100.0, float(percent)))
        if p >= 85:
            color = "#ef4444"
        elif p >= 70:
            color = "#f59e0b"
        else:
            color = "#22c55e"

        return (
            "<div style=\"flex:1 1 220px;width:100%;max-width:260px;height:10px;border-radius:999px;"
            "background:rgba(255,255,255,.10);overflow:hidden;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08);\">"
            f"<div style=\"height:100%;width:{p:.0f}%;background:{color};border-radius:999px;\"></div>"
            "</div>"
        )

    def _get_load_average(self) -> tuple[float, float, float] | None:
        try:
            return os.getloadavg()
        except Exception:
            return None

    def _get_disk_usage(self) -> tuple[float | None, float | None, float | None, str]:
        for path in ["/", os.path.expanduser("~")]:
            try:
                usage = psutil.disk_usage(path)
                used_gb = usage.used / (1024 ** 3)
                total_gb = usage.total / (1024 ** 3)
                return usage.percent, used_gb, total_gb, path
            except Exception:
                continue
        return None, None, None, "/"

    def _format_bytes(self, value: int) -> str:
        size = float(value)
        for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
            if size < 1024:
                return f"{size:.1f} {unit}"
            size /= 1024
        return f"{size:.1f} EB"