reHDDPrinter/layouter.py

199 lines
8.2 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" Author: Hendrik Schutter, localhorst@mosad.xyz
Date of creation: 2022/11/16
Date of last modification: 2022/11/16
pip install qrcode
"""
import re
import os
import sys
import time
import subprocess
import shlex
import shutil
import dataclasses
import glob
import PIL
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import datetime
import json
import qrcode
@dataclasses.dataclass
class DriveData:
drive_index: int
drive_state: str #none, deleted, shredded
modelfamiliy: str
modelname: str
capacity: int #in bytes
serialnumber: str
power_on_hours: int #in hours
power_cycle: int
smart_error_count: int
shred_timestamp: int #unix timestamp
shred_duration: int #in seconds
@dataclasses.dataclass
class DriveDataPrintable:
modelfamiliy: str #max lenght 25
modelname: str #max lenght 25
capacity: str #max lenght 25, in human-readable format with unit (GB/TB)
serialnumber: str #max lenght 25
power_on_hours: str #max lenght 25, in hours and days and years
power_cycle: str #max lenght 25
smart_error_count: str #max lenght 25
shred_timestamp: str #max lenght 25, human-readable
shred_duration: str #max lenght 25, human-readable
@dataclasses.dataclass
class ReHddInfo:
link: str
version: str
@dataclasses.dataclass
class DriveDataJson:
drive: DriveData
rehdd: ReHddInfo
rehdd_info = ReHddInfo("https://git.mosad.xyz/localhorst/reHDD", "bV0.2.2") # read this from rehdd process
temp_drive = DriveData(drive_index=0, drive_state="shredded", modelfamiliy="Toshiba 2.5\\ HDD MK..65GSSX",\
modelname="TOSHIBA MK3265GSDX", capacity=343597383680, serialnumber="YG6742U56UDRL123", power_on_hours=7074,\
power_cycle=4792, smart_error_count=1, shred_timestamp=1647937421, shred_duration=81718)
def get_font_path_regular():
path = "/usr/share/fonts"
files = glob.glob(path + "/**/DejaVuSans.ttf", recursive = True)
return files[0]
def get_font_path_bold():
path = "/usr/share/fonts"
files = glob.glob(path + "/**/DejaVuSans-Bold.ttf", recursive = True)
return files[0]
def human_readable_capacity(size, decimal_places=0):
for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']:
if size < 1024.0 or unit == 'PiB':
break
size /= 1024.0
return f"{size:.{decimal_places}f} {unit}"
def human_readable_power_on_hours(hours, decimal_places=2):
return str(hours) + "h or " + str(int(hours/24)) + "d or " + str("{:.2f}".format(float(hours/24/365))) + "y"
def cut_string(max_lenght, data):
if (len(data) > max_lenght):
return data[0:(max_lenght-4)] + " ..."
else:
return data
def format_to_printable(drive):
#print(cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelfamiliy)))
#print(cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelname)))
#print(cut_string(20, human_readable_capacity(drive.capacity)))
#print(cut_string(20, re.sub(r"[^a-zA-Z0-9.-_]", "", drive.serialnumber)))
#print(cut_string(30, human_readable_power_on_hours(drive.power_on_hours)))
#print(cut_string(10, str(drive.power_cycle)))
#print(cut_string(10, str(drive.smart_error_count)))
#print(cut_string(30, datetime.datetime.utcfromtimestamp(drive.shred_timestamp).strftime('%Y-%m-%d %H:%M:%S')))
#print(cut_string(30, str(datetime.timedelta(seconds = drive.shred_duration))))
return DriveDataPrintable(
cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelfamiliy)),\
cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelname)),\
cut_string(20, human_readable_capacity(drive.capacity)),\
cut_string(16, re.sub(r"[^a-zA-Z0-9.-_]", "", drive.serialnumber)),\
cut_string(30, human_readable_power_on_hours(drive.power_on_hours)),\
cut_string(10, str(drive.power_cycle)),\
cut_string(10, str(drive.smart_error_count)),\
cut_string(30, datetime.datetime.utcfromtimestamp(drive.shred_timestamp).strftime('%Y-%m-%d %H:%M:%S')),\
cut_string(30, str(datetime.timedelta(seconds = drive.shred_duration))))
def draw_text(drawable, printable_data, text_x_offset):
font_file_regular = get_font_path_regular()
font_file_bold = get_font_path_bold()
font_size = 20
text_y_offset = 10
text_y_offset_increment = 25
value_colum_x_offset = 120
drawable.text((text_x_offset, text_y_offset), printable_data.serialnumber,(0),font=ImageFont.truetype(font_file_bold, 30))
text_y_offset += 40
drawable.text((text_x_offset, text_y_offset), "Familiy: ",(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.modelfamiliy,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Model: ",(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.modelname,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Hours: " ,(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.power_on_hours,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Cycles: ",(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.power_cycle,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Errors: ", (0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.smart_error_count,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Shred on: ",(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.shred_timestamp,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), "Duration: " ,(0),font=ImageFont.truetype(font_file_bold, font_size))
drawable.text((text_x_offset+value_colum_x_offset, text_y_offset), printable_data.shred_duration,(0),font=ImageFont.truetype(font_file_regular, font_size))
text_y_offset += text_y_offset_increment
drawable.text((text_x_offset, text_y_offset), printable_data.capacity,(0),font=ImageFont.truetype(font_file_bold, font_size*3))
def draw_outline(drawable, margin, width, output_width, output_height):
#upper
drawable.line([(margin,margin), (output_width -margin ,margin)], fill=None, width=width, joint=None)
#left
drawable.line([(margin,margin), (margin ,output_height-margin)], fill=None, width=width, joint=None)
#right
drawable.line([(output_width-margin,margin), (output_width-margin ,output_height-margin)], fill=None, width=width, joint=None)
#lower
drawable.line([(margin,output_height - margin), (output_width-margin,output_height -margin)], fill=None, width=width, joint=None)
def draw_qr_code(image, data):
qr_img = qrcode.make(data)
qr_img.thumbnail((291, 291), Image.Resampling.LANCZOS)
image.paste(qr_img, (7, 7))
def main():
output_width = 696
output_height = 300
printable_data = format_to_printable(temp_drive)
qr_data = DriveDataJson(temp_drive, rehdd_info)
json_qr_daten = json.dumps(dataclasses.asdict(qr_data))
#print(printable_data)
text_x_offset= 300
output_image = Image.new('1', (output_width, output_height), "white")
draw = ImageDraw.Draw(output_image)
draw_outline(draw, 1, 4, output_width, output_height)
draw_text(draw, printable_data, text_x_offset)
draw_qr_code(output_image, str(json_qr_daten).replace(" ", ""))
output_image.save("output.png")
if __name__ == "__main__":
main()