From: Keith Packard Date: Sun, 22 Jun 2025 21:37:30 +0000 (-0700) Subject: Make tab and csv part import 'the same'. X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=3f5d8d33b1cb2c31afb42489d5a1c239fdcf7536;p=hw%2Faltusmetrum Make tab and csv part import 'the same'. .tab files have one line per refdes, .csv files have one line per component. When importing a .tab file, merge matching components by sorting-in another refdes. When importing a .csv file, sort the refdes numerically. When dumping a .tab file, iterate over the refdes and emit duplicate lines. Signed-off-by: Keith Packard --- diff --git a/bin/parts.py b/bin/parts.py index 5d3ce8c..d7402a2 100644 --- a/bin/parts.py +++ b/bin/parts.py @@ -34,14 +34,21 @@ key_attrs = ('device', 'value', 'footprint') # This is the preferred order when writing a CSV file pref_order_csv = ('device', 'value', 'footprint', 'loadstatus', 'provided', 'mfg', - 'mfg_part_number', 'vendor', 'vendor_part_number', 'quantity', 'refdes') + 'mfg_part_number', 'vendor', 'vendor_part_number', 'quantity', 'refdes', 'pnpformat') pref_order_tab = ('refdes', 'device', 'value', 'footprint', 'loadstatus', 'provided', 'mfg', - 'mfg_part_number', 'vendor', 'vendor_part_number', 'quantity') + 'mfg_part_number', 'vendor', 'vendor_part_number', 'quantity', 'pnpformat') value_pattern=r'([0-9]+)(\.[0-9]*)?([kmMupng]?)(F|H|Hz|V|screws)?([ _][0-9]+(\.[0-9]*))?' refdes_pattern=r'([a-zA-Z]+)([0-9]+)' +def refdes_number(refdes): + mr = re.fullmatch(refdes_pattern, refdes) + if mr: + return int(mr.group(2)) + ord(mr.group(1)) + raise ValueError('Invalid refdes %s' % (refdes,)) + + class Part(): """ A single part containing a dictionary with all of the attributes @@ -58,6 +65,14 @@ class Part(): for i in range(len(values)): if values[i]: self.attrs[keys[i]] = values[i] + if not self.get('quantity'): + self.set('quantity', '1') + refdes = self.get_refdes() + if refdes: + self.set('refdes', " ".join(sorted(refdes, key=refdes_number))) + + def __str__(self): + return self.attrs.__str__() # Get an attribute value, returning None for # missing attributes @@ -66,6 +81,28 @@ class Part(): return self.attrs[attr] return None + # Get the list of refdes for a part + def get_refdes(self): + refdes = self.get('refdes') + if not refdes: + return () + return refdes.split() + + # Add a refdes to a part. Refdes is a space-separated string + def add_refdes(self, refdes): + if not refdes: + return + my_refdes = self.get('refdes') + quantity = self.get('quantity') + if quantity: + quantity = str(int(quantity) + 1) + else: + quantity = str(1) + if my_refdes: + refdes = my_refdes + " " + refdes + self.set('refdes', " ".join(sorted(refdes.split(), key=refdes_number))) + self.set('quantity', quantity) + # Get an attribute value in lower case def get_lower(self, attr): if attr in self.attrs: @@ -157,7 +194,10 @@ class Parts(): # Add/replace a part, computing the key def set(self, part): key = part.key() - self.parts[key] = part + if key in self.parts: + self.parts[key].add_refdes(part.get('refdes')) + else: + self.parts[key] = part # Import from a CSV file object def import_csv_file(self, infile): @@ -273,7 +313,10 @@ class Parts(): refdes = 'unknown' if part is not None: refdes = part.get_unknown('refdes') - + try: + refdes = refdes[0] + except: + pass mr = re.fullmatch(refdes_pattern, refdes) if mr: category = mr.group(1) @@ -329,7 +372,11 @@ class Parts(): keys = sorted(list(self.parts), key=self.cmp_tab_key) for key in keys: part = self.get(key) - print("\t".join(tuple(map(part.get_unknown, attrs))), file=outfile) + orig_refdes = part.get('refdes') + for refdes in part.get_refdes(): + part.set('refdes', refdes) + print("\t".join(tuple(map(part.get_unknown, attrs))), file=outfile) + part.set('refdes', orig_refdes) # Export to a tab-delimited file def export_tab(self, outname):