Innovenergy_trunk/csharp/App/Backend/patch_missing_alarms.py

116 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""
patch_missing_alarms.py
Re-translates specific keys that failed during a previous run of
generate_alarm_translations.py (e.g. due to LLM JSON-format glitches),
then regenerates AlarmKnowledgeBase.cs with the now-complete English set.
Translates one key per API call to dodge the multi-key JSON formatting
issue that caused the original failures.
Edit MISSING below to set which keys to retry per language, then run:
cd csharp/App/Backend
python3 patch_missing_alarms.py
"""
import json
import os
import sys
from generate_alarm_translations import (
translate_batch,
parse_kb_key_sections,
write_knowledge_base_cs,
load_env_file,
CHECKED_FILE,
KNOWLEDGE_BASE,
RESOURCES_DIR,
TARGET_LANGUAGES,
)
# Keys that failed during the 2026-04-28 run.
# Update this dict if a new run produces different failures.
MISSING = {
"en": [
"DcBusOvervoltage",
"DcBusUndervoltage",
"DcBusVoltageUnbalance",
"BusSlowOvervoltage",
"HardwareBusOvervoltage",
],
"it": [
"NtcTemperatureSensorBroken",
"SyncSignalAbnormal",
"GridStartupConditionsNotMet",
"BatteryCommunicationFailure",
"BatteryDisconnected",
],
}
def main():
api_key = os.environ.get("MISTRAL_API_KEY", "").strip()
if not api_key:
script_dir = os.path.dirname(os.path.abspath(__file__))
api_key = load_env_file(os.path.join(script_dir, ".env")).get("MISTRAL_API_KEY", "").strip()
if not api_key:
print("ERROR: MISTRAL_API_KEY not found in environment or .env file.")
sys.exit(1)
print("MISTRAL_API_KEY loaded.")
with open(CHECKED_FILE, encoding="utf-8-sig") as f:
de = json.load(f)
en_translations = None
for lang_code, missing_keys in MISSING.items():
lang_name = TARGET_LANGUAGES[lang_code]
out_file = os.path.join(RESOURCES_DIR, f"AlarmTranslationsChecked.{lang_code}.json")
with open(out_file, encoding="utf-8") as f:
existing = json.load(f)
# Idempotent: only translate keys that are still genuinely missing from the JSON.
actually_missing = [
k for k in missing_keys
if k in de and (k not in existing or not existing[k].get("Explanation"))
]
if not actually_missing:
print(f"\n── {lang_name} ({lang_code}) already complete ({len(existing)} entries) — skipping translation ──")
else:
print(f"\n── Patching {lang_name} ({lang_code}) — {len(actually_missing)} keys ──")
translated = {}
for key in actually_missing:
print(f" {key}")
result = translate_batch(api_key, {key: de[key]}, lang_name)
if result and key in result:
r = result[key]
translated[key] = {
"Explanation": r.get("Explanation", ""),
"Causes": r.get("Causes", []),
"NextSteps": r.get("NextSteps", []),
}
snippet = r.get("Explanation", "")[:80]
print(f" OK: {snippet}{'...' if len(r.get('Explanation','')) > 80 else ''}")
else:
print(f" FAILED: {key}")
existing.update(translated)
with open(out_file, "w", encoding="utf-8") as f:
json.dump(existing, f, ensure_ascii=False, indent=2)
print(f" ✓ Wrote {len(existing)} total entries → {out_file}")
if lang_code == "en":
en_translations = existing
if en_translations is not None and os.path.exists(KNOWLEDGE_BASE):
print("\n── Regenerating AlarmKnowledgeBase.cs ──")
key_sections = parse_kb_key_sections(KNOWLEDGE_BASE)
write_knowledge_base_cs(KNOWLEDGE_BASE, en_translations, key_sections)
print("\n✓ Patch done.")
if __name__ == "__main__":
main()