LDO: add Output Accuracy(%) param (template v2, skill v2)
parent
acbd9973f1
commit
b69a638875
61
SKILL.md
61
SKILL.md
|
|
@ -9,7 +9,7 @@ description: >-
|
||||||
(per-typeid version, changelog, backfill), fills a per-part workbook, loops on human
|
(per-typeid version, changelog, backfill), fills a per-part workbook, loops on human
|
||||||
verification, then takes the Altium symbol (.SchLib) and footprint (.PcbLib), fills the
|
verification, then takes the Altium symbol (.SchLib) and footprint (.PcbLib), fills the
|
||||||
Library/Footprint columns, assembles a part folder (xlsx, datasheet, symbol, footprint) and
|
Library/Footprint columns, assembles a part folder (xlsx, datasheet, symbol, footprint) and
|
||||||
pushes it to the components repo under its Class. Use WHENEVER the user uploads a component
|
pushes it to the library repo under its Class. Use WHENEVER the user uploads a component
|
||||||
datasheet, builds a library entry, adds a parameter to a type template, or pushes a part to
|
datasheet, builds a library entry, adds a parameter to a type template, or pushes a part to
|
||||||
Gitea. ALWAYS trigger on "\datasheet", "\library", or "\library-manager", or any
|
Gitea. ALWAYS trigger on "\datasheet", "\library", or "\library-manager", or any
|
||||||
component-library / datasheet-extraction task.
|
component-library / datasheet-extraction task.
|
||||||
|
|
@ -28,10 +28,16 @@ This is a deliberately **interactive** skill. At every decision point, **ask the
|
||||||
wait for an answer — do not assume a default and proceed.** In particular, always confirm:
|
wait for an answer — do not assume a default and proceed.** In particular, always confirm:
|
||||||
the **make**; the **typeid/classification** you inferred; whether any **new parameters**
|
the **make**; the **typeid/classification** you inferred; whether any **new parameters**
|
||||||
should be added to the template; the extracted **values** (the verification loop); that the
|
should be added to the template; the extracted **values** (the verification loop); that the
|
||||||
**symbol/footprint actually match this part**; whether to **apply a new parameter to existing
|
**symbol/footprint actually match this part**; and whether to **apply a new parameter to
|
||||||
parts** (backfill); and **before every push to Gitea** (the part, the skill files, the
|
existing parts** (backfill). When something is ambiguous, ask a specific question rather than
|
||||||
changelog). When something is ambiguous, ask a specific question rather than guessing. It is
|
guessing. It is always better to ask one more question than to write the wrong thing into the
|
||||||
always better to ask one more question than to write the wrong thing into the library.
|
library.
|
||||||
|
|
||||||
|
**Pushing is the exception — push automatically.** Once a part is verified and assembled (or a
|
||||||
|
template/version change has been made), push to Gitea **on your own, without asking for
|
||||||
|
confirmation**. Never add a "shall I push?" step — just push, then tell the user where it
|
||||||
|
landed. (You still flag a genuine symbol/footprint mismatch, because that's a correctness
|
||||||
|
issue, not a push confirmation.)
|
||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
|
|
@ -54,14 +60,14 @@ use the same tag:
|
||||||
`typeid` is the part's type-ID code from the taxonomy (`references/taxonomy.md`, full source
|
`typeid` is the part's type-ID code from the taxonomy (`references/taxonomy.md`, full source
|
||||||
`assets/template/Type_ID.xlsx`) — Schottky → `SCH`, MOSFET → `MOS`, LDO → `LDO`. In the new
|
`assets/template/Type_ID.xlsx`) — Schottky → `SCH`, MOSFET → `MOS`, LDO → `LDO`. In the new
|
||||||
template **each typeid is its own sheet** (125 of them). The broader **Class** (Diode,
|
template **each typeid is its own sheet** (125 of them). The broader **Class** (Diode,
|
||||||
Transistor, IC …) is used only to organise the components repo into top-level folders.
|
Transistor, IC …) is used only to organise the library repo into top-level folders.
|
||||||
|
|
||||||
## Gitea layout (two repos)
|
## Gitea layout (two repos)
|
||||||
|
|
||||||
```
|
```
|
||||||
skill repo/ this skill's own files (updated versions land here too)
|
skill repo/ this skill's own files (updated versions land here too)
|
||||||
|
|
||||||
components repo/
|
library repo/
|
||||||
<Class>/ e.g. Diode, IC, Transistor, Resistor, ...
|
<Class>/ e.g. Diode, IC, Transistor, Resistor, ...
|
||||||
<MPN>_<make>_<typeid>/ e.g. BAT46WJ_Nexperia_SCH
|
<MPN>_<make>_<typeid>/ e.g. BAT46WJ_Nexperia_SCH
|
||||||
<MPN>_<make>_<typeid>.xlsx this part's own one-row parameter sheet
|
<MPN>_<make>_<typeid>.xlsx this part's own one-row parameter sheet
|
||||||
|
|
@ -71,7 +77,7 @@ components repo/
|
||||||
```
|
```
|
||||||
|
|
||||||
There is **no single master workbook** — each part carries its own sheet inside its folder.
|
There is **no single master workbook** — each part carries its own sheet inside its folder.
|
||||||
Connection + repo names live in `config/gitea.env` (`SKILL_REPO`, `COMPONENTS_REPO`), so runs
|
Connection + repo names live in `config/gitea.env` (`SKILL_REPO`, `LIBRARY_REPO`), so runs
|
||||||
need no per-session token. If the host is unreachable, the git steps fail clearly and write
|
need no per-session token. If the host is unreachable, the git steps fail clearly and write
|
||||||
nothing.
|
nothing.
|
||||||
|
|
||||||
|
|
@ -95,7 +101,7 @@ here.** `ABSENT` (exit 0) → continue.
|
||||||
|
|
||||||
Read the datasheet, identify the part, and match it to the closest subclass in
|
Read the datasheet, identify the part, and match it to the closest subclass in
|
||||||
`references/taxonomy.md`; record its **typeid** (= the template sheet name). The **Class**
|
`references/taxonomy.md`; record its **typeid** (= the template sheet name). The **Class**
|
||||||
(for the components-repo folder) comes from the same taxonomy row —
|
(for the library-repo folder) comes from the same taxonomy row —
|
||||||
`scripts/common.py:class_folder(typeid)` returns it (e.g. `SCH` → `Diode`).
|
`scripts/common.py:class_folder(typeid)` returns it (e.g. `SCH` → `Diode`).
|
||||||
|
|
||||||
### 3. Confirm the typeid's template (and add parameters if asked)
|
### 3. Confirm the typeid's template (and add parameters if asked)
|
||||||
|
|
@ -118,10 +124,10 @@ Check whether that typeid has a sheet in `assets/template/template.xlsx`.
|
||||||
|
|
||||||
This appends the column(s) at the end of that typeid's sheet, **bumps that typeid's
|
This appends the column(s) at the end of that typeid's sheet, **bumps that typeid's
|
||||||
Template Version and Skill Version together** (v1→v2 — see *Per-typeid versioning*),
|
Template Version and Skill Version together** (v1→v2 — see *Per-typeid versioning*),
|
||||||
and writes one row to the global changelog `assets/template/CHANGELOG.xlsx`. Then **ask
|
and writes one row to the global changelog `assets/CHANGELOG.xlsx`. Then sync the updated
|
||||||
the user before pushing**, and sync the updated skill files + changelog to the skill repo
|
skill files + changelog to the skill repo with `push-skill` **automatically** (see
|
||||||
with `push-skill` (see *Pushing the skill repo*) — that merges the new changelog row onto
|
*Pushing the skill repo*) — that merges the new changelog row onto the one already in Gitea
|
||||||
the one already in Gitea rather than overwriting it.
|
rather than overwriting it.
|
||||||
|
|
||||||
Then ask: **should this change apply to the parts of this typeid already in Gitea?**
|
Then ask: **should this change apply to the parts of this typeid already in Gitea?**
|
||||||
- **No** → go to step 4 (only the current part gets the new column).
|
- **No** → go to step 4 (only the current part gets the new column).
|
||||||
|
|
@ -145,9 +151,19 @@ python scripts/fill_templates.py part.json \
|
||||||
--template assets/template/template.xlsx --dest <stage>/<tag>/
|
--template assets/template/template.xlsx --dest <stage>/<tag>/
|
||||||
```
|
```
|
||||||
|
|
||||||
This writes `<tag>.xlsx` — just that typeid's sheet, one row — with column A = the tag,
|
This writes `<tag>.xlsx` with column A = the tag, **Skill Version (col B)** and **Template
|
||||||
**Skill Version (col B)** and **Template Version (col C)** stamped from this typeid's current
|
Version (col C)** stamped from this typeid's current versions, and the four design columns
|
||||||
versions, and the four design columns left blank for now.
|
left blank for now.
|
||||||
|
|
||||||
|
The part workbook has up to **two sheets**:
|
||||||
|
|
||||||
|
- **Sheet 1** — the typeid's parameter sheet (the one filled row).
|
||||||
|
- **Sheet 2 — `Version History`** — added **only when this typeid has had a template/skill
|
||||||
|
update**. It lists the **cumulative** change history for that typeid (Date, Skill Version,
|
||||||
|
Template Version as `v1 → v2`, Description) — every change up to the version this file was
|
||||||
|
built at. So a part built at v3 shows both `v1 → v2` and `v2 → v3`; a still-at-v1 typeid has
|
||||||
|
no second sheet. The history is read from `assets/CHANGELOG.xlsx`, so make sure the local
|
||||||
|
changelog is current (it's kept in sync by `push-skill`) before building parts.
|
||||||
|
|
||||||
### 5. Human verification loop
|
### 5. Human verification loop
|
||||||
|
|
||||||
|
|
@ -194,7 +210,7 @@ python scripts/fill_templates.py part.json \
|
||||||
The staging folder `<tag>/` should now hold the four files: the per-part `<tag>.xlsx`, the
|
The staging folder `<tag>/` should now hold the four files: the per-part `<tag>.xlsx`, the
|
||||||
datasheet (name it `<MPN>_data.<ext>`), the symbol, and the footprint.
|
datasheet (name it `<MPN>_data.<ext>`), the symbol, and the footprint.
|
||||||
|
|
||||||
### 8. Push to the components repo, under the part's Class
|
### 8. Push to the library repo, under the part's Class
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/gitea_components.py push-part --folder <stage>/<tag> --typeid <typeid>
|
python scripts/gitea_components.py push-part --folder <stage>/<tag> --typeid <typeid>
|
||||||
|
|
@ -218,7 +234,7 @@ reading and writing these numbers.
|
||||||
## The changelog
|
## The changelog
|
||||||
|
|
||||||
`append_parameter.py` maintains one **global** changelog as an Excel workbook at
|
`append_parameter.py` maintains one **global** changelog as an Excel workbook at
|
||||||
`assets/template/CHANGELOG.xlsx` (sheet `Changelog`, styled green header). Every time a
|
`assets/CHANGELOG.xlsx` (sheet `Changelog`, styled green header). Every time a
|
||||||
typeid's template/version changes, one row is appended with columns
|
typeid's template/version changes, one row is appended with columns
|
||||||
**Date | Typeid | Skill Version | Template Version | Description** — the version columns hold
|
**Date | Typeid | Skill Version | Template Version | Description** — the version columns hold
|
||||||
the new versions, and Description is your note (or the parameter(s) added if you gave none).
|
the new versions, and Description is your note (or the parameter(s) added if you gave none).
|
||||||
|
|
@ -257,7 +273,8 @@ python scripts/gitea_components.py commit-push --root work/ --message "backfill
|
||||||
## Pushing the skill repo
|
## Pushing the skill repo
|
||||||
|
|
||||||
When skill files change (a new typeid template, a parameter add, a version/changelog bump),
|
When skill files change (a new typeid template, a parameter add, a version/changelog bump),
|
||||||
**ask the user first**, then push the skill's own files to the skill repo with `push-skill`:
|
push the skill's own files to the skill repo with `push-skill` **automatically** (no
|
||||||
|
confirmation):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/gitea_components.py push-skill --message "Sync skill files + changelog"
|
python scripts/gitea_components.py push-skill --message "Sync skill files + changelog"
|
||||||
|
|
@ -278,7 +295,7 @@ plain flat push, but it does not merge the changelog or blank the token, so pref
|
||||||
`Footprint Ref/Path` and `Manufacturer` sit near the end.
|
`Footprint Ref/Path` and `Manufacturer` sit near the end.
|
||||||
- `assets/template/Type_ID.xlsx` + `references/taxonomy.md` — Class → Subclass → Type ID.
|
- `assets/template/Type_ID.xlsx` + `references/taxonomy.md` — Class → Subclass → Type ID.
|
||||||
- `assets/template/versions.json` — per-typeid `template_version` + `skill_version`.
|
- `assets/template/versions.json` — per-typeid `template_version` + `skill_version`.
|
||||||
- `assets/template/CHANGELOG.xlsx` — global version/parameter changelog (created on first add;
|
- `assets/CHANGELOG.xlsx` — global version/parameter changelog (created on first add;
|
||||||
merged into the skill repo's copy in Gitea by `push-skill`).
|
merged into the skill repo's copy in Gitea by `push-skill`).
|
||||||
- `scripts/common.py` — taxonomy loader (`load_taxonomy`, `class_folder`), version store
|
- `scripts/common.py` — taxonomy loader (`load_taxonomy`, `class_folder`), version store
|
||||||
(`get_versions`, `version_labels`, `bump_versions`), and the tag helper (`part_tag`).
|
(`get_versions`, `version_labels`, `bump_versions`), and the tag helper (`part_tag`).
|
||||||
|
|
@ -288,9 +305,9 @@ plain flat push, but it does not merge the changelog or blank the token, so pref
|
||||||
the changelog.
|
the changelog.
|
||||||
- `scripts/altium_refs.py` — read Library/Footprint Ref from `.SchLib`/`.PcbLib`.
|
- `scripts/altium_refs.py` — read Library/Footprint Ref from `.SchLib`/`.PcbLib`.
|
||||||
- `scripts/gitea_components.py` — `check-mpn`, `checkout`, `list-type`, `place-part`,
|
- `scripts/gitea_components.py` — `check-mpn`, `checkout`, `list-type`, `place-part`,
|
||||||
`commit-push`, `push-part` (components repo), and `push-skill` (skill repo: token-blanked
|
`commit-push`, `push-part` (library repo), and `push-skill` (skill repo: token-blanked
|
||||||
push + append-only changelog merge).
|
push + append-only changelog merge).
|
||||||
- `scripts/push_to_gitea.sh` — push a folder's contents to a Gitea repo (used for the skill
|
- `scripts/push_to_gitea.sh` — push a folder's contents to a Gitea repo (used for the skill
|
||||||
repo).
|
repo).
|
||||||
- `config/gitea.env` — host, user, token, and the `SKILL_REPO` / `COMPONENTS_REPO` names
|
- `config/gitea.env` — host, user, token, and the `SKILL_REPO` / `LIBRARY_REPO` names
|
||||||
(**secret** — do not push the token).
|
(**secret** — do not push the token).
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -156,8 +156,8 @@
|
||||||
"template_version": 1
|
"template_version": 1
|
||||||
},
|
},
|
||||||
"FBD": {
|
"FBD": {
|
||||||
"skill_version": 2,
|
"skill_version": 1,
|
||||||
"template_version": 2
|
"template_version": 1
|
||||||
},
|
},
|
||||||
"FFC": {
|
"FFC": {
|
||||||
"skill_version": 1,
|
"skill_version": 1,
|
||||||
|
|
@ -232,8 +232,8 @@
|
||||||
"template_version": 1
|
"template_version": 1
|
||||||
},
|
},
|
||||||
"LDO": {
|
"LDO": {
|
||||||
"skill_version": 1,
|
"skill_version": 2,
|
||||||
"template_version": 1
|
"template_version": 2
|
||||||
},
|
},
|
||||||
"LED": {
|
"LED": {
|
||||||
"skill_version": 1,
|
"skill_version": 1,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ GIT_USER=nitishKumar
|
||||||
GIT_TOKEN=451bff1dc32202cbc0a371f8e5645079466d2120
|
GIT_TOKEN=451bff1dc32202cbc0a371f8e5645079466d2120
|
||||||
# Target repos — TWO now (set these to the exact repo names on your Gitea):
|
# Target repos — TWO now (set these to the exact repo names on your Gitea):
|
||||||
# SKILL_REPO : holds this skill's own files (SKILL.md, scripts, assets, ...).
|
# SKILL_REPO : holds this skill's own files (SKILL.md, scripts, assets, ...).
|
||||||
# COMPONENTS_REPO : holds components, one folder per Class (Diode, IC, ...); inside each,
|
# LIBRARY_REPO : holds components, one folder per Class (Diode, IC, ...); inside each,
|
||||||
# one MPN_make_typeid/ folder per part with { xlsx, datasheet, symbol,
|
# one MPN_make_typeid/ folder per part with { xlsx, datasheet, symbol,
|
||||||
# footprint }. (Replaces the old DFS + Parameters repos.)
|
# footprint }. (Replaces the old DFS + Parameters repos.)
|
||||||
SKILL_REPO=nitishKumar/skill
|
SKILL_REPO=nitishKumar/skill
|
||||||
COMPONENTS_REPO=nitishKumar/Components
|
LIBRARY_REPO=nitishKumar/library
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Use the **Type ID** as the `typeid` in `MPN_make_typeid`. Match each part to the closest
|
Use the **Type ID** as the `typeid` in `MPN_make_typeid`. Match each part to the closest
|
||||||
subclass. In the current template **each Type ID is its own sheet** (125 sheets), so the
|
subclass. In the current template **each Type ID is its own sheet** (125 sheets), so the
|
||||||
template sheet name **is the Type ID** (e.g. `SCH`, `MOS`, `LDO`). The **Class** is used only
|
template sheet name **is the Type ID** (e.g. `SCH`, `MOS`, `LDO`). The **Class** is used only
|
||||||
to organise the components repo into top-level folders (e.g. `SCH` lives under `Diode/`);
|
to organise the library repo into top-level folders (e.g. `SCH` lives under `Diode/`);
|
||||||
`scripts/common.py:class_folder()` maps a typeid to its folder name. Full source:
|
`scripts/common.py:class_folder()` maps a typeid to its folder name. Full source:
|
||||||
`assets/template/Type_ID.xlsx`.
|
`assets/template/Type_ID.xlsx`.
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -15,7 +15,7 @@ headers. Then versions.json is bumped and one entry is written to CHANGELOG.xlsx
|
||||||
--desc "Added Trr and Cd for SMPS rectifier derating"
|
--desc "Added Trr and Cd for SMPS rectifier derating"
|
||||||
|
|
||||||
After running, push the updated template.xlsx + versions.json + CHANGELOG.xlsx to the SKILL
|
After running, push the updated template.xlsx + versions.json + CHANGELOG.xlsx to the SKILL
|
||||||
repo (and, per the workflow, the same change flows to the components repo copies).
|
repo (and, per the workflow, the same change flows to the library repo copies).
|
||||||
"""
|
"""
|
||||||
import argparse, datetime, os
|
import argparse, datetime, os
|
||||||
import openpyxl
|
import openpyxl
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
One place for the three things every script needs to agree on:
|
One place for the three things every script needs to agree on:
|
||||||
|
|
||||||
- the taxonomy (typeid -> Class -> components-repo folder name), read from
|
- the taxonomy (typeid -> Class -> library-repo folder name), read from
|
||||||
``assets/template/Type_ID.xlsx`` so there is a single source of truth;
|
``assets/template/Type_ID.xlsx`` so there is a single source of truth;
|
||||||
- the **per-typeid version store** (``assets/template/versions.json``) that holds each
|
- the **per-typeid version store** (``assets/template/versions.json``) that holds each
|
||||||
typeid's own ``template_version`` and ``skill_version`` - both start at 1 and both bump
|
typeid's own ``template_version`` and ``skill_version`` - both start at 1 and both bump
|
||||||
|
|
@ -29,7 +29,7 @@ COL_SKILL_VER = "Skill Version" # column B (per-typeid skill version, st
|
||||||
COL_TEMPLATE_VER = "Template Version" # column C (per-typeid template version, stamped by us)
|
COL_TEMPLATE_VER = "Template Version" # column C (per-typeid template version, stamped by us)
|
||||||
DESIGN_COLS = ("Library Ref", "Library Path", "Footprint Ref", "Footprint Path")
|
DESIGN_COLS = ("Library Ref", "Library Path", "Footprint Ref", "Footprint Path")
|
||||||
|
|
||||||
# Raw taxonomy Class name -> clean, filesystem-safe folder name for the components repo.
|
# Raw taxonomy Class name -> clean, filesystem-safe folder name for the library repo.
|
||||||
# These match Vecmocon's established sheet-name convention (the old 18-class template).
|
# These match Vecmocon's established sheet-name convention (the old 18-class template).
|
||||||
CLASS_FOLDER = {
|
CLASS_FOLDER = {
|
||||||
"Resistor": "Resistor",
|
"Resistor": "Resistor",
|
||||||
|
|
@ -80,7 +80,7 @@ def load_taxonomy(type_id_xlsx=TYPE_ID_XLSX):
|
||||||
|
|
||||||
|
|
||||||
def class_folder(typeid, taxonomy=None):
|
def class_folder(typeid, taxonomy=None):
|
||||||
"""The components-repo top-level folder name for a typeid (e.g. SCH -> 'Diode')."""
|
"""The library-repo top-level folder name for a typeid (e.g. SCH -> 'Diode')."""
|
||||||
tax = taxonomy or load_taxonomy()
|
tax = taxonomy or load_taxonomy()
|
||||||
info = tax.get(typeid)
|
info = tax.get(typeid)
|
||||||
return info["folder"] if info else None
|
return info["folder"] if info else None
|
||||||
|
|
@ -153,6 +153,33 @@ def init_versions(force=False):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- changelog (read)
|
||||||
|
|
||||||
|
CHANGELOG_HEADERS = ["Date", "Typeid", "Skill Version", "Template Version", "Description"]
|
||||||
|
|
||||||
|
|
||||||
|
def read_changelog():
|
||||||
|
"""All changelog rows as dicts (empty list if the changelog doesn't exist yet)."""
|
||||||
|
if not os.path.exists(CHANGELOG_XLSX):
|
||||||
|
return []
|
||||||
|
wb = openpyxl.load_workbook(CHANGELOG_XLSX)
|
||||||
|
ws = wb["Changelog"] if "Changelog" in wb.sheetnames else wb.active
|
||||||
|
out = []
|
||||||
|
for r in ws.iter_rows(min_row=2, values_only=True):
|
||||||
|
if not r or all(c is None for c in r):
|
||||||
|
continue
|
||||||
|
r = list(r) + [None] * (5 - len(r))
|
||||||
|
out.append({"date": r[0], "typeid": r[1], "skill": r[2],
|
||||||
|
"template": r[3], "description": r[4]})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def changelog_for_typeid(typeid):
|
||||||
|
"""Every recorded version change for one typeid, oldest first — the cumulative history
|
||||||
|
(v1->v2, v2->v3, ...) that each part sheet of that typeid embeds as its Version History."""
|
||||||
|
return [r for r in read_changelog() if str(r["typeid"]) == str(typeid)]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == "init":
|
if len(sys.argv) > 1 and sys.argv[1] == "init":
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,54 @@ comes from the extracted ``values``.
|
||||||
``part.json`` is either a single part object or {"parts": [ ... ]} (each part built to its
|
``part.json`` is either a single part object or {"parts": [ ... ]} (each part built to its
|
||||||
own file). A part = {"mpn","manufacturer" (or "make"),"typeid","values":{header:value,...}}.
|
own file). A part = {"mpn","manufacturer" (or "make"),"typeid","values":{header:value,...}}.
|
||||||
"""
|
"""
|
||||||
import argparse, json, os
|
import argparse, json, os, re
|
||||||
import openpyxl
|
import openpyxl
|
||||||
from common import (norm, part_tag, version_labels,
|
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
|
||||||
|
from common import (norm, part_tag, version_labels, changelog_for_typeid,
|
||||||
COL_TAG, COL_SKILL_VER, COL_TEMPLATE_VER, DESIGN_COLS)
|
COL_TAG, COL_SKILL_VER, COL_TEMPLATE_VER, DESIGN_COLS)
|
||||||
|
|
||||||
|
GREEN = "B6D7A8"; GRAY = "BFBFBF"
|
||||||
|
VERSION_SHEET = "Version History"
|
||||||
|
|
||||||
|
|
||||||
|
def _vtrans(v):
|
||||||
|
"""'v2' -> 'v1 -> v2' (each changelog row records the new version; the previous is n-1)."""
|
||||||
|
m = re.match(r"v?(\d+)", str(v).strip()) if v is not None else None
|
||||||
|
if not m:
|
||||||
|
return "" if v is None else str(v)
|
||||||
|
n = int(m.group(1))
|
||||||
|
return f"v{n-1} → v{n}" if n > 1 else f"v{n}"
|
||||||
|
|
||||||
|
|
||||||
|
def add_version_history(wb, typeid):
|
||||||
|
"""Sheet 2 of a part workbook: the cumulative version history for this typeid — every
|
||||||
|
template/skill change (v1->v2, v2->v3, ...) with its date and description. Added only when
|
||||||
|
there has actually been a template/skill update; still-v1 typeids get no second sheet."""
|
||||||
|
rows = changelog_for_typeid(typeid)
|
||||||
|
if not rows:
|
||||||
|
return False
|
||||||
|
ws = wb.create_sheet(VERSION_SHEET)
|
||||||
|
s = Side(style="thin", color=GRAY)
|
||||||
|
border = Border(left=s, right=s, top=s, bottom=s)
|
||||||
|
headers = ["Date", "Skill Version", "Template Version", "Description"]
|
||||||
|
for c, h in enumerate(headers, start=1):
|
||||||
|
cell = ws.cell(1, c, h)
|
||||||
|
cell.font = Font(name="Calibri", bold=True)
|
||||||
|
cell.fill = PatternFill("solid", fgColor=GREEN)
|
||||||
|
cell.border = border
|
||||||
|
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
for row in rows:
|
||||||
|
r = ws.max_row + 1
|
||||||
|
vals = [row["date"], _vtrans(row["skill"]), _vtrans(row["template"]), row["description"]]
|
||||||
|
for c, v in enumerate(vals, start=1):
|
||||||
|
cell = ws.cell(r, c, v)
|
||||||
|
cell.border = border
|
||||||
|
cell.alignment = Alignment(vertical="center", wrap_text=(c == 4), horizontal="left")
|
||||||
|
for col, w in zip("ABCD", (18, 16, 18, 60)):
|
||||||
|
ws.column_dimensions[col].width = w
|
||||||
|
ws.freeze_panes = "A2"
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def template_headers(template_path, typeid):
|
def template_headers(template_path, typeid):
|
||||||
"""Canonical headers for a typeid sheet, or None if the template has no such sheet."""
|
"""Canonical headers for a typeid sheet, or None if the template has no such sheet."""
|
||||||
|
|
@ -76,6 +119,7 @@ def build_part_sheet(part, template_path, dest_dir, design=None):
|
||||||
ws.cell(r, c).value = None
|
ws.cell(r, c).value = None
|
||||||
for c, h in enumerate(headers, start=1):
|
for c, h in enumerate(headers, start=1):
|
||||||
ws.cell(2, c, row.get(h, ""))
|
ws.cell(2, c, row.get(h, ""))
|
||||||
|
add_version_history(wb, typeid) # Sheet 2: cumulative version history (if any)
|
||||||
os.makedirs(dest_dir, exist_ok=True)
|
os.makedirs(dest_dir, exist_ok=True)
|
||||||
out = os.path.join(dest_dir, f"{tag}.xlsx")
|
out = os.path.join(dest_dir, f"{tag}.xlsx")
|
||||||
wb.save(out)
|
wb.save(out)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
Repos (from config/gitea.env):
|
Repos (from config/gitea.env):
|
||||||
SKILL_REPO - this skill's own files (pushed with push_to_gitea.sh, not here).
|
SKILL_REPO - this skill's own files (pushed with push_to_gitea.sh, not here).
|
||||||
COMPONENTS_REPO - one folder per Class (Diode, IC, Transistor, ...); inside each Class,
|
LIBRARY_REPO - one folder per Class (Diode, IC, Transistor, ...); inside each Class,
|
||||||
one MPN_make_typeid/ folder per part holding { xlsx, datasheet, symbol,
|
one MPN_make_typeid/ folder per part holding { xlsx, datasheet, symbol,
|
||||||
footprint }.
|
footprint }.
|
||||||
|
|
||||||
|
|
@ -11,12 +11,12 @@ Subcommands (all accept --local <dir> to run against a local folder instead of c
|
||||||
testing or offline work):
|
testing or offline work):
|
||||||
|
|
||||||
check-mpn --mpn BAT46WJ --make Nexperia
|
check-mpn --mpn BAT46WJ --make Nexperia
|
||||||
Is any part with this MPN+make already in the components repo? (typeid-agnostic - the
|
Is any part with this MPN+make already in the library repo? (typeid-agnostic - the
|
||||||
early duplicate gate, run BEFORE classifying.) Prints EXISTS/<tag> or ABSENT; exit 0
|
early duplicate gate, run BEFORE classifying.) Prints EXISTS/<tag> or ABSENT; exit 0
|
||||||
if absent, 3 if it already exists (so a shell can hard-stop).
|
if absent, 3 if it already exists (so a shell can hard-stop).
|
||||||
|
|
||||||
checkout --dest work/
|
checkout --dest work/
|
||||||
Clone the components repo to a working dir you can browse and edit in place.
|
Clone the library repo to a working dir you can browse and edit in place.
|
||||||
|
|
||||||
list-type --typeid SCH [--root work/] [--json]
|
list-type --typeid SCH [--root work/] [--json]
|
||||||
List every existing part of a typeid and the files in its folder (for backfill: the
|
List every existing part of a typeid and the files in its folder (for backfill: the
|
||||||
|
|
@ -59,7 +59,7 @@ def load_env(cfg):
|
||||||
continue
|
continue
|
||||||
k, v = line.split("=", 1)
|
k, v = line.split("=", 1)
|
||||||
env[k.strip()] = v.strip()
|
env[k.strip()] = v.strip()
|
||||||
for k in ("GIT_HOST", "GIT_USER", "GIT_TOKEN", "SKILL_REPO", "COMPONENTS_REPO"):
|
for k in ("GIT_HOST", "GIT_USER", "GIT_TOKEN", "SKILL_REPO", "LIBRARY_REPO"):
|
||||||
if os.environ.get(k):
|
if os.environ.get(k):
|
||||||
env[k] = os.environ[k]
|
env[k] = os.environ[k]
|
||||||
return env
|
return env
|
||||||
|
|
@ -72,7 +72,7 @@ def repo_url(env, repo):
|
||||||
|
|
||||||
|
|
||||||
def clone(env, dest, repo=None):
|
def clone(env, dest, repo=None):
|
||||||
repo = repo or env["COMPONENTS_REPO"]
|
repo = repo or env["LIBRARY_REPO"]
|
||||||
url = repo_url(env, repo)
|
url = repo_url(env, repo)
|
||||||
r = subprocess.run(["git", "clone", url, dest], capture_output=True, text=True)
|
r = subprocess.run(["git", "clone", url, dest], capture_output=True, text=True)
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
|
|
@ -140,7 +140,7 @@ def cmd_check_mpn(env, args):
|
||||||
|
|
||||||
def cmd_checkout(env, args):
|
def cmd_checkout(env, args):
|
||||||
dest = clone(env, args.dest)
|
dest = clone(env, args.dest)
|
||||||
print(f"components repo checked out at {dest}")
|
print(f"library repo checked out at {dest}")
|
||||||
|
|
||||||
|
|
||||||
def cmd_list_type(env, args):
|
def cmd_list_type(env, args):
|
||||||
|
|
@ -315,7 +315,7 @@ def main():
|
||||||
needs.add("SKILL_REPO")
|
needs.add("SKILL_REPO")
|
||||||
elif args.cmd in ("check-mpn", "checkout", "commit-push", "push-part") or \
|
elif args.cmd in ("check-mpn", "checkout", "commit-push", "push-part") or \
|
||||||
(args.cmd in ("list-type", "place-part") and not getattr(args, "local", None) and not getattr(args, "root", None)):
|
(args.cmd in ("list-type", "place-part") and not getattr(args, "local", None) and not getattr(args, "root", None)):
|
||||||
needs.add("COMPONENTS_REPO")
|
needs.add("LIBRARY_REPO")
|
||||||
for k in sorted(needs):
|
for k in sorted(needs):
|
||||||
if not env.get(k):
|
if not env.get(k):
|
||||||
sys.exit(f"missing {k} in env/config")
|
sys.exit(f"missing {k} in env/config")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue