Skip to content

Refactor InvenTree sync scripts into a Python package with configurable category map#8

Merged
peterus merged 4 commits intomainfrom
copilot/refactor-inventory-sync-scripts
Feb 20, 2026
Merged

Refactor InvenTree sync scripts into a Python package with configurable category map#8
peterus merged 4 commits intomainfrom
copilot/refactor-inventory-sync-scripts

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 19, 2026

Two monolithic scripts (bom-export.py, part_importer.py) handled all InvenTree sync logic with a tight coupling via a single ensure_parts_exist import. Split into a proper inventree_sync package with clear separation of concerns.

Package structure (scripts/inventree_sync/)

  • models.pyPartData, BomEntry dataclasses (previously duplicated across both scripts)
  • fetchers.pyLCSCFetcher, MouserFetcher (all supplier HTTP logic)
  • categories.py – part-name generation, category resolution, load_category_map() to load the KiCad→InvenTree mapping from a YAML file; logs a warning when a KiCad symbol is not found in the map
  • default_categories.yaml – externalized KiCad symbol → InvenTree category hierarchy map (replaces the hardcoded dict); well-commented and easy to extend
  • client.py – Low-level InvenTree API helpers (part creation, supplier/manufacturer records, price breaks, image uploads); supplier companies are resolved by name lookup only — no hardcoded PKs
  • part_manager.pyensure_parts_exist: orchestrates fetch → merge → create pipeline; accepts an optional category_map parameter
  • __init__.py – Clean public API (BomEntry, PartData, ensure_parts_exist, load_category_map)

Entry point

bom-export.pybom_export.py (valid Python module name); now imports from the package and contains only BOM-export-specific logic:

from inventree_sync import BomEntry, ensure_parts_exist, load_category_map

A --categories CLI argument allows users to supply their own YAML mapping file instead of the built-in default:

python3 scripts/bom_export.py \
  --csv_file bom.csv \
  --categories my_categories.yaml \
  ...

The YAML format is straightforward to extend:

R:      [Resistors, Surface Mount]
My_IC:  [My Custom Category, Sub]

When a KiCad symbol is not found in the category map, a WARNING is emitted and the part is placed in the supplier-provided category (if available) or Miscellaneous as a fallback — so the BOM export never silently drops a part.

GitHub Actions

Updated create-release-docs.yaml to invoke bom_export.py instead of the old bom-export.py.

Original prompt

Im Scripts Ordner befinden sich 3 Scripte. Zwei davon sind zum syncen zu InvenTree. Refactor die zwei Scripte großzügig! Du kannst auch ein Python Module dafür erstellen. Passe danach die Github Action Sccipte an.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: peterus <1764325+peterus@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor inventory sync scripts and update GitHub Actions Refactor InvenTree sync scripts into a Python package Feb 19, 2026
Copilot AI requested a review from peterus February 19, 2026 21:37
@peterus
Copy link
Copy Markdown
Member

peterus commented Feb 19, 2026

@copilot kannst du die map Tabelle extern anlegen (zb. als json oder yaml) und diese dann als parameter angeben? Hintergrund ist der das ich das script vielleicht veröffentlichen möchte und der user selber entscheiden soll wie er die Kategorien benennt. auch soll es dann leichter zu erweitern sein.

Co-authored-by: peterus <1764325+peterus@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 19, 2026

@copilot kannst du die map Tabelle extern anlegen (zb. als json oder yaml) und diese dann als parameter angeben? Hintergrund ist der das ich das script vielleicht veröffentlichen möchte und der user...

Die Mapping-Tabelle wurde in scripts/inventree_sync/default_categories.yaml ausgelagert (commit 3d898cf). Das Script lädt sie standardmäßig automatisch, kann aber per --categories meine_kategorien.yaml auf eine eigene Datei umgeleitet werden:

python3 scripts/bom_export.py \
  --csv_file bom.csv \
  --categories my_categories.yaml \
  ...

Die YAML-Struktur ist einfach zu erweitern:

R:      [Resistors, Surface Mount]
My_IC:  [My Custom Category, Sub]

load_category_map() ist auch als öffentliche Funktion im Paket exportiert, falls das Skript als Library verwendet wird.

Copilot AI changed the title Refactor InvenTree sync scripts into a Python package Refactor InvenTree sync scripts into a Python package with configurable category map Feb 19, 2026
Comment thread scripts/inventree_sync/part_manager.py Outdated
Comment thread scripts/inventree_sync/default_categories.yaml
…category map

