import re import json from collections import Counter from typing import Any def parse_gcode_metadata(gcode: str) -> dict[str, Any]: """Extracts metadata fields from G-code comments.""" fields = {} # 1. Time per print (decimal hours) time_match = re.search(r"; estimated printing time \(normal mode\) = (.*)", gcode) if time_match: time_str = time_match.group(1) h = re.search(r"(\d+)h", time_str) m = re.search(r"(\d+)m", time_str) s = re.search(r"(\d+)s", time_str) total_hours = 0.0 if h: total_hours += float(h.group(1)) if m: total_hours += float(m.group(1)) / 60.0 if s: total_hours += float(s.group(1)) / 3600.0 fields["Time per print"] = round(total_hours, 2) # 2. Parts per print parts_match = re.search(r"; objects_info = (.*)", gcode) if parts_match: try: info = json.loads(parts_match.group(1)) fields["Parts per print"] = len(info.get("objects", [])) fields["objects_info"] = info except Exception: pass # 3. Weight per print weight_match = re.search(r"; total filament used \[g\] = ([\d\.]+)", gcode) if weight_match: fields["Weight per print"] = int(float(weight_match.group(1))) return fields def get_gcode_name_from_metadata(gcode: str, default: str = "output") -> str: """Generates a descriptive name for the G-code based on its objects_info metadata.""" parts_match = re.search(r"; objects_info = (.*)", gcode) filament_match = re.search(r"; filament_type = (.*)", gcode) filament = filament_match[1] if filament_match else '' printer_match = re.search(r"; printer_model = (.*)", gcode) printer = printer_match[1] if printer_match else '' if parts_match: try: gcode_info = json.loads(parts_match.group(1)) names: list[str] = [str(v['name'].split(" id:")[0]) for v in gcode_info['objects']] counts = Counter(names) object_names = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items()) parts = [object_names, filament, printer] return "-".join([str(p) for p in parts if p]) except: pass return default