optivibe-reference 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- optivibe_reference/__init__.py +14 -0
- optivibe_reference/_envelope.py +19 -0
- optivibe_reference/_paths.py +66 -0
- optivibe_reference/_probe_common.py +142 -0
- optivibe_reference/build_data.py +271 -0
- optivibe_reference/build_steps.py +182 -0
- optivibe_reference/catalog_build.py +1138 -0
- optivibe_reference/data/operand_semantics.json +2192 -0
- optivibe_reference/data/operand_synonyms.json +1687 -0
- optivibe_reference/data/provenance_grants.txt +5 -0
- optivibe_reference/data/tolerance_operand_synonyms.json +307 -0
- optivibe_reference/errors.py +57 -0
- optivibe_reference/glass_build.py +433 -0
- optivibe_reference/glass_dispersion.py +207 -0
- optivibe_reference/glass_math.py +383 -0
- optivibe_reference/glass_parse.py +168 -0
- optivibe_reference/manual_build.py +672 -0
- optivibe_reference/manual_enrich.py +490 -0
- optivibe_reference/probe_operands.py +226 -0
- optivibe_reference/probe_tolerances.py +371 -0
- optivibe_reference/server.py +547 -0
- optivibe_reference/tolerance_build.py +554 -0
- optivibe_reference/tools/__init__.py +5 -0
- optivibe_reference/tools/_glass_common.py +137 -0
- optivibe_reference/tools/_glass_kind.py +133 -0
- optivibe_reference/tools/_query_params.py +57 -0
- optivibe_reference/tools/_rag_match.py +92 -0
- optivibe_reference/tools/find_glass_pair.py +574 -0
- optivibe_reference/tools/find_glasses.py +339 -0
- optivibe_reference/tools/lookup_glass.py +262 -0
- optivibe_reference/tools/lookup_operand.py +463 -0
- optivibe_reference/tools/search_reference.py +321 -0
- optivibe_reference-0.2.0.dist-info/METADATA +44 -0
- optivibe_reference-0.2.0.dist-info/RECORD +37 -0
- optivibe_reference-0.2.0.dist-info/WHEEL +5 -0
- optivibe_reference-0.2.0.dist-info/entry_points.txt +2 -0
- optivibe_reference-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""build_steps.py — the glass-catalog and manual-corpus build steps, as package code.
|
|
2
|
+
|
|
3
|
+
These were the bodies of ``scripts/build_glass_catalog.py`` and
|
|
4
|
+
``scripts/build_manual_corpus.py``. They live in the package so an INSTALLED
|
|
5
|
+
optivibe-reference (no ``scripts/`` dir) can run them through ``build_data``
|
|
6
|
+
(``optivibe-build-data``); the two scripts are now thin wrappers with the same CLI.
|
|
7
|
+
|
|
8
|
+
* ``build_glass_catalog()`` — resolve the install Glasscat dir (``ZEMAX_GLASSCAT``
|
|
9
|
+
override, else ``~/Documents/Zemax/Glasscat``), assert the ``.agf``-derivation grant
|
|
10
|
+
(fail-closed), re-parse + recompute every shipped ``.agf`` record, write
|
|
11
|
+
``data/glass_catalog.json``. No raw ``.agf`` is kept; only OUR computed/parsed numbers
|
|
12
|
+
+ factual glass names land in the JSON.
|
|
13
|
+
* ``build_manual_corpus(argv)`` — resolve the licensed install-dir manual PDF
|
|
14
|
+
(``OPTIVIBE_MANUAL_PDF`` else the standard install-dir glob) and build the local FTS5
|
|
15
|
+
``.db`` + the TEXT-FREE manifest. ``--emit-raw`` ALSO writes the VERBATIM raw
|
|
16
|
+
operand/tolerance extracts (the manual oracle, never committed) into the captures dir.
|
|
17
|
+
|
|
18
|
+
Both print ASCII status ONLY and NEVER relay extracted manual or catalog prose (the
|
|
19
|
+
cp1252 ``conda run`` relay crash, probe finding 3).
|
|
20
|
+
"""
|
|
21
|
+
import glob
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
from . import _paths, glass_build, manual_build
|
|
27
|
+
from .glass_parse import resolve_glasscat
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# --- glass catalog ----------------------------------------------------------
|
|
31
|
+
def build_glass_catalog():
|
|
32
|
+
"""Build ``data/glass_catalog.json`` from the install Glasscat dir; return 0.
|
|
33
|
+
|
|
34
|
+
Raises ``FileNotFoundError`` when no Glasscat dir resolves (the caller's SKIP) and
|
|
35
|
+
``ProvenanceGateError`` when the derivation grant is not on file (a FAIL).
|
|
36
|
+
"""
|
|
37
|
+
glasscat = resolve_glasscat()
|
|
38
|
+
print("[glass-build] Glasscat dir: %s" % glasscat)
|
|
39
|
+
|
|
40
|
+
catalog = glass_build.build_glass_catalog_json(
|
|
41
|
+
glasscat, glass_build.PROVENANCE_PATH
|
|
42
|
+
)
|
|
43
|
+
glass_build.write_glass_catalog_json(glass_build.GLASS_CATALOG_JSON, catalog)
|
|
44
|
+
|
|
45
|
+
rows = catalog["rows"]
|
|
46
|
+
nd_valid = sum(1 for r in rows if r["nd_valid"])
|
|
47
|
+
pg_f_valid = sum(1 for r in rows if r["pg_f_valid"])
|
|
48
|
+
nd_null = sum(1 for r in rows if r["nd"] is None)
|
|
49
|
+
print("[glass-build] agf_glass_count: %d" % catalog["agf_glass_count"])
|
|
50
|
+
print("[glass-build] rows written: %d" % len(rows))
|
|
51
|
+
print("[glass-build] nd_valid rows: %d" % nd_valid)
|
|
52
|
+
print("[glass-build] pg_f_valid rows: %d" % pg_f_valid)
|
|
53
|
+
print("[glass-build] nd-null (out-of-band) rows: %d" % nd_null)
|
|
54
|
+
print("[glass-build] wrote -> %s" % glass_build.GLASS_CATALOG_JSON)
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# --- manual corpus ----------------------------------------------------------
|
|
59
|
+
def raw_extract_path():
|
|
60
|
+
"""The GITIGNORED verbatim merit-operand raw extract (the oracle)."""
|
|
61
|
+
return os.path.join(_paths.captures_dir(), "operand_raw_descriptions.json")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def tol_raw_extract_path():
|
|
65
|
+
"""The GITIGNORED verbatim tolerance-operand raw extract (the oracle)."""
|
|
66
|
+
return os.path.join(_paths.captures_dir(), "tolerance_raw_descriptions.json")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def find_manual_pdf():
|
|
70
|
+
"""Resolve the licensed manual PDF, or return None (genuine absence)."""
|
|
71
|
+
override = os.environ.get("OPTIVIBE_MANUAL_PDF")
|
|
72
|
+
if override and os.path.isfile(override):
|
|
73
|
+
return override
|
|
74
|
+
for base in (
|
|
75
|
+
os.environ.get("ProgramFiles", r"C:\Program Files"),
|
|
76
|
+
os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)"),
|
|
77
|
+
):
|
|
78
|
+
hits = glob.glob(
|
|
79
|
+
os.path.join(base, "*Zemax OpticStudio*", "OpticStudio_UserManual_en.pdf")
|
|
80
|
+
)
|
|
81
|
+
if hits:
|
|
82
|
+
return sorted(hits, reverse=True)[0]
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def write_raw(path, entries, order):
|
|
87
|
+
"""Write ``{code: {code, page, raw_text}}`` to a GITIGNORED capture (oracle).
|
|
88
|
+
|
|
89
|
+
NEVER prints the extracted prose — the caller prints only the entry count.
|
|
90
|
+
|
|
91
|
+
ATOMIC (a hardening finding): the oracle is written to a same-dir
|
|
92
|
+
``<path>.tmp`` then ``os.replace``d onto the final path, so a concurrent reader
|
|
93
|
+
never sees a half-written raw extract.
|
|
94
|
+
"""
|
|
95
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
96
|
+
payload = {code: entries[code] for code in order}
|
|
97
|
+
tmp_path = path + ".tmp"
|
|
98
|
+
try:
|
|
99
|
+
with open(tmp_path, "w", encoding="utf-8", newline="\n") as fh:
|
|
100
|
+
json.dump(payload, fh, indent=2, ensure_ascii=True)
|
|
101
|
+
fh.write("\n")
|
|
102
|
+
os.replace(tmp_path, path) # atomic same-dir rename
|
|
103
|
+
except Exception:
|
|
104
|
+
try:
|
|
105
|
+
os.remove(tmp_path)
|
|
106
|
+
except OSError:
|
|
107
|
+
pass
|
|
108
|
+
raise
|
|
109
|
+
return payload
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def emit_raw(pdf_path):
|
|
113
|
+
"""Extract BOTH raw operand oracles to the GITIGNORED captures (verbatim source).
|
|
114
|
+
|
|
115
|
+
Merit: the finding-2 bbox two-column pairer over the alphabetical §5.2.1.3
|
|
116
|
+
section -> ``operand_raw_descriptions.json``. Tolerance: the NEW heading-delimited
|
|
117
|
+
§7.2.1.1 pairer -> ``tolerance_raw_descriptions.json`` (a SEPARATE
|
|
118
|
+
strategy). NEVER prints the extracted prose — only entry counts.
|
|
119
|
+
"""
|
|
120
|
+
# Lazy: manual_enrich imports manual_build, and only this path needs it.
|
|
121
|
+
from . import manual_enrich
|
|
122
|
+
|
|
123
|
+
# The merit operand INVENTORY gates the glued ``CODE(`` split (an entry
|
|
124
|
+
# opens only when the prefix is a real operand, never on prose like ``RMS(``).
|
|
125
|
+
# ONE shared loader — no second path/list here.
|
|
126
|
+
merit_valid_codes = manual_enrich.load_merit_operand_inventory()
|
|
127
|
+
merit_pages = manual_enrich.iter_operand_pages(pdf_path)
|
|
128
|
+
merit_entries, merit_order = manual_enrich.pair_operands(
|
|
129
|
+
merit_pages, valid_codes=merit_valid_codes
|
|
130
|
+
)
|
|
131
|
+
merit_path = raw_extract_path()
|
|
132
|
+
merit_payload = write_raw(merit_path, merit_entries, merit_order)
|
|
133
|
+
print("raw-extract: wrote {} operand entries -> {}".format(
|
|
134
|
+
len(merit_payload), merit_path))
|
|
135
|
+
|
|
136
|
+
# Pass the tolerance INVENTORY code set so a phantom heading token
|
|
137
|
+
# (``IMPORTANT NOTE``) or a swallowed title word is dropped at source, never
|
|
138
|
+
# admitted as a bogus code into the raw oracle.
|
|
139
|
+
tol_valid_codes = set(manual_enrich.load_tolerance_operand_inventory())
|
|
140
|
+
tol_pages = manual_enrich.iter_tolerance_operand_pages(pdf_path)
|
|
141
|
+
tol_entries, tol_order = manual_enrich.pair_tolerance_operands(
|
|
142
|
+
tol_pages, valid_codes=tol_valid_codes
|
|
143
|
+
)
|
|
144
|
+
tol_path = tol_raw_extract_path()
|
|
145
|
+
tol_payload = write_raw(tol_path, tol_entries, tol_order)
|
|
146
|
+
print("raw-extract: wrote {} tolerance entries -> {}".format(
|
|
147
|
+
len(tol_payload), tol_path))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def build_manual_corpus(argv=None, find_pdf=None):
|
|
151
|
+
"""Build the manual corpus (and, with ``--emit-raw``, the raw oracles); return 0.
|
|
152
|
+
|
|
153
|
+
``find_pdf`` defaults to ``find_manual_pdf``; the script wrapper passes its own
|
|
154
|
+
module-level name so a caller patching the wrapper still steers this function. On
|
|
155
|
+
genuine PDF absence (license-free CI) it prints an ASCII status and returns 0 — the
|
|
156
|
+
only allowed skip of the live build.
|
|
157
|
+
"""
|
|
158
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
159
|
+
emit = "--emit-raw" in argv
|
|
160
|
+
|
|
161
|
+
pdf = (find_pdf or find_manual_pdf)()
|
|
162
|
+
if pdf is None:
|
|
163
|
+
print(
|
|
164
|
+
"manual PDF not present (set OPTIVIBE_MANUAL_PDF or install OpticStudio); "
|
|
165
|
+
"skipping corpus build (license-free CI has no PDF)."
|
|
166
|
+
)
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
print("building manual corpus from the install-dir PDF ...")
|
|
170
|
+
result = manual_build.build_manual_db(pdf)
|
|
171
|
+
if result.get("skipped"):
|
|
172
|
+
print("corpus: up-to-date (content-addressed skip); nothing to rebuild.")
|
|
173
|
+
else:
|
|
174
|
+
print("corpus: pages={} chunks={}".format(
|
|
175
|
+
result["pdf_page_count"], result["chunk_count"]))
|
|
176
|
+
print("corpus db -> {}".format(result["db_path"]))
|
|
177
|
+
print("manifest -> {}".format(result["manifest_path"]))
|
|
178
|
+
|
|
179
|
+
if emit:
|
|
180
|
+
emit_raw(pdf)
|
|
181
|
+
|
|
182
|
+
return 0
|