Skip to content

Commit 400c242

Browse files
committed
Move parse_number_and_unit() and NumberAndUnit definition to wv_utils.py
Remove unused attribute Remove unused `&&` in GitHub workflow Remove duplicate `category` attribute Removed from `Connector` class since it is already defined in the `Component` superclass. Remove unnecessary casting of `int` to `float` #251 (comment) Continue work on BOM handling (WIP)
1 parent 6f6235a commit 400c242

File tree

6 files changed

+207
-153
lines changed

6 files changed

+207
-153
lines changed

.github/workflows/main.yml

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@ jobs:
1010
matrix:
1111
python-version: ["3.8", "3.10"]
1212
steps:
13-
- uses: actions/checkout@v2
14-
- name: Set up Python ${{ matrix.python-version }}
15-
uses: actions/setup-python@v2
16-
with:
17-
python-version: ${{ matrix.python-version }}
18-
- name: Setup Graphviz
19-
uses: ts-graphviz/setup-graphviz@v1
20-
- name: Install dependencies
21-
run: |
22-
python -m pip install --upgrade pip
23-
pip install .
24-
- name: Create Examples
25-
run: PYTHONPATH=$(pwd)/src:$PYTHONPATH && python src/wireviz/tools/build_examples.py
26-
- name: Upload examples, demos, and tutorials
27-
uses: actions/upload-artifact@v2
28-
with:
29-
name: examples-and-tutorials
30-
path: |
31-
examples/
32-
tutorial/
13+
- uses: actions/checkout@v2
14+
- name: Set up Python ${{ matrix.python-version }}
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- name: Setup Graphviz
19+
uses: ts-graphviz/setup-graphviz@v1
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install .
24+
- name: Create Examples
25+
run: PYTHONPATH=$(pwd)/src:$PYTHONPATH python src/wireviz/tools/build_examples.py
26+
- name: Upload examples, demos, and tutorials
27+
uses: actions/upload-artifact@v2
28+
with:
29+
name: examples-and-tutorials
30+
path: |
31+
examples/
32+
tutorial/

src/wireviz/wv_dataclasses.py

Lines changed: 79 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
SingleColor,
2121
get_color_by_colorcode_index,
2222
)
23-
from wireviz.wv_utils import aspect_ratio, awg_equiv, mm2_equiv, remove_links
23+
from wireviz.wv_utils import (
24+
NumberAndUnit,
25+
awg_equiv,
26+
aspect_ratio,
27+
mm2_equiv,
28+
parse_number_and_unit,
29+
remove_links,
30+
)
2431

2532
# Each type alias have their legal values described in comments
2633
# - validation might be implemented in the future
@@ -54,7 +61,6 @@
5461
Side = Enum("Side", "LEFT RIGHT")
5562
ArrowDirection = Enum("ArrowDirection", "NONE BACK FORWARD BOTH")
5663
ArrowWeight = Enum("ArrowWeight", "SINGLE DOUBLE")
57-
NumberAndUnit = namedtuple("NumberAndUnit", "number unit")
5864

5965
AUTOGENERATED_PREFIX = "AUTOGENERATED_"
6066

@@ -184,7 +190,7 @@ class Component:
184190
supplier: str = None
185191
spn: str = None
186192
# BOM info
187-
qty: NumberAndUnit = NumberAndUnit(1, None)
193+
qty: Optional[Union[None, int, float]] = None
188194
amount: Optional[NumberAndUnit] = None
189195
sum_amounts_in_bom: bool = True
190196
ignore_in_bom: bool = False
@@ -195,70 +201,31 @@ def __post_init__(self):
195201
partnos = [remove_links(entry) for entry in partnos]
196202
partnos = tuple(partnos)
197203
self.partnumbers = PartNumberInfo(*partnos)
198-
199-
self.qty = self.parse_number_and_unit(self.qty, None)
200-
self.amount = self.parse_number_and_unit(self.amount, None)
201-
202-
def parse_number_and_unit(
203-
self,
204-
inp: Optional[Union[NumberAndUnit, float, int, str]],
205-
default_unit: Optional[str] = None,
206-
) -> Optional[NumberAndUnit]:
207-
if inp is None:
208-
return None
209-
elif isinstance(inp, NumberAndUnit):
210-
return inp
211-
elif isinstance(inp, float) or isinstance(inp, int):
212-
return NumberAndUnit(float(inp), default_unit)
213-
elif isinstance(inp, str):
214-
if " " in inp:
215-
number, unit = inp.split(" ", 1)
216-
else:
217-
number, unit = inp, default_unit
218-
try:
219-
number = float(number)
220-
except ValueError:
221-
raise Exception(
222-
f"{inp} is not a valid number and unit.\n"
223-
"It must be a number, or a number and unit separated by a space."
224-
)
225-
else:
226-
return NumberAndUnit(number, unit)
204+
self.amount = parse_number_and_unit(self.amount, None)
227205

