import os
import sqlite3
from pathlib import Path
from tensorboard.backend import http_util
from werkzeug import wrappers, Response, exceptions
from ..common.utils import Utils,FILE_EXTENSION
from ..services.monvis_service import MonvisService
class MonvisController:
def __init__(self, db_path):
self.db_path = db_path
self.monvis_service = MonvisService(self.db_path)
self.is_db_connected = self.monvis_service.is_db_connected
@staticmethod
@wrappers.Request.application
def static_file_route(request):
filename = os.path.basename(request.path)
extension = os.path.splitext(filename)[1]
if extension == ".html":
mimetype = "text/html"
elif extension == ".js":
mimetype = "application/javascript"
else:
mimetype = "application/octet-stream"
try:
if filename != "index.html" and filename != "index.js":
raise exceptions.NotFound("404 Not Found") from e
server_dir = Path(__file__).resolve().parent.parent
filepath = server_dir / "static" / filename
with open(filepath, "rb") as infile:
contents = infile.read()
except IOError as e:
raise exceptions.NotFound("404 Not Found") from e
return Response(contents, content_type=mimetype, headers={"X-Content-Type-Options": "nosniff"})
@wrappers.Request.application
def request_metrics(self, request):
"""Return all available metrics and fixed stats."""
try:
result = self.monvis_service.get_metrics_stat()
except sqlite3.Error as e:
result = {"success": False, "error": f"sqlite error: {Utils.replace_paths_with_filenames(str(e))}"}
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(str(e))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_values(self, request):
"""Return list of values for specified metric, stat and dimension."""
try:
data = Utils.safe_json_loads(request.get_data().decode("utf-8"), {})
metric = data.get("metric")
stat = data.get("stat")
dimension = data.get("dimension")
tags = data.get("tags")
result = self.monvis_service.get_values(metric, stat, dimension, tags)
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(str(e))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_tags(self, request):
"""Return list of tags for specified metric, stat and dimension."""
try:
metric = request.args.get("metric")
result = self.monvis_service.get_tags(metric)
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(str(e))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_heatmap_data(self, request):
"""Return heatmap data for specified parameters."""
try:
data = Utils.safe_json_loads(request.get_data().decode("utf-8"), {})
metric = data.get("metric")
stat = data.get("stat")
dimension = data.get("dimension")
value = data.get("value")
tags = data.get("tags")
result = self.monvis_service.get_heatmap_data(metric, stat, dimension, value, tags)
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(str(e))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_trend_data(self, request):
"""Return trend data for specified parameters."""
try:
data = Utils.safe_json_loads(request.get_data().decode("utf-8"), {})
metric = data.get("metric")
stat = data.get("stat")
dimension = data.get("dimension")
dim_x = data.get("dimX")
dim_y = data.get("dimYIdx")
tags = data.get("tags")
result = self.monvis_service.get_trend_data(metric, stat, dimension, dim_x, dim_y, tags)
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(str(e))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_db_files(self, request):
"""Return list of all available database files."""
try:
logdir = os.path.dirname(self.db_path)
success, error = Utils.safe_check_load_file_path(logdir, True)
if not success:
raise Exception(error)
db_files = []
for file in os.listdir(logdir):
if file.endswith(FILE_EXTENSION):
db_files.append(file)
db_files.sort()
current_db = os.path.basename(self.db_path)
result = {
"success": True,
"data": db_files,
"current": current_db
}
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(Utils.replace_paths_with_filenames(str(e)))}
return http_util.Respond(request, result, "application/json")
@wrappers.Request.application
def request_switch_db(self, request):
"""Switch to a different database file."""
try:
data = Utils.safe_json_loads(request.get_data().decode("utf-8"), {})
db_filename = data.get("db_filename")
if not db_filename:
return http_util.Respond(
request,
{"success": False, "error": "db_filename is required"},
"application/json"
)
if not db_filename.endswith(FILE_EXTENSION):
return http_util.Respond(
request,
{"success": False, "error": "Invalid database file format"},
"application/json"
)
logdir = os.path.dirname(self.db_path)
new_db_path = os.path.join(logdir, db_filename)
is_safe, error = Utils.safe_check_load_file_path(new_db_path, is_dir=False)
if not is_safe:
return http_util.Respond(
request,
{"success": False, "error": str(error)},
"application/json"
)
if not os.path.exists(new_db_path):
return http_util.Respond(
request,
{"success": False, "error": f"Database file {db_filename} not found"},
"application/json"
)
self.db_path = new_db_path
self.monvis_service = MonvisService(self.db_path)
self.is_db_connected = self.monvis_service.is_db_connected
result = {
"success": True,
"message": f"Successfully switched to {db_filename}",
"data": {"db_path": new_db_path}
}
except Exception as e:
result = {"success": False, "error": Utils.replace_paths_with_filenames(Utils.replace_paths_with_filenames(str(e)))}
return http_util.Respond(request, result, "application/json")