Merge pull request 'Upgrade to py-allspice 3.0.0' (#5) from jt/upgrade-py-allspice into main
Reviewed-on: Actions/generate-bom-altium#5 Reviewed-by: shrikanth-allspice <shrikanth-allspice@noreply.hub.allspice.io>
This commit is contained in:
commit
694b7e5ccf
@ -2,6 +2,6 @@ FROM python:3.12-bookworm
|
||||
|
||||
COPY entrypoint.py /entrypoint.py
|
||||
|
||||
RUN pip install py-allspice~=2.5
|
||||
RUN pip install py-allspice~=3.0
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.py" ]
|
||||
|
70
README.md
70
README.md
@ -1,25 +1,71 @@
|
||||
# Generate BOM for Altium Projects
|
||||
|
||||
Generate a BOM for an Altium project on AllSpice Hub using py-allspice. This
|
||||
currently uses the PCB file for computing quantities.
|
||||
Generate a BOM for an Altium project on AllSpice Hub using py-allspice.
|
||||
|
||||
## Usage
|
||||
|
||||
Add the following step to your actions:
|
||||
Add the following steps to your actions:
|
||||
|
||||
```yaml
|
||||
# Checkout is only needed if columns.json is committed in the repo.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate BOM
|
||||
uses: https://hub.allspice.io/Actions/generate-bom-altium@main
|
||||
with:
|
||||
project_path: Archimajor.PrjPcb
|
||||
pcb_path: Archimajor.PcbDoc
|
||||
columns: .allspice/columns.json
|
||||
output_file_name: bom.csv
|
||||
attributes_mapping: '
|
||||
{
|
||||
"description": ["PART DESCRIPTION"],
|
||||
"designator": ["Designator"],
|
||||
"manufacturer": ["Manufacturer", "MANUFACTURER"],
|
||||
"part_number": ["PART", "MANUFACTURER #"]
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
where `.allspice/columns.json` looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"part_number": ["PART", "MANUFACTURER #", "MPN"],
|
||||
"manufacturer": ["Manufacturer", "MANUFACTURER", "MFG", "Mfg"],
|
||||
"designator": ["Designator", "REFDES", "Refdes", "Ref"],
|
||||
"part_id": ["_part_id"],
|
||||
"description": ["PART DESCRIPTION", "_description"]
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing the Attributes Extracted by the BOM Script
|
||||
|
||||
This script relies on a `columns.json` file. This file maps the Component
|
||||
Attributes in the SchDoc files to the columns of the BOM. An example for
|
||||
`columns.json` is:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": ["PART DESCRIPTION"],
|
||||
"designator": ["Designator"],
|
||||
"manufacturer": ["Manufacturer", "MANUFACTURER"],
|
||||
"part_number": ["PART", "MANUFACTURER #"]
|
||||
}
|
||||
```
|
||||
|
||||
In this file, the keys are the names of the columns in the BOM, and the values
|
||||
are a list of the names of the attributes in the SchDoc files that should be
|
||||
mapped to that column. For example, if your part number is stored either in the
|
||||
`PART` or `MANUFACTURER #` attribute, you would add both of those to the list.
|
||||
If there is only one attribute, you can omit the list and just use a string. The
|
||||
script checks these attributes in order, and uses the _first_ one it finds. So
|
||||
if both `PART` and `MANUFACTURER #` are defined, it will use `PART`.
|
||||
|
||||
Note that py-allspice also adds two attributes: `_part_id` and `_description`.
|
||||
These correspond to the Library Reference and description fields of the
|
||||
component. The underscore is added ahead of the name to prevent these additional
|
||||
attributes from overriding any of your own. You can use these like:
|
||||
|
||||
```json
|
||||
{
|
||||
"Description": ["PART DESCRIPTION", "_description"],
|
||||
"Part Number": ["PART", "_part_id"]
|
||||
}
|
||||
```
|
||||
|
||||
By default, the script picks up a `columns.json` file from the working
|
||||
directory. If you want to keep it in a different place, or rename it, you can
|
||||
pass the `--columns` argument to the script to specify where it is.
|
||||
|
27
action.yml
27
action.yml
@ -7,16 +7,24 @@ inputs:
|
||||
project_path:
|
||||
description: "Path to the project file from the root of the repo"
|
||||
required: true
|
||||
pcb_path:
|
||||
description: "Path to the PCB file from the root of the repo"
|
||||
required: true
|
||||
output_file_name:
|
||||
description: "Name of the output file"
|
||||
required: true
|
||||
default: "bom.csv"
|
||||
attributes_mapping:
|
||||
description: "JSON string with the mapping of the attributes to the AllSpice attributes"
|
||||
columns:
|
||||
description: >
|
||||
A path to a JSON file mapping columns to the attributes they are from.
|
||||
required: true
|
||||
group_by:
|
||||
description: >
|
||||
A comma-separated list of columns to group the BOM by. If not present, the
|
||||
BOM will be flat.
|
||||
default: ''
|
||||
variant:
|
||||
description: >
|
||||
The variant of the project to generate the BOM for. If not present, the
|
||||
BOM will be generated for the default variant.
|
||||
default: ''
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "Dockerfile"
|
||||
@ -25,12 +33,15 @@ runs:
|
||||
- ${{ github.sha }}
|
||||
- "--allspice_hub_url"
|
||||
- ${{ github.server_url }}
|
||||
- "--attributes_mapping"
|
||||
- ${{ inputs.attributes_mapping }}
|
||||
- "--columns"
|
||||
- ${{ inputs.columns }}
|
||||
- "--group_by"
|
||||
- ${{ inputs.group_by }}
|
||||
- "--variant"
|
||||
- ${{ inputs.variant }}
|
||||
- "--output_file"
|
||||
- "${{ github.workspace}}/${{ inputs.output_file_name }}"
|
||||
- ${{ github.repository }}
|
||||
- ${{ inputs.project_path }}
|
||||
- ${{ inputs.pcb_path }}
|
||||
env:
|
||||
ALLSPICE_AUTH_TOKEN: ${{ github.token }}
|
||||
|
@ -11,7 +11,8 @@ import sys
|
||||
from contextlib import ExitStack
|
||||
|
||||
from allspice import AllSpice
|
||||
from allspice.utils.bom_generation import AttributesMapping, generate_bom_for_altium
|
||||
from allspice.utils.bom_generation import generate_bom_for_altium
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parse command line arguments. If you're writing a special purpose script,
|
||||
@ -19,36 +20,55 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="generate_bom", description="Generate a BOM from a PrjPcb file."
|
||||
)
|
||||
parser.add_argument("repository", help="The repo containing the project")
|
||||
parser.add_argument(
|
||||
"prjpcb_file", help="The path to the PrjPcb file in the source repo."
|
||||
"repository", help="The repo containing the project in the form 'owner/repo'"
|
||||
)
|
||||
parser.add_argument("prjpcb_file", help="The path to the PrjPcb file in the source repo.")
|
||||
parser.add_argument(
|
||||
"pcb_file",
|
||||
help="The path to the PCB file in the source repo.",
|
||||
"--columns",
|
||||
help=(
|
||||
"A path to a JSON file mapping columns to the attributes they are from. See the README "
|
||||
"for more details. Defaults to 'columns.json'."
|
||||
),
|
||||
default="columns.json",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_ref",
|
||||
help="The git reference the netlist should be generated for (eg. branch name, tag name, commit SHA). Defaults to main.",
|
||||
help=(
|
||||
"The git reference the BOM should be generated for (eg. branch name, tag name, commit "
|
||||
"SHA). Defaults to the main branch."
|
||||
),
|
||||
default="main",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--allspice_hub_url",
|
||||
help="The URL of your AllSpice Hub instance. Defaults to https://hub.allspice.io.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attributes_mapping",
|
||||
help="JSON text containing the attributes mapping.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output_file",
|
||||
help="The path to the output file. If absent, the CSV will be output to the command line.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--group_by",
|
||||
help=(
|
||||
"A comma-separated list of columns to group the BOM by. If not present, the BOM will "
|
||||
"be flat."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--variant",
|
||||
help=(
|
||||
"The variant of the project to generate the BOM for. If not present, the BOM will be "
|
||||
"generated for the default variant."
|
||||
),
|
||||
default="",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
attributes_mapper = AttributesMapping.from_dict(json.loads(args.attributes_mapping))
|
||||
columns_file = args.columns
|
||||
with open(columns_file, "r") as f:
|
||||
columns = json.loads(f.read())
|
||||
|
||||
# Use Environment Variables to store your auth token. This keeps your token
|
||||
# secure when sharing code.
|
||||
@ -60,14 +80,12 @@ if __name__ == "__main__":
|
||||
if args.allspice_hub_url is None:
|
||||
allspice = AllSpice(token_text=auth_token)
|
||||
else:
|
||||
allspice = AllSpice(
|
||||
token_text=auth_token, allspice_hub_url=args.allspice_hub_url
|
||||
)
|
||||
allspice = AllSpice(token_text=auth_token, allspice_hub_url=args.allspice_hub_url)
|
||||
|
||||
repo_owner, repo_name = args.repository.split("/")
|
||||
repository = allspice.get_repository(repo_owner, repo_name)
|
||||
prjpcb_file = args.prjpcb_file
|
||||
pcb_file = args.pcb_file
|
||||
group_by = args.group_by.split(",") if args.group_by else None
|
||||
|
||||
print("Generating BOM...", file=sys.stderr)
|
||||
|
||||
@ -75,38 +93,21 @@ if __name__ == "__main__":
|
||||
allspice,
|
||||
repository,
|
||||
prjpcb_file,
|
||||
pcb_file,
|
||||
attributes_mapper,
|
||||
args.source_ref,
|
||||
columns,
|
||||
group_by=group_by,
|
||||
ref=args.source_ref,
|
||||
variant=args.variant if args.variant else None,
|
||||
)
|
||||
bom_rows = [
|
||||
[
|
||||
bom_row.manufacturer,
|
||||
bom_row.part_number,
|
||||
bom_row.quantity,
|
||||
", ".join(bom_row.designators),
|
||||
bom_row.description,
|
||||
|
||||
]
|
||||
for bom_row in bom_rows
|
||||
]
|
||||
|
||||
with ExitStack() as stack:
|
||||
keys = bom_rows[0].keys()
|
||||
if args.output_file is not None:
|
||||
f = stack.enter_context(open(args.output_file, "w"))
|
||||
writer = csv.writer(f)
|
||||
writer = csv.DictWriter(f, fieldnames=keys)
|
||||
else:
|
||||
writer = csv.writer(sys.stdout)
|
||||
writer = csv.DictWriter(sys.stdout, fieldnames=keys)
|
||||
|
||||
header = [
|
||||
"Manufacturer",
|
||||
"MFG Part Number",
|
||||
"Quantity",
|
||||
"Reference Designator",
|
||||
"Description",
|
||||
]
|
||||
|
||||
writer.writerow(header)
|
||||
writer.writeheader()
|
||||
writer.writerows(bom_rows)
|
||||
|
||||
print("Generated bom.", file=sys.stderr)
|
||||
|
Loading…
Reference in New Issue
Block a user