228206
@property
229207
def bom_hash(self) -> BomHash:
208+
if isinstance(self, AdditionalComponent):
209+
_amount = self.amount_computed
210+
else:
211+
_amount = self.amount
212+
230213
if self.sum_amounts_in_bom:
231214
_hash = BomHash(
232215
description=self.description,
233-
qty_unit=self.amount.unit if self.amount else None,
216+
qty_unit=_amount.unit if _amount else None,
234217
amount=None,
235218
partnumbers=self.partnumbers,
236219
)
237220
else:
238221
_hash = BomHash(
239222
description=self.description,
240-
qty_unit=self.qty.unit,
241-
amount=self.amount,
223+
qty_unit=None,
224+
amount=_amount,
242225
partnumbers=self.partnumbers,
243226
)
244227
return _hash
245228

246-
@property
247-
def bom_qty(self) -> float:
248-
if self.sum_amounts_in_bom:
249-
if self.amount:
250-
return self.qty.number * self.amount.number
251-
else:
252-
return self.qty.number
253-
else:
254-
return self.qty.number
255-
256-
def bom_amount(self) -> NumberAndUnit:
257-
if self.sum_amounts_in_bom:
258-
return NumberAndUnit(None, None)
259-
else:
260-
return self.amount
261-
262229
@property
263230
def has_pn_info(self) -> bool:
264231
return any([self.pn, self.manufacturer, self.mpn, self.supplier, self.spn])
@@ -292,7 +259,9 @@ def __post_init__(self):
292259
@dataclass
293260
class AdditionalComponent(GraphicalComponent):
294261
qty_multiplier: Union[QtyMultiplierConnector, QtyMultiplierCable, int] = 1
295-
_qty_multiplier_computed: Union[int, float] = 1
262+
qty_computed: Optional[int] = None
263+
explicit_qty: bool = True
264+
amount_computed: Optional[NumberAndUnit] = None
296265
note: str = None
297266

298267
def __post_init__(self):
@@ -311,9 +280,13 @@ def __post_init__(self):
311280
else:
312281
raise Exception(f"Unknown qty multiplier: {self.qty_multiplier}")
313282

314-
@property
315-
def bom_qty(self):
316-
return self.qty.number * self._qty_multiplier_computed
283+
if self.qty is None and self.qty_multiplier in [
284+
QtyMultiplierCable.TOTAL_LENGTH,
285+
QtyMultiplierCable.LENGTH,
286+
1,
287+
]: # simplify add.comp. table in parent node for implicit qty 1
288+
self.qty = 1
289+
self.explicit_qty = False
317290

318291

319292
@dataclass
@@ -325,8 +298,6 @@ class TopLevelGraphicalComponent(GraphicalComponent): # abstract class
325298
additional_parameters: Optional[Dict] = None
326299
additional_components: List[AdditionalComponent] = field(default_factory=list)
327300
notes: Optional[MultilineHypertext] = None
328-
# BOM options
329-
add_up_in_bom: Optional[bool] = None
330301
# rendering options
331302
bgcolor_title: Optional[SingleColor] = None
332303
show_name: Optional[bool] = None
@@ -336,7 +307,6 @@ class TopLevelGraphicalComponent(GraphicalComponent): # abstract class
336307
class Connector(TopLevelGraphicalComponent):
337308
# connector-specific properties
338309
style: Optional[str] = None
339-
category: Optional[str] = None
340310
loops: List[List[Pin]] = field(default_factory=list)
341311
# pin information in particular
342312
pincount: Optional[int] = None
@@ -380,13 +350,12 @@ def __post_init__(self) -> None:
380350
self.color = MultiColor(self.color)
381351

382352
# connectors do not support custom qty or amount
383-
if self.qty != NumberAndUnit(1, None):
353+
if self.qty is None:
354+
self.qty = 1
355+
if self.qty != 1:
384356
raise Exception("Connector qty != 1 not supported")
385357
if self.amount is not None:
386358
raise Exception("Connector amount not supported")
387-
# TODO: Delete next two assignments if tests above is sufficient. Please verify!
388-
self.qty = NumberAndUnit(1, None)
389-
self.amount = None
390359

391360
if isinstance(self.image, dict):
392361
self.image = Image(**self.image)
@@ -451,7 +420,9 @@ def __post_init__(self) -> None:
451420
raise Exception("Loops must be between exactly two pins!")
452421
for pin in loop:
453422
if pin not in self.pins:
454-
raise Exception(f'Unknown loop pin "{pin}" for connector "{self.name}"!')
423+
raise Exception(
424+
f'Unknown loop pin "{pin}" for connector "{self.name}"!'
425+
)
455426
# Make sure loop connected pins are not hidden.
456427
# side=None, determine side to show loops during rendering
457428
self.activate_pin(pin, side=None, is_connection=True)
@@ -488,7 +459,12 @@ def compute_qty_multipliers(self):
488459
raise Exception("Used a cable multiplier in a connector!")
489460
else: # int or float
490461
computed_factor = subitem.qty_multiplier
491-
subitem._qty_multiplier_computed = computed_factor
462+
463+
if subitem.qty is not None:
464+
subitem.qty_computed = subitem.qty * computed_factor
465+
else:
466+
subitem.qty_computed = computed_factor
467+
subitem.amount_computed = subitem.amount
492468

