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) if parts_match: try: gcode_info = json.loads(parts_match.group(1)) names: list[str] = [str(v['name'].split(".")[0]) for v in gcode_info['objects']] counts = Counter(names) return "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items()) except: pass return default