From c993d9db6ff2db7430e06b1f33a61c3fcdc7c839 Mon Sep 17 00:00:00 2001 From: napowderly Date: Mon, 11 Nov 2024 13:14:27 -0800 Subject: [PATCH 1/5] add rich table for BOM printout --- examples/minimal_led.py | 7 +- .../exporters/documentation/reports.py | 76 +++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/faebryk/exporters/documentation/reports.py diff --git a/examples/minimal_led.py b/examples/minimal_led.py index a48349a7..060aaa01 100644 --- a/examples/minimal_led.py +++ b/examples/minimal_led.py @@ -11,6 +11,7 @@ import faebryk.library._F as F from faebryk.core.module import Module +from faebryk.exporters.documentation.reports import print_bom from faebryk.libs.brightness import TypicalLuminousIntensity from faebryk.libs.examples.buildutil import apply_design_to_pcb from faebryk.libs.logging import setup_basic_logging @@ -32,9 +33,6 @@ def __preinit__(self) -> None: ) -# Boilerplate ----------------------------------------------------------------- - - def main(): logger.info("Building app") app = App() @@ -42,6 +40,9 @@ def main(): logger.info("Export") apply_design_to_pcb(app) + logger.info("Generating BOM") + print_bom(app) + if __name__ == "__main__": setup_basic_logging() diff --git a/src/faebryk/exporters/documentation/reports.py b/src/faebryk/exporters/documentation/reports.py new file mode 100644 index 00000000..416f7bf9 --- /dev/null +++ b/src/faebryk/exporters/documentation/reports.py @@ -0,0 +1,76 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +""" +This file contains BOM generation utilities. +""" + +import logging + +from rich.console import Console +from rich.table import Table + +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.picker.picker import DescriptiveProperties + +logger = logging.getLogger(__name__) + + +def print_bom(root_module: Module) -> None: + """ + Print a Bill of Materials table for the given root module. + + Args: + root_module: The root module containing the components to list in the BOM + """ + console = Console() + table = Table(title="BOM") + + # Add columns + table.add_column("Name", style="white") + table.add_column("Designator", style="cyan") + table.add_column("Footprint", style="green") + table.add_column("LCSC", style="yellow") + table.add_column("Manufacturer PN", style="magenta") + + components = root_module.get_children_modules(types=(Module,)) + + for comp in components: + # Only process components that have required traits + if not all([ + comp.has_trait(F.has_designator), + comp.has_trait(F.has_footprint), + comp.has_trait(F.has_descriptive_properties) + ]): + continue + + # Get component name + comp_name = comp.get_name() + + # Get designator + designator = comp.get_trait(F.has_designator).get_designator() + + # Get footprint + footprint = comp.get_trait(F.has_footprint).get_footprint() + footprint_name = ( + footprint.get_trait(F.has_kicad_footprint).get_kicad_footprint_name() + if footprint.has_trait(F.has_kicad_footprint) + else str(footprint) + ) + + # Get part numbers + properties = comp.get_trait(F.has_descriptive_properties).get_properties() + mfr_pn = properties.get(DescriptiveProperties.partno, "N/A") + lcsc_pn = properties.get("LCSC", "N/A") + + # Add row to table + table.add_row( + comp_name, + designator, + footprint_name, + lcsc_pn, + mfr_pn + ) + + console.print(table) From bdc33819546d6e6501d75651ad6814c2d7624161 Mon Sep 17 00:00:00 2001 From: napowderly Date: Mon, 11 Nov 2024 13:17:08 -0800 Subject: [PATCH 2/5] Remove unnessesary change from example --- examples/minimal_led.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/minimal_led.py b/examples/minimal_led.py index 060aaa01..9c4d21ae 100644 --- a/examples/minimal_led.py +++ b/examples/minimal_led.py @@ -32,6 +32,8 @@ def __preinit__(self) -> None: TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) +# Boilerplate ----------------------------------------------------------------- + def main(): logger.info("Building app") From cde150c2892a399b87dc7b73d8352136252b6323 Mon Sep 17 00:00:00 2001 From: napowderly Date: Mon, 11 Nov 2024 13:17:54 -0800 Subject: [PATCH 3/5] add new line --- examples/minimal_led.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/minimal_led.py b/examples/minimal_led.py index 9c4d21ae..7d41996e 100644 --- a/examples/minimal_led.py +++ b/examples/minimal_led.py @@ -32,6 +32,7 @@ def __preinit__(self) -> None: TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) + # Boilerplate ----------------------------------------------------------------- From 9ee6309c3b0caa76e9908bcdd255d29edf2f0e14 Mon Sep 17 00:00:00 2001 From: napowderly Date: Mon, 11 Nov 2024 13:20:18 -0800 Subject: [PATCH 4/5] Black format reports.py --- .../exporters/documentation/reports.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/faebryk/exporters/documentation/reports.py b/src/faebryk/exporters/documentation/reports.py index 416f7bf9..e97fcdb9 100644 --- a/src/faebryk/exporters/documentation/reports.py +++ b/src/faebryk/exporters/documentation/reports.py @@ -38,11 +38,13 @@ def print_bom(root_module: Module) -> None: for comp in components: # Only process components that have required traits - if not all([ - comp.has_trait(F.has_designator), - comp.has_trait(F.has_footprint), - comp.has_trait(F.has_descriptive_properties) - ]): + if not all( + [ + comp.has_trait(F.has_designator), + comp.has_trait(F.has_footprint), + comp.has_trait(F.has_descriptive_properties), + ] + ): continue # Get component name @@ -65,12 +67,6 @@ def print_bom(root_module: Module) -> None: lcsc_pn = properties.get("LCSC", "N/A") # Add row to table - table.add_row( - comp_name, - designator, - footprint_name, - lcsc_pn, - mfr_pn - ) + table.add_row(comp_name, designator, footprint_name, lcsc_pn, mfr_pn) console.print(table) From 1bdf93776c0ecee401f859fce4dde0d1eb136815 Mon Sep 17 00:00:00 2001 From: napowderly Date: Mon, 11 Nov 2024 13:39:10 -0800 Subject: [PATCH 5/5] cleanup docstring --- .../exporters/documentation/reports.py | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/faebryk/exporters/documentation/reports.py b/src/faebryk/exporters/documentation/reports.py index e97fcdb9..1af2bbe9 100644 --- a/src/faebryk/exporters/documentation/reports.py +++ b/src/faebryk/exporters/documentation/reports.py @@ -16,23 +16,39 @@ logger = logging.getLogger(__name__) +# At module level +REQUIRED_TRAITS = (F.has_designator, F.has_footprint, F.has_descriptive_properties) + +BOM_COLUMNS = [ + ("Name", "white"), + ("Designator", "cyan"), + ("Footprint", "green"), + ("LCSC", "yellow"), + ("Manufacturer PN", "magenta"), +] + def print_bom(root_module: Module) -> None: - """ - Print a Bill of Materials table for the given root module. + """Print a Bill of Materials table for the given root module. + + Generates a rich-formatted table containing component information including + names, designators, footprints, and part numbers. Only processes components + that have designator, footprint, and descriptive properties traits. Args: root_module: The root module containing the components to list in the BOM + + Required component traits: + - has_designator + - has_footprint + - has_descriptive_properties """ console = Console() table = Table(title="BOM") - # Add columns - table.add_column("Name", style="white") - table.add_column("Designator", style="cyan") - table.add_column("Footprint", style="green") - table.add_column("LCSC", style="yellow") - table.add_column("Manufacturer PN", style="magenta") + # Simplify column addition + for column_name, style in BOM_COLUMNS: + table.add_column(column_name, style=style) components = root_module.get_children_modules(types=(Module,))