493469

494470
@dataclass
@@ -623,14 +599,17 @@ def length_str(self):
623599
@property
624600
def bom_hash(self):
625601
if self.category == "bundle":
626-
raise Exception("Do this at the wire level!") # TODO
602+
# This line should never be reached, since caller checks
603+
# whether item is a bundle and if so, calls bom_hash
604+
# for each individual wire instead
605+
raise Exception("Do this at the wire level!")
627606
else:
628607
return super().bom_hash
629608

630609
@property
631610
def description(self) -> str:
632611
if self.category == "bundle":
633-
raise Exception("Do this at the wire level!") # TODO
612+
raise Exception("Do this at the wire level!")
634613
else:
635614
substrs = [
636615
("", "Cable"),
@@ -668,15 +647,21 @@ def __post_init__(self) -> None:
668647
self.bgcolor_title = SingleColor(self.bgcolor_title)
669648
self.color = MultiColor(self.color)
670649

650+
# cables do not support custom qty or amount
651+
if self.qty is None:
652+
self.qty = 1
653+
if self.qty != 1:
654+
raise Exception("Cable qty != 1 not supported")
655+
671656
if isinstance(self.image, dict):
672657
self.image = Image(**self.image)
673658

674659
# TODO:
675660
# allow gauge, length, and other fields to be lists too (like part numbers),
676661
# and assign them the same way to bundles.
677662

678-
self.gauge = self.parse_number_and_unit(self.gauge, "mm2")
679-
self.length = self.parse_number_and_unit(self.length, "m")
663+
self.gauge = parse_number_and_unit(self.gauge, "mm2")
664+
self.length = parse_number_and_unit(self.length, "m")
680665
self.amount = self.length # for BOM
681666

682667
if self.wirecount: # number of wires explicitly defined
@@ -753,9 +738,11 @@ def __post_init__(self) -> None:
753738
index=index_offset,
754739
id=id,
755740
label="Shield",
756-
color=MultiColor(self.shield)
757-
if isinstance(self.shield, str)
758-
else MultiColor(None),
741+
color=(
742+
MultiColor(self.shield)
743+
if isinstance(self.shield, str)
744+
else MultiColor(None)
745+
),
759746
parent=self.designator,
760747
)
761748

@@ -789,27 +776,40 @@ def compute_qty_multipliers(self):
789776
)
790777
qty_multipliers_computed = {
791778
"WIRECOUNT": len(self.wire_objects),
792-
"TERMINATIONS": 999, # TODO
779+
# "TERMINATIONS": ___, # TODO
793780
"LENGTH": self.length.number if self.length else 0,
794781
"TOTAL_LENGTH": total_length,
795782
}
796783
for subitem in self.additional_components:
797784
if isinstance(subitem.qty_multiplier, QtyMultiplierCable):
798785
computed_factor = qty_multipliers_computed[subitem.qty_multiplier.name]
799-
# inherit component's length unit if appropriate
800786
if subitem.qty_multiplier.name in ["LENGTH", "TOTAL_LENGTH"]:
801-
if subitem.qty.unit is not None:
787+
# since length can have a unit, use amount fields to hold
788+
if subitem.amount is not None:
802789
raise Exception(
803-
f"No unit may be specified when using"
804-
f"{subitem.qty_multiplier} as a multiplier"
790+
f"No amount may be specified when using "
791+
f"{subitem.qty_multiplier.name} as a multiplier."
805792
)
806-
subitem.qty = NumberAndUnit(subitem.qty.number, self.length.unit)
793+
subitem.qty_computed = subitem.qty if subitem.qty else 1
794+
subitem.amount_computed = NumberAndUnit(
795+
computed_factor, self.length.unit
796+
)
797+
else:
798+
# multiplier unrelated to length, therefore no unit
799+
if subitem.qty is not None:
800+
subitem.qty_computed = subitem.qty * computed_factor
801+
else:
802+
subitem.qty_computed = computed_factor
803+
subitem.amount_computed = subitem.amount
807804

808805
elif isinstance(subitem.qty_multiplier, QtyMultiplierConnector):
809806
raise Exception("Used a connector multiplier in a cable!")
810807
else: # int or float
811-
computed_factor = subitem.qty_multiplier
812-
subitem._qty_multiplier_computed = computed_factor
808+
if subitem.qty is not None:
809+
subitem.qty_computed = subitem.qty * subitem.qty_multiplier
810+
else:
811+
subitem.qty_computed = subitem.qty_multiplier
812+
subitem.amount_computed = subitem.amount
813813

814814

815815
@dataclass

0 commit comments

Comments
 (0)