added exporting reports from config tool
This commit is contained in:
387
tools/cfg_util/boards/__init__.py
Normal file
387
tools/cfg_util/boards/__init__.py
Normal file
@@ -0,0 +1,387 @@
|
||||
import datetime
|
||||
import ipaddress
|
||||
from base64 import b64decode
|
||||
from io import BytesIO
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter, inch
|
||||
from reportlab.lib.styles import ParagraphStyle, TA_CENTER
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.platypus import (
|
||||
SimpleDocTemplate,
|
||||
KeepInFrame,
|
||||
Table,
|
||||
Image,
|
||||
Paragraph,
|
||||
TableStyle,
|
||||
PageBreak,
|
||||
Spacer,
|
||||
)
|
||||
from svglib.svglib import svg2rlg
|
||||
|
||||
from miners.miner_factory import MinerFactory
|
||||
from tools.cfg_util.decorators import disable_buttons
|
||||
from tools.cfg_util.imgs import IMAGE_SELECTION_MATRIX, LOGO
|
||||
from tools.cfg_util.tables import TableManager
|
||||
|
||||
IP_STYLE = ParagraphStyle(
|
||||
"IP Style",
|
||||
alignment=TA_CENTER,
|
||||
fontSize=7,
|
||||
fontName="Helvetica-Bold",
|
||||
)
|
||||
TITLE_STYLE = ParagraphStyle(
|
||||
"Title",
|
||||
alignment=TA_CENTER,
|
||||
fontSize=20,
|
||||
spaceAfter=40,
|
||||
fontName="Helvetica-Bold",
|
||||
)
|
||||
|
||||
|
||||
def add_first_page_number(canvas, doc):
|
||||
canvas.saveState()
|
||||
canvas.drawString(letter[0] - 60, 20, "Page " + str(doc.page))
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
def add_page_header(canvas, doc):
|
||||
canvas.saveState()
|
||||
canvas.drawCentredString(
|
||||
(letter[0] / 16) * 14,
|
||||
letter[1] - 57,
|
||||
datetime.datetime.now().strftime("%Y-%b-%d"),
|
||||
)
|
||||
img_dec = b64decode(LOGO)
|
||||
img = BytesIO(img_dec)
|
||||
img.seek(0)
|
||||
|
||||
canvas.drawImage(
|
||||
ImageReader(img),
|
||||
30,
|
||||
letter[1] - 65,
|
||||
150,
|
||||
35,
|
||||
)
|
||||
canvas.drawString(letter[0] - 60, 20, "Page " + str(doc.page))
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
@disable_buttons("Exporting Report")
|
||||
async def boards_report(file_location):
|
||||
p1_logo, p1_title = create_first_page()
|
||||
data = {}
|
||||
table_manager = TableManager()
|
||||
for key in table_manager.data.keys():
|
||||
line = table_manager.data[key]
|
||||
data[line["IP"]] = {
|
||||
"Model": line["Model"],
|
||||
"Total Chips": line["Total"],
|
||||
"Left Chips": line["Left Board"],
|
||||
"Center Chips": line["Center Board"],
|
||||
"Right Chips": line["Right Board"],
|
||||
"Nominal": round((line["Ideal"] / 3) * 0.9),
|
||||
}
|
||||
|
||||
async for miner in MinerFactory().get_miner_generator([key for key in data.keys()]):
|
||||
if miner:
|
||||
data[str(miner.ip)]["Nominal"] = round(miner.nominal_chips * 0.9)
|
||||
|
||||
list_data = []
|
||||
for ip in data.keys():
|
||||
new_data = data[ip]
|
||||
new_data["IP"] = ip
|
||||
list_data.append(new_data)
|
||||
|
||||
list_data = sorted(
|
||||
list_data, reverse=False, key=lambda x: ipaddress.ip_address(x["IP"])
|
||||
)
|
||||
|
||||
image_selection_data = {}
|
||||
for miner in list_data:
|
||||
miner_bad_boards = ""
|
||||
if miner["Left Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += "l"
|
||||
if miner["Center Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += "c"
|
||||
if miner["Right Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += "r"
|
||||
image_selection_data[miner["IP"]] = miner_bad_boards
|
||||
|
||||
doc = SimpleDocTemplate(
|
||||
file_location,
|
||||
pagesize=letter,
|
||||
topMargin=1 * inch,
|
||||
leftMargin=1 * inch,
|
||||
rightMargin=1 * inch,
|
||||
bottomMargin=1 * inch,
|
||||
title=f"Board Report {datetime.datetime.now().strftime('%Y/%b/%d')}",
|
||||
)
|
||||
|
||||
pie_chart, board_table = create_boards_pie_chart(image_selection_data)
|
||||
|
||||
table_data = get_table_data(image_selection_data)
|
||||
|
||||
miner_img_table = Table(
|
||||
table_data,
|
||||
colWidths=0.8 * inch,
|
||||
# repeatRows=1,
|
||||
# rowHeights=[4 * inch],
|
||||
)
|
||||
|
||||
miner_img_table.setStyle(
|
||||
TableStyle(
|
||||
[
|
||||
("SPAN", (0, 0), (-1, 0)),
|
||||
("LEFTPADDING", (0, 0), (-1, -1), 0),
|
||||
("RIGHTPADDING", (0, 0), (-1, -1), 0),
|
||||
("BOTTOMPADDING", (0, 1), (-1, -1), 0),
|
||||
("TOPPADDING", (0, 1), (-1, -1), 0),
|
||||
("BOTTOMPADDING", (0, 0), (-1, 0), 20),
|
||||
("TOPPADDING", (0, 0), (-1, 0), 20),
|
||||
]
|
||||
)
|
||||
)
|
||||
elements = [
|
||||
p1_logo,
|
||||
p1_title,
|
||||
PageBreak(),
|
||||
pie_chart,
|
||||
Spacer(0, 60),
|
||||
board_table,
|
||||
PageBreak(),
|
||||
miner_img_table,
|
||||
PageBreak(),
|
||||
Paragraph(
|
||||
"Board Data",
|
||||
style=TITLE_STYLE,
|
||||
),
|
||||
create_data_table(list_data),
|
||||
PageBreak(),
|
||||
]
|
||||
|
||||
doc.build(
|
||||
elements,
|
||||
onFirstPage=add_first_page_number,
|
||||
onLaterPages=add_page_header,
|
||||
)
|
||||
|
||||
|
||||
def create_boards_pie_chart(data):
|
||||
labels = ["All Working", "1 Bad Board", "2 Bad Boards", "3 Bad Boards"]
|
||||
num_bad_boards = [0, 0, 0, 0]
|
||||
for item in data.keys():
|
||||
num_bad_boards[len(data[item])] += 1
|
||||
cmap = plt.get_cmap("Blues")
|
||||
cs = cmap(np.linspace(0.2, 0.8, num=len(num_bad_boards)))
|
||||
|
||||
fig1, ax = plt.subplots()
|
||||
ax.pie(
|
||||
num_bad_boards,
|
||||
labels=labels,
|
||||
autopct="%1.2f%%",
|
||||
shadow=True,
|
||||
startangle=180,
|
||||
colors=cs,
|
||||
pctdistance=0.8,
|
||||
)
|
||||
ax.axis("equal")
|
||||
ax.set_title("Broken Boards", fontsize=24, pad=20)
|
||||
|
||||
imgdata = BytesIO()
|
||||
fig1.savefig(imgdata, format="svg")
|
||||
imgdata.seek(0) # rewind the data
|
||||
drawing = svg2rlg(imgdata)
|
||||
imgdata.close()
|
||||
plt.close("all")
|
||||
pie_chart = KeepInFrame(375, 375, [Image(drawing)], hAlign="CENTER")
|
||||
|
||||
table_data = [labels, num_bad_boards]
|
||||
|
||||
t = Table(table_data)
|
||||
|
||||
table_style = TableStyle(
|
||||
[
|
||||
# ("FONTSIZE", (0, 0), (-1, -1), 13),
|
||||
# line for below titles
|
||||
("LINEBELOW", (0, 0), (-1, 0), 2, colors.black),
|
||||
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
||||
# line for above totals
|
||||
("LINEABOVE", (0, -1), (-1, -1), 2, colors.black),
|
||||
# line for beside unit #
|
||||
("LINEAFTER", (0, 0), (0, -1), 2, colors.black),
|
||||
# gridlines and outline of table
|
||||
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
|
||||
("BOX", (0, 0), (-1, -1), 2, colors.black),
|
||||
("LEFTPADDING", (0, 0), (-1, -1), 3),
|
||||
("RIGHTPADDING", (0, 0), (-1, -1), 3),
|
||||
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
|
||||
("TOPPADDING", (0, 0), (-1, -1), 3),
|
||||
]
|
||||
)
|
||||
|
||||
t.setStyle(table_style)
|
||||
|
||||
# zebra stripes on table
|
||||
for each in range(len(table_data)):
|
||||
if each % 2 == 0:
|
||||
bg_color = colors.whitesmoke
|
||||
else:
|
||||
bg_color = colors.lightgrey
|
||||
|
||||
t.setStyle(TableStyle([("BACKGROUND", (0, each), (-1, each), bg_color)]))
|
||||
|
||||
return pie_chart, t
|
||||
|
||||
|
||||
def create_first_page():
|
||||
title_style = ParagraphStyle(
|
||||
"Title",
|
||||
alignment=TA_CENTER,
|
||||
fontSize=50,
|
||||
spaceAfter=40,
|
||||
spaceBefore=150,
|
||||
fontName="Helvetica-Bold",
|
||||
)
|
||||
|
||||
img_dec = b64decode(LOGO)
|
||||
img = BytesIO(img_dec)
|
||||
img.seek(0)
|
||||
|
||||
logo = KeepInFrame(450, 105, [Image(img)])
|
||||
title = Paragraph("Board Report", style=title_style)
|
||||
return logo, title
|
||||
|
||||
|
||||
def create_data_table(data):
|
||||
left_bad_boards = 0
|
||||
right_bad_boards = 0
|
||||
center_bad_boards = 0
|
||||
table_data = []
|
||||
for miner in data:
|
||||
miner_bad_boards = 0
|
||||
if miner["Left Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += 1
|
||||
left_bad_boards += 1
|
||||
if miner["Center Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += 1
|
||||
right_bad_boards += 1
|
||||
if miner["Right Chips"] < miner["Nominal"]:
|
||||
miner_bad_boards += 1
|
||||
center_bad_boards += 1
|
||||
table_data.append(
|
||||
[
|
||||
miner["IP"],
|
||||
miner["Total Chips"],
|
||||
miner["Left Chips"],
|
||||
miner["Center Chips"],
|
||||
miner["Right Chips"],
|
||||
miner_bad_boards,
|
||||
]
|
||||
)
|
||||
|
||||
table_data.append(
|
||||
[
|
||||
"Total",
|
||||
sum([miner[1] for miner in table_data]),
|
||||
sum([miner[2] for miner in table_data]),
|
||||
sum([miner[3] for miner in table_data]),
|
||||
sum([miner[4] for miner in table_data]),
|
||||
sum([miner[5] for miner in table_data]),
|
||||
]
|
||||
)
|
||||
|
||||
table_data[:0] = (
|
||||
[
|
||||
"IP",
|
||||
"Total Chips",
|
||||
"Left Board Chips",
|
||||
"Center Board Chips",
|
||||
"Right Board Chips",
|
||||
"Failed Boards",
|
||||
],
|
||||
)
|
||||
|
||||
# create the table
|
||||
t = Table(table_data, repeatRows=1)
|
||||
|
||||
# generate a basic table style
|
||||
table_style = TableStyle(
|
||||
[
|
||||
("FONTSIZE", (0, 0), (-1, -1), 8),
|
||||
# line for below titles
|
||||
("LINEBELOW", (0, 0), (-1, 0), 2, colors.black),
|
||||
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
||||
# line for above totals
|
||||
("LINEABOVE", (0, -1), (-1, -1), 2, colors.black),
|
||||
("FONTNAME", (0, -1), (-1, -1), "Helvetica-Bold"),
|
||||
# line for beside unit #
|
||||
("LINEAFTER", (0, 0), (0, -1), 2, colors.black),
|
||||
("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
|
||||
# gridlines and outline of table
|
||||
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
|
||||
("BOX", (0, 0), (-1, -1), 2, colors.black),
|
||||
]
|
||||
)
|
||||
|
||||
for (
|
||||
row,
|
||||
values,
|
||||
) in enumerate(table_data):
|
||||
if not row == 0 and not row == (len(table_data) - 1):
|
||||
failed_boards = values[5]
|
||||
if not failed_boards == 0:
|
||||
table_style.add("TEXTCOLOR", (5, row), (5, row), colors.red)
|
||||
|
||||
# set the styles to the table
|
||||
t.setStyle(table_style)
|
||||
|
||||
# zebra stripes on table
|
||||
for each in range(len(table_data)):
|
||||
if each % 2 == 0:
|
||||
bg_color = colors.whitesmoke
|
||||
else:
|
||||
bg_color = colors.lightgrey
|
||||
|
||||
t.setStyle(TableStyle([("BACKGROUND", (0, each), (-1, each), bg_color)]))
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def get_table_data(data):
|
||||
table_elems = [[Paragraph("Hashboard Visual Representation", style=TITLE_STYLE)]]
|
||||
table_row = []
|
||||
table_style = TableStyle(
|
||||
[
|
||||
("LEFTPADDING", (0, 0), (-1, -1), 0),
|
||||
("RIGHTPADDING", (0, 0), (-1, -1), 0),
|
||||
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
|
||||
("BOX", (0, 0), (-1, -1), 2, colors.black),
|
||||
]
|
||||
)
|
||||
table_width = 0.8 * inch
|
||||
for ip in data.keys():
|
||||
img_dec = b64decode(IMAGE_SELECTION_MATRIX[data[ip]])
|
||||
img = BytesIO(img_dec)
|
||||
img.seek(0)
|
||||
image = KeepInFrame(table_width, table_width, [Image(img)])
|
||||
ip_para = Paragraph(ip, style=IP_STYLE)
|
||||
|
||||
table_row.append(
|
||||
Table([[ip_para], [image]], colWidths=table_width, style=table_style)
|
||||
)
|
||||
|
||||
# table_row.append(image)
|
||||
# table_row_txt.append(ip_para)
|
||||
|
||||
if len(table_row) > 7:
|
||||
# table_elems.append(table_row_txt)
|
||||
# table_elems.append(table_row)
|
||||
table_elems.append(table_row)
|
||||
# table_row_txt = []
|
||||
table_row = []
|
||||
if not table_row == []:
|
||||
table_elems.append(table_row)
|
||||
return table_elems
|
||||
File diff suppressed because one or more lines are too long
@@ -174,8 +174,8 @@ TEMP_COL_WIDTH = 8
|
||||
USER_COL_WIDTH = 33
|
||||
WATTAGE_COL_WIDTH = 10
|
||||
SPLIT_COL_WIDTH = 8
|
||||
TOTAL_CHIP_WIDTH = 8
|
||||
IDEAL_CHIP_WIDTH = 8
|
||||
TOTAL_CHIP_WIDTH = 9
|
||||
IDEAL_CHIP_WIDTH = 9
|
||||
SCAN_COL_WIDTHS = [
|
||||
IP_COL_WIDTH,
|
||||
MODEL_COL_WIDTH,
|
||||
@@ -283,7 +283,16 @@ def get_boards_layout():
|
||||
for i in range(3):
|
||||
BOARDS_COL_WIDTHS.append(round(add_length / 3))
|
||||
boards_layout = [
|
||||
[sg.Text("", pad=((0, 0), (10, 1)))],
|
||||
[
|
||||
sg.Input(visible=False, enable_events=True, key="boards_report_file"),
|
||||
sg.SaveAs(
|
||||
"Create Report",
|
||||
key="boards_report",
|
||||
file_types=(("PDF Files", "*.pdf"),),
|
||||
target="boards_report_file",
|
||||
pad=((0, 5), (6, 0)),
|
||||
),
|
||||
],
|
||||
[
|
||||
sg.Button(
|
||||
"ALL",
|
||||
@@ -365,7 +374,7 @@ def get_command_layout():
|
||||
key="cmd_all",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((0, 1), (1, 1)),
|
||||
pad=((0, 5), (1, 1)),
|
||||
),
|
||||
sg.Button(
|
||||
"LIGHT",
|
||||
@@ -596,21 +605,21 @@ def get_config_layout():
|
||||
key="cfg_import",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((0, 5), (5, 0)),
|
||||
pad=((0, 5), (6, 0)),
|
||||
),
|
||||
sg.Button(
|
||||
"CONFIG",
|
||||
key="cfg_config",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((0, 5), (5, 0)),
|
||||
pad=((0, 5), (6, 0)),
|
||||
),
|
||||
sg.Button(
|
||||
"GENERATE",
|
||||
key="cfg_generate",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((0, 5), (5, 0)),
|
||||
pad=((0, 5), (6, 0)),
|
||||
),
|
||||
],
|
||||
[
|
||||
@@ -619,19 +628,21 @@ def get_config_layout():
|
||||
key="cfg_all",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((0, 5), (1, 1)),
|
||||
pad=((0, 5), (1, 0)),
|
||||
),
|
||||
sg.Button(
|
||||
"OPEN IN WEB",
|
||||
key="cfg_web",
|
||||
border_width=BTN_BORDER,
|
||||
disabled_button_color=BTN_DISABLED,
|
||||
pad=((5, 5), (3, 2)),
|
||||
),
|
||||
sg.Push(background_color=MAIN_TABS_BG),
|
||||
sg.Checkbox(
|
||||
"Append IP to Username",
|
||||
key="cfg_append_ip",
|
||||
background_color=MAIN_TABS_BG,
|
||||
pad=((5, 5), (3, 2)),
|
||||
),
|
||||
],
|
||||
[
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import PySimpleGUI as sg
|
||||
import asyncio
|
||||
import sys
|
||||
from tools.cfg_util.boards import boards_report
|
||||
from tools.cfg_util.imgs import TkImages
|
||||
from tools.cfg_util.scan import btn_scan
|
||||
from tools.cfg_util.commands import (
|
||||
@@ -132,6 +133,11 @@ async def ui():
|
||||
if event == "boards_refresh":
|
||||
_table = "boards_table"
|
||||
asyncio.create_task(btn_refresh(_table, value[_table]))
|
||||
if event == "boards_report_file":
|
||||
print(value["boards_report_file"])
|
||||
if not value["boards_report_file"] == "":
|
||||
asyncio.create_task(boards_report(value["boards_report_file"]))
|
||||
window["boards_report_file"].update("")
|
||||
|
||||
# pools tab
|
||||
if event == "pools_all":
|
||||
|
||||
Reference in New Issue
Block a user