Dashboard improvements

This commit is contained in:
Brett Williams
2022-10-08 17:55:24 -07:00
parent 7b33f06405
commit e12171a6d2

View File

@@ -1,28 +1,24 @@
#!/usr/bin/env python #!/usr/bin/env python
import datetime import datetime
import json
import os.path import os.path
import socket import socket
import time
import threading import threading
import time
import traceback import traceback
import psutil
import requests import requests
import click
from rich import box from rich import box
from rich.console import Console from rich.console import Console
from rich.layout import Layout
from rich.live import Live from rich.live import Live
from rich.panel import Panel
from rich.table import Column from rich.table import Column
from rich.table import Table from rich.table import Table
from rich.text import Text from rich.text import Text
from rich.layout import Layout
from rich.panel import Panel
from rich.tree import Tree from rich.tree import Tree
import zordon_server import zordon_server
from zordon_server import RenderStatus from zordon_server import RenderStatus, string_to_status
from zordon_server import string_to_status
""" """
The RenderDashboard is designed to be run on a remote machine or on the local server The RenderDashboard is designed to be run on a remote machine or on the local server
@@ -36,6 +32,8 @@ status_colors = {RenderStatus.ERROR: "red", RenderStatus.CANCELLED: 'orange1', R
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED, categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
RenderStatus.COMPLETED, RenderStatus.CANCELLED] RenderStatus.COMPLETED, RenderStatus.CANCELLED]
renderer_colors = {'ffmpeg': '[magenta]', 'Blender': '[orange1]', 'aerender':'[purple'}
local_hostname = socket.gethostname() local_hostname = socket.gethostname()
@@ -46,15 +44,19 @@ def status_string_to_color(status_string):
def sorted_jobs(all_jobs): def sorted_jobs(all_jobs):
# sorted_jobs = []
# if all_jobs: sort_by_date = True
# for status_category in categories: if not sort_by_date:
# found_jobs = [x for x in all_jobs if x['status'] == status_category.value] sorted_job_list = []
# if found_jobs: if all_jobs:
# sorted_found_jobs = sorted(found_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True) for status_category in categories:
# sorted_jobs.extend(sorted_found_jobs) found_jobs = [x for x in all_jobs if x['status'] == status_category.value]
sorted_jobs = sorted(all_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True) if found_jobs:
return sorted_jobs sorted_found_jobs = sorted(found_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
sorted_job_list.extend(sorted_found_jobs)
else:
sorted_job_list = sorted(all_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
return sorted_job_list
def create_node_tree(all_server_data) -> Tree: def create_node_tree(all_server_data) -> Tree:
@@ -69,29 +71,30 @@ def create_node_tree(all_server_data) -> Tree:
node_tree = Tree(node_tree_text) node_tree = Tree(node_tree_text)
stats_text = f"CPU: {server_data['status']['cpu_percent']}% | RAM: {server_data['status']['memory_percent']}% | " \ stats_text = f"CPU: [yellow]{server_data['status']['cpu_percent']}% [default]| RAM: " \
f"Cores: {server_data['status']['cpu_count']} | {server_data['status']['platform'].split('-')[0]}" f"[yellow]{server_data['status']['memory_percent']}% [default]| Cores: " \
f"[yellow]{server_data['status']['cpu_count']} [default]| " \
f"{server_data['status']['platform'].split('-')[0]}"
node_tree.add(Tree(stats_text)) node_tree.add(Tree(stats_text))
running_jobs = [job for job in server_data['jobs'] if job['status'] == 'running'] running_jobs = [job for job in server_data['jobs'] if job['status'] == 'running']
not_started = [job for job in server_data['jobs'] if job['status'] == 'not_started'] not_started = [job for job in server_data['jobs'] if job['status'] == 'not_started']
scheduled = [job for job in server_data['jobs'] if job['status'] == 'scheduled'] scheduled = [job for job in server_data['jobs'] if job['status'] == 'scheduled']
jobs_to_display = running_jobs + not_started + scheduled
jobs_tree = Tree(f"Running: [green]{len(running_jobs)} [default]| Queued: [cyan]{len(not_started)}" jobs_tree = Tree(f"Running: [green]{len(running_jobs)} [default]| Queued: [cyan]{len(not_started)}"
f"[default] | Scheduled: [cyan]{len(scheduled)}") f"[default] | Scheduled: [cyan]{len(scheduled)}")
if running_jobs or not_started or scheduled: for job in jobs_to_display:
for job in running_jobs: renderer = f"{renderer_colors[job['renderer']]}{job['renderer']}[default]"
filename = os.path.basename(job['render']['input']).split('.')[0] filename = os.path.basename(job['render']['input']).split('.')[0]
jobs_tree.add(f"[bold]{filename} ({job['id']}) - {status_string_to_color(job['status'])}{(float(job['render']['percent_complete']) * 100):.1f}%") if job['status'] == 'running':
for job in not_started: jobs_tree.add(f"[bold]{renderer} {filename} ({job['id']}) - {status_string_to_color(job['status'])}{(float(job['render']['percent_complete']) * 100):.1f}%")
filename = os.path.basename(job['render']['input']).split('.')[0]
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
for job in scheduled:
filename = os.path.basename(job['render']['input']).split('.')[0]
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
else: else:
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
if not jobs_to_display:
jobs_tree.add("[italic]No running jobs") jobs_tree.add("[italic]No running jobs")
node_tree.add(jobs_tree) node_tree.add(jobs_tree)
@@ -102,13 +105,13 @@ def create_node_tree(all_server_data) -> Tree:
def create_jobs_table(all_server_data) -> Table: def create_jobs_table(all_server_data) -> Table:
table = Table("ID", "Project", "Output", "Renderer", Column(header="Priority", justify="center"), table = Table("ID", "Project", "Output", "Renderer", Column(header="Priority", justify="center"),
Column(header="Status", justify="center"), Column(header="Time Elapsed", justify="right"), Column(header="Status", justify="center"), Column(header="Time Elapsed", justify="right"),
Column(header="# Frames", justify="right"), "Node", show_lines=True, Column(header="# Frames", justify="right"), "Client", show_lines=True,
box=box.HEAVY_HEAD) box=box.HEAVY_HEAD)
all_jobs = [] all_jobs = []
for server_name, server_data in all_server_data['servers'].items(): for server_name, server_data in all_server_data['servers'].items():
for job in server_data['jobs']: for job in server_data['jobs']:
job['node'] = server_name #todo: clean this up
all_jobs.append(job) all_jobs.append(job)
all_jobs = sorted_jobs(all_jobs) all_jobs = sorted_jobs(all_jobs)
@@ -117,7 +120,10 @@ def create_jobs_table(all_server_data) -> Table:
job_status = string_to_status(job['status']) job_status = string_to_status(job['status'])
job_color = '[{}]'.format(status_colors[job_status]) job_color = '[{}]'.format(status_colors[job_status])
job_text = f"{job_color}" + job_status.value job_text = f"{job_color}" + job_status.value.title()
if job_status == RenderStatus.ERROR and job['render']['errors']:
job_text = job_text + "\n" + "\n".join(job['render']['errors'])
elapsed_time = job['render'].get('time_elapsed', 'unknown') elapsed_time = job['render'].get('time_elapsed', 'unknown')
@@ -136,8 +142,9 @@ def create_jobs_table(all_server_data) -> Table:
# Priority # Priority
priority_color = ["red", "yellow", "cyan"][(job['priority'] - 1)] priority_color = ["red", "yellow", "cyan"][(job['priority'] - 1)]
node_title = ("[yellow]" if job['node'] == local_hostname else "[magenta]") + job['node'] client_name = job['client'] or 'unknown'
renderer_colors = {'ffmpeg': '[magenta]', 'Blender': '[orange1]'} client_colors = {'unknown': '[red]', local_hostname: '[yellow]'}
client_title = client_colors.get(client_name, '[magenta]') + client_name
table.add_row( table.add_row(
job['id'], job['id'],
@@ -148,7 +155,7 @@ def create_jobs_table(all_server_data) -> Table:
job_text, job_text,
elapsed_time, elapsed_time,
str(max(int(job['render']['total_frames']), 1)), str(max(int(job['render']['total_frames']), 1)),
node_title client_title
) )
return table return table
@@ -179,7 +186,6 @@ class RenderDashboard:
return req.json() return req.json()
except Exception as e: except Exception as e:
pass pass
# print(f"Exception fetching server data: {e}")
return None return None
def get_jobs(self): def get_jobs(self):
@@ -216,20 +222,20 @@ def my_callback(inp):
if __name__ == '__main__': if __name__ == '__main__':
server_ip = input("Enter server IP or None for local: ") or local_hostname get_server_ip = input("Enter server IP or None for local: ") or local_hostname
client = RenderDashboard(server_ip, "8080") client = RenderDashboard(get_server_ip, "8080")
if not client.connect(): if not client.connect():
if server_ip == local_hostname: if client.server_ip == local_hostname:
start_server = input("Local server not running. Start server? (y/n) ") start_server = input("Local server not running. Start server? (y/n) ")
if start_server and start_server[0].lower() == "y": if start_server and start_server[0].lower() == "y":
# Startup the local server # Startup the local server
zordon_server.start_server(background_thread=True) zordon_server.RenderServer.start(background_thread=True)
test = client.connect() test = client.connect()
print(f"connected? {test}") print(f"connected? {test}")
else: else:
print(f"\nUnable to connect to server: {server_ip}") print(f"\nUnable to connect to server: {client.server_ip}")
print("\nVerify IP address is correct and server is running") print("\nVerify IP address is correct and server is running")
exit(1) exit(1)
@@ -256,8 +262,8 @@ if __name__ == '__main__':
# Server connection header # Server connection header
header_text = Text(f"Connected to server: ") header_text = Text(f"Connected to server: ")
header_text.append(f"{server_ip} ", style="green") header_text.append(f"{client.server_ip} ", style="green")
if server_ip == local_hostname: if client.server_ip == local_hostname:
header_text.append("(This Computer)", style="yellow") header_text.append("(This Computer)", style="yellow")
else: else:
header_text.append("(Remote)", style="magenta") header_text.append("(Remote)", style="magenta")