Co-authored-by: peterus <1764325+peterus@users.noreply.github.com>
Copilot AI requested a review from peterus February 20, 2026 20:08
@peterus peterus marked this pull request as ready for review February 20, 2026 20:12
Copilot AI review requested due to automatic review settings February 20, 2026 20:12
@peterus peterus merged commit 1a66eba into main Feb 20, 2026
3 checks passed
@peterus peterus deleted the copilot/refactor-inventory-sync-scripts branch February 20, 2026 20:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the InvenTree BOM/part sync logic from standalone scripts into a reusable scripts/inventree_sync Python package, externalizing the KiCad→InvenTree category mapping into YAML and updating automation to use the new entry point.

Changes:

  • Introduces scripts/inventree_sync/ package (models, fetchers, category handling, InvenTree client helpers, and orchestration via ensure_parts_exist).
  • Replaces the hardcoded category map with default_categories.yaml and adds --categories CLI support to load a custom YAML mapping.
  • Renames the entry script to bom_export.py and updates the GitHub Actions workflow accordingly.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/requirements.txt Adds PyYAML dependency for YAML category map loading.
scripts/part_importer.py Removes the old monolithic importer script (logic moved into the package).
scripts/inventree_sync/models.py Adds shared dataclasses (PartData, BomEntry).
scripts/inventree_sync/fetchers.py Moves LCSC/Mouser fetching logic into dedicated fetchers.
scripts/inventree_sync/categories.py Adds YAML category-map loading, category resolution, and name generation helpers.
scripts/inventree_sync/default_categories.yaml Provides the default KiCad→InvenTree category mapping.
scripts/inventree_sync/client.py Centralizes InvenTree API helper operations (create/find/update parts and related records).
scripts/inventree_sync/part_manager.py Implements high-level orchestration (ensure_parts_exist) with optional category map injection.
scripts/inventree_sync/init.py Exposes a clean public API for scripts to import.
scripts/bom_export.py Updates BOM export script to use the new package and adds --categories.
.github/workflows/create-release-docs.yaml Updates workflow to call scripts/bom_export.py.
Comments suppressed due to low confidence (1)

scripts/bom_export.py:25

  • The PR description says load_category_map is part of the package’s public API (from inventree_sync import ... load_category_map), but this script imports it from the submodule. Importing from the package root would better match the intended API surface and avoids encouraging callers to reach into internal modules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to +9
# category tree. The special keys "package_subcategory_caps" and
# "package_subcategory_resistors" control which KiCad symbols get an
# additional package-level sub-category appended automatically.
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This YAML header comment mentions special keys (e.g. package_subcategory_caps / package_subcategory_resistors) controlling package-level sub-categories, but the Python code currently hardcodes these symbol sets and does not read any special YAML keys. Either remove/adjust this comment to match reality, or implement support for configuring these sets via the YAML file.

Suggested change
# category tree. The special keys "package_subcategory_caps" and
# "package_subcategory_resistors" control which KiCad symbols get an
# additional package-level sub-category appended automatically.
# category tree. Some KiCad symbols (for example, basic resistors and
# capacitors) may have an additional package-level sub-category appended
# automatically by the synchronization script; this behavior is defined in
# the Python code, not configured via special keys in this YAML file.

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +62
for key, value in raw.items():
if not isinstance(value, list) or not all(isinstance(s, str) for s in value):
raise SystemExit(
f"ERROR: invalid entry in {file_path}: key '{key}' must map to "
f"a list of strings, got {type(value).__name__!r}"
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_category_map() assumes the YAML top-level is a mapping. If the YAML file contains a list/string/etc, raw.items() will raise AttributeError, which bypasses the intended SystemExit with a descriptive message. Validate raw is a dict (or coerce/raise a SystemExit with a clear error) before iterating.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +109
lcsc_fetcher = LCSCFetcher()
mouser_fetcher = MouserFetcher()

lcsc_supplier = get_or_create_supplier(api, name="LCSC")
mouser_supplier = get_or_create_supplier(api, name="Mouser")
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When category_map is None, resolve_part_category() will call load_category_map() for every BOM entry, repeatedly reading/parsing the YAML file. Consider loading the default category map once at the start of ensure_parts_exist() (before the loop) when no map is provided, and then pass the preloaded dict through.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants