Update: Code formatting
All checks were successful
CI/CD Pipeline / Unit Tests (Python 3.10) (push) Successful in 9m23s
CI/CD Pipeline / Unit Tests (Python 3.11) (push) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.9) (push) Successful in 9m19s
CI/CD Pipeline / Code Quality & Linting (push) Successful in 44s
CI/CD Pipeline / Security Scanning (push) Successful in 16s
CI/CD Pipeline / Integration Tests (push) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (push) Successful in 36s
CI/CD Pipeline / Generate Test Report (push) Successful in 4s
CI/CD Pipeline / CI/CD Pipeline Status (push) Successful in 1s
CI/CD Pipeline / Unit Tests (Python 3.10) (pull_request) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.11) (pull_request) Successful in 9m17s
CI/CD Pipeline / Unit Tests (Python 3.9) (pull_request) Successful in 9m19s
CI/CD Pipeline / Code Quality & Linting (pull_request) Successful in 41s
CI/CD Pipeline / Security Scanning (pull_request) Successful in 15s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (pull_request) Has been skipped
CI/CD Pipeline / Generate Test Report (pull_request) Successful in 2s
CI/CD Pipeline / CI/CD Pipeline Status (pull_request) Successful in 1s
All checks were successful
CI/CD Pipeline / Unit Tests (Python 3.10) (push) Successful in 9m23s
CI/CD Pipeline / Unit Tests (Python 3.11) (push) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.9) (push) Successful in 9m19s
CI/CD Pipeline / Code Quality & Linting (push) Successful in 44s
CI/CD Pipeline / Security Scanning (push) Successful in 16s
CI/CD Pipeline / Integration Tests (push) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (push) Successful in 36s
CI/CD Pipeline / Generate Test Report (push) Successful in 4s
CI/CD Pipeline / CI/CD Pipeline Status (push) Successful in 1s
CI/CD Pipeline / Unit Tests (Python 3.10) (pull_request) Successful in 9m19s
CI/CD Pipeline / Unit Tests (Python 3.11) (pull_request) Successful in 9m17s
CI/CD Pipeline / Unit Tests (Python 3.9) (pull_request) Successful in 9m19s
CI/CD Pipeline / Code Quality & Linting (pull_request) Successful in 41s
CI/CD Pipeline / Security Scanning (pull_request) Successful in 15s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 9m14s
CI/CD Pipeline / Build Docker Image (pull_request) Has been skipped
CI/CD Pipeline / Generate Test Report (pull_request) Successful in 2s
CI/CD Pipeline / CI/CD Pipeline Status (pull_request) Successful in 1s
This commit is contained in:
599
pterodisbot.py
599
pterodisbot.py
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,16 @@ This module provides graphing capabilities for server CPU and memory usage.
|
|||||||
Generates line graphs as PNG images for embedding in Discord messages.
|
Generates line graphs as PNG images for embedding in Discord messages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import matplotlib
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import matplotlib.dates as mdates
|
|
||||||
from collections import deque
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Dict, Optional
|
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.dates as mdates
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
matplotlib.use("Agg") # Use non-interactive backend for server environments
|
matplotlib.use("Agg") # Use non-interactive backend for server environments
|
||||||
|
|
||||||
@@ -52,13 +53,9 @@ class ServerMetricsGraphs:
|
|||||||
# Track if we have enough data for meaningful graphs (at least 2 points)
|
# Track if we have enough data for meaningful graphs (at least 2 points)
|
||||||
self.has_sufficient_data = False
|
self.has_sufficient_data = False
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"Initialized metrics tracking for server {server_name} ({server_id})")
|
||||||
f"Initialized metrics tracking for server {server_name} ({server_id})"
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_data_point(
|
def add_data_point(self, cpu_percent: float, memory_mb: float, timestamp: Optional[datetime] = None):
|
||||||
self, cpu_percent: float, memory_mb: float, timestamp: Optional[datetime] = None
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Add a new data point to the metrics history.
|
Add a new data point to the metrics history.
|
||||||
|
|
||||||
@@ -76,9 +73,7 @@ class ServerMetricsGraphs:
|
|||||||
# Update sufficient data flag
|
# Update sufficient data flag
|
||||||
self.has_sufficient_data = len(self.data_points) >= 2
|
self.has_sufficient_data = len(self.data_points) >= 2
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"Added metrics data point for {self.server_name}: CPU={cpu_percent}%, Memory={memory_mb}MB")
|
||||||
f"Added metrics data point for {self.server_name}: CPU={cpu_percent}%, Memory={memory_mb}MB"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _calculate_cpu_scale_limit(self, max_cpu_value: float) -> int:
|
def _calculate_cpu_scale_limit(self, max_cpu_value: float) -> int:
|
||||||
"""
|
"""
|
||||||
@@ -105,9 +100,7 @@ class ServerMetricsGraphs:
|
|||||||
BytesIO object containing PNG image data, or None if insufficient data
|
BytesIO object containing PNG image data, or None if insufficient data
|
||||||
"""
|
"""
|
||||||
if not self.has_sufficient_data:
|
if not self.has_sufficient_data:
|
||||||
logger.debug(
|
logger.debug(f"Insufficient data for CPU graph generation: {self.server_name}")
|
||||||
f"Insufficient data for CPU graph generation: {self.server_name}"
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -134,9 +127,7 @@ class ServerMetricsGraphs:
|
|||||||
|
|
||||||
# Add horizontal grid lines at 100% increments for better readability
|
# Add horizontal grid lines at 100% increments for better readability
|
||||||
for i in range(100, cpu_scale_limit + 1, 100):
|
for i in range(100, cpu_scale_limit + 1, 100):
|
||||||
ax.axhline(
|
ax.axhline(y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8)
|
||||||
y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8
|
|
||||||
)
|
|
||||||
|
|
||||||
# Format time axis
|
# Format time axis
|
||||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
||||||
@@ -182,15 +173,11 @@ class ServerMetricsGraphs:
|
|||||||
# Clean up matplotlib resources
|
# Clean up matplotlib resources
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"Generated CPU graph for {self.server_name} (scale: 0-{cpu_scale_limit}%)")
|
||||||
f"Generated CPU graph for {self.server_name} (scale: 0-{cpu_scale_limit}%)"
|
|
||||||
)
|
|
||||||
return img_buffer
|
return img_buffer
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(f"Failed to generate CPU graph for {self.server_name}: {str(e)}")
|
||||||
f"Failed to generate CPU graph for {self.server_name}: {str(e)}"
|
|
||||||
)
|
|
||||||
plt.close("all") # Clean up any remaining figures
|
plt.close("all") # Clean up any remaining figures
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -202,9 +189,7 @@ class ServerMetricsGraphs:
|
|||||||
BytesIO object containing PNG image data, or None if insufficient data
|
BytesIO object containing PNG image data, or None if insufficient data
|
||||||
"""
|
"""
|
||||||
if not self.has_sufficient_data:
|
if not self.has_sufficient_data:
|
||||||
logger.debug(
|
logger.debug(f"Insufficient data for memory graph generation: {self.server_name}")
|
||||||
f"Insufficient data for memory graph generation: {self.server_name}"
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -274,9 +259,7 @@ class ServerMetricsGraphs:
|
|||||||
return img_buffer
|
return img_buffer
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(f"Failed to generate memory graph for {self.server_name}: {str(e)}")
|
||||||
f"Failed to generate memory graph for {self.server_name}: {str(e)}"
|
|
||||||
)
|
|
||||||
plt.close("all") # Clean up any remaining figures
|
plt.close("all") # Clean up any remaining figures
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -288,9 +271,7 @@ class ServerMetricsGraphs:
|
|||||||
BytesIO object containing PNG image data, or None if insufficient data
|
BytesIO object containing PNG image data, or None if insufficient data
|
||||||
"""
|
"""
|
||||||
if not self.has_sufficient_data:
|
if not self.has_sufficient_data:
|
||||||
logger.debug(
|
logger.debug(f"Insufficient data for combined graph generation: {self.server_name}")
|
||||||
f"Insufficient data for combined graph generation: {self.server_name}"
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -326,9 +307,7 @@ class ServerMetricsGraphs:
|
|||||||
|
|
||||||
# Add horizontal grid lines at 100% increments for CPU subplot
|
# Add horizontal grid lines at 100% increments for CPU subplot
|
||||||
for i in range(100, cpu_scale_limit + 1, 100):
|
for i in range(100, cpu_scale_limit + 1, 100):
|
||||||
ax1.axhline(
|
ax1.axhline(y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8)
|
||||||
y=i, color="#ffffff", alpha=0.2, linestyle="--", linewidth=0.8
|
|
||||||
)
|
|
||||||
|
|
||||||
# Title with vCPU info if applicable
|
# Title with vCPU info if applicable
|
||||||
title = f"{self.server_name} - Resource Usage"
|
title = f"{self.server_name} - Resource Usage"
|
||||||
@@ -387,15 +366,11 @@ class ServerMetricsGraphs:
|
|||||||
|
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"Generated combined graph for {self.server_name} (CPU scale: 0-{cpu_scale_limit}%)")
|
||||||
f"Generated combined graph for {self.server_name} (CPU scale: 0-{cpu_scale_limit}%)"
|
|
||||||
)
|
|
||||||
return img_buffer
|
return img_buffer
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(f"Failed to generate combined graph for {self.server_name}: {str(e)}")
|
||||||
f"Failed to generate combined graph for {self.server_name}: {str(e)}"
|
|
||||||
)
|
|
||||||
plt.close("all")
|
plt.close("all")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -468,9 +443,7 @@ class ServerMetricsManager:
|
|||||||
self.server_graphs: Dict[str, ServerMetricsGraphs] = {}
|
self.server_graphs: Dict[str, ServerMetricsGraphs] = {}
|
||||||
logger.info("Initialized ServerMetricsManager")
|
logger.info("Initialized ServerMetricsManager")
|
||||||
|
|
||||||
def get_or_create_server_graphs(
|
def get_or_create_server_graphs(self, server_id: str, server_name: str) -> ServerMetricsGraphs:
|
||||||
self, server_id: str, server_name: str
|
|
||||||
) -> ServerMetricsGraphs:
|
|
||||||
"""
|
"""
|
||||||
Get existing ServerMetricsGraphs instance or create a new one.
|
Get existing ServerMetricsGraphs instance or create a new one.
|
||||||
|
|
||||||
@@ -487,9 +460,7 @@ class ServerMetricsManager:
|
|||||||
|
|
||||||
return self.server_graphs[server_id]
|
return self.server_graphs[server_id]
|
||||||
|
|
||||||
def add_server_data(
|
def add_server_data(self, server_id: str, server_name: str, cpu_percent: float, memory_mb: float):
|
||||||
self, server_id: str, server_name: str, cpu_percent: float, memory_mb: float
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Add data point to a server's metrics tracking.
|
Add data point to a server's metrics tracking.
|
||||||
|
|
||||||
@@ -541,9 +512,7 @@ class ServerMetricsManager:
|
|||||||
self.remove_server(server_id)
|
self.remove_server(server_id)
|
||||||
|
|
||||||
if servers_to_remove:
|
if servers_to_remove:
|
||||||
logger.info(
|
logger.info(f"Cleaned up metrics for {len(servers_to_remove)} inactive servers")
|
||||||
f"Cleaned up metrics for {len(servers_to_remove)} inactive servers"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_summary(self) -> Dict[str, any]:
|
def get_summary(self) -> Dict[str, any]:
|
||||||
"""
|
"""
|
||||||
@@ -554,12 +523,6 @@ class ServerMetricsManager:
|
|||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
"total_servers": len(self.server_graphs),
|
"total_servers": len(self.server_graphs),
|
||||||
"servers_with_data": sum(
|
"servers_with_data": sum(1 for graphs in self.server_graphs.values() if graphs.has_sufficient_data),
|
||||||
1
|
"total_data_points": sum(len(graphs.data_points) for graphs in self.server_graphs.values()),
|
||||||
for graphs in self.server_graphs.values()
|
|
||||||
if graphs.has_sufficient_data
|
|
||||||
),
|
|
||||||
"total_data_points": sum(
|
|
||||||
len(graphs.data_points) for graphs in self.server_graphs.values()
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user