helpers.py (5676B)
1 #!/usr/bin/env python3 2 # -*- coding: utf-8 -*- 3 # vim:fenc=utf-8 4 5 import json 6 import subprocess 7 import tempfile 8 import time 9 10 11 def print_parts_list(parts, output="full"): 12 if output == "short": 13 _list_ascii_short(parts) 14 if output == "full": 15 _list_ascii(parts) 16 if output == "json": 17 _list_json(parts) 18 19 20 def _list_ascii(parts): 21 # max takes an iterable and key is a function where iterables are 22 # passed and comparison performed, so we look at the longest desc to 23 # get the lenght of the field. We also check if the header is longer, 24 # the result is whatever is longer 25 l_pn = len(max(parts, key=lambda k: len(k["pn"]))["pn"]) 26 l_cat = len(max(parts, key=lambda k: len(k["cname"]))["cname"]) 27 l_man = len(max(parts, key=lambda k: len(k["manufacturer"]))["manufacturer"]) 28 l_desc = len(max(parts, key=lambda k: len(k["description"]))["description"]) 29 l_cat = l_cat if l_cat > len("Category") else len("Category") 30 l_man = l_man if l_man > len("Manufacturer") else len("Manufacturer") 31 l_desc = l_desc if l_desc > len("Description") else len("Description") 32 33 header = ( 34 f"| {'ID':5} | {'PN':{l_pn}} | " 35 f"{'Category':{l_cat}} | " 36 f"{'Manufacturer':{l_man}} | " 37 f"{'Description':{l_desc}} | " 38 f"{'Type':4} | " 39 f"{'Footp':6} | " 40 f"{'Qty':4} |" 41 ) 42 for i, p in enumerate(parts): 43 if i % 25 == 0: 44 print("-" * len(header)) 45 print(header) 46 print("-" * len(header)) 47 print( 48 f"| {p['id']:<5} | {p['pn']:{l_pn}} | " 49 f"{p['cname']:{l_cat}} | " 50 f"{_sanitize_value(p['manufacturer']):{l_man}} | " 51 f"{_sanitize_value(p['description']):{l_desc}} | " 52 f"{_sanitize_value(p['part_type'])[0:3]:4} | " 53 f"{_sanitize_value(p['footprint'])[0:5]:6} | " 54 f"{p['quantity']:4} |" 55 ) 56 57 58 def _list_ascii_short(parts): 59 header = ( 60 f"| {'ID':4} | " 61 f"{'Category':8} | " 62 f"{'PN':10} | " 63 f"{'Manufacturer':16} | " 64 f"{'Description':25} |" 65 ) 66 for i, p in enumerate(parts): 67 if i % 25 == 0: 68 print("-" * 79) 69 print(header) 70 print("-" * 79) 71 print( 72 f"| {p['id']:<4} | " 73 f"{p['cname'][0:7]:8} | " 74 f"{_sanitize_value(p['pn'])[0:9]:10} | " 75 f"{_sanitize_value(p['manufacturer'])[0:15]:16} | " 76 f"{_sanitize_value(p['description'])[0:24]:25} |" 77 ) 78 79 80 def _list_json(parts): 81 p = [dict(zip(part.keys(), part)) for part in parts] 82 print(json.dumps({"parts": p})) 83 84 85 def print_part(p, history, output="full"): 86 if output == "full": 87 _part_ascii(p, history) 88 if output == "json": 89 _part_json(p, history) 90 91 92 def _part_ascii(p, history): 93 print(f"PN: {p['pn']}\tManufacturer: {p['manufacturer']}\tMPN: {p['mpn']}") 94 print( 95 f"Category: {p['cat']}\tType: {p['part_type']}" f"\tFootprint: {p['footprint']}" 96 ) 97 print(f"Storage: {p['storage']}") 98 print(f"Created: {p['insert_date']}" f"\tUpdated: {p['update_date']}\n") 99 print(f"Description:\n{p['description']}\n") 100 print(f"Specs:\n{p['specs']}") 101 102 if p["datasheet"] is not None: 103 print("This part has a datasheet available.") 104 if p["image"] is not None: 105 print("This part has an image available.") 106 107 print(f"\nQuantity: {p['quantity']}") 108 # here should go the historical data. 109 if history: 110 print("History:") 111 for h in history: 112 print(f"{h['insert_date']} | " f"{h['movement']:4} | " f"{h['mcomment']}") 113 114 115 def _part_json(part, history): 116 part = dict(zip(part.keys(), part)) 117 # remove bytes data if present. For now is useless in this output. 118 del part["datasheet"] 119 del part["image"] 120 121 part["history"] = [dict(zip(h.keys(), h)) for h in history] 122 123 print(json.dumps({"part": part})) 124 125 126 def _sanitize_value(value): 127 if value is None: 128 return "-" 129 return value 130 131 132 def open_file(content, extension): 133 with tempfile.NamedTemporaryFile(suffix=extension) as f: 134 f.write(content) 135 subprocess.Popen(["xdg-open", f.name], start_new_session=True) 136 time.sleep(2) 137 138 139 def html_main_index(dest_folder, categories, storages, env): 140 tpl = env.get_template("index.html") 141 with open(f"{dest_folder}/index.html", "w") as f: 142 f.write(tpl.render(cat=categories, sto=storages)) 143 144 145 def html_category_index(dest_folder, category, parts, env): 146 tpl = env.get_template("cat.html") 147 with open(f"{dest_folder}/cat_list_{category['id']}.html", "w") as f: 148 f.write(tpl.render(category=category, parts=parts)) 149 150 151 def html_storage_index(dest_folder, storage, parts, env): 152 tpl = env.get_template("storage.html") 153 with open(f"{dest_folder}/storage_{storage['id']}.html", "w") as f: 154 f.write(tpl.render(storage=storage, parts=parts)) 155 156 157 def html_part(dest_folder, part, part_history, env): 158 tpl = env.get_template("part.html") 159 with open(f"{dest_folder}/part_{part['id']}.html", "w") as f: 160 f.write(tpl.render(part=part, history=part_history)) 161 162 163 def html_css(dest_folder, env): 164 tpl = env.get_template("style.css") 165 with open(f"{dest_folder}/style.css", "w") as f: 166 f.write(tpl.render()) 167 168 169 def html_attachments(dest_folder, part_id, datasheet, image): 170 if datasheet["datasheet"] is not None: 171 with open(f"{dest_folder}/part_datasheet_{part_id}.pdf", "wb") as f: 172 f.write(datasheet["datasheet"]) 173 if image["image"] is not None: 174 with open(f"{dest_folder}/part_image_{part_id}.jpg", "wb") as f: 175 f.write(image["image"])