- cherry: orofacial/TMD PPTX presentations + 음악가 구강건강 가이드 - papa: sinus/infraoccluded PPTX, garden-monitor Arduino, MIDI files - databases: PCSP scraper, translation scripts, vision benchmark, batch JSONs - web-ui: code.js/companion.py/styles.css major updates, autostart installer - src: ollama-client, session, LLMProvider, ollama-adapter patches - .gitignore: add claude-key, SQLite WAL/SHM, __pycache__, nohup.out Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Batch translate PCSP article titles to Korean using the homeclaw gateway API."""
|
|
|
|
import sqlite3
|
|
import json
|
|
import time
|
|
import sys
|
|
import urllib.request
|
|
import urllib.parse
|
|
|
|
DB_PATH = "/home/kim/homeclaw/.smallclaw/databases/psychotherapy_cases.db"
|
|
API_URL = "http://localhost:18789/v1/chat/completions"
|
|
|
|
def translate_batch(titles, model="exa-one-3.0:free"):
|
|
"""Translate a batch of titles using the gateway API."""
|
|
prompt = """Translate the following psychology/psychotherapy article titles from English to Korean.
|
|
Rules:
|
|
- Keep person names in quotes as-is (e.g., "Fran", "Jane", "Tommy")
|
|
- Keep acronyms as-is (CBT, DBT, AEDP, OCD, etc.)
|
|
- Translate therapy names: Cognitive Behavioral Therapy→인지행동치료, Dialectical Behavior Therapy→변증법적행동치료, Psychodynamic→정신역동, Schema Therapy→도식치료, Exposure Therapy→노출치료, etc.
|
|
- Keep the overall meaning and tone professional
|
|
- Each line has format: NUMBER|ENGLISH_TITLE
|
|
- Return ONLY the translations in format: NUMBER|KOREAN_TITLE
|
|
- Do not add any explanations or notes
|
|
|
|
"""
|
|
for id_, title, cat in titles:
|
|
prompt += f"{id_}|{title}\n"
|
|
|
|
data = json.dumps({
|
|
"model": model,
|
|
"messages": [
|
|
{"role": "system", "content": "You are a professional Korean-English translator specializing in psychology and psychotherapy terminology. Translate accurately and naturally."},
|
|
{"role": "user", "content": prompt}
|
|
],
|
|
"temperature": 0.3,
|
|
"max_tokens": 4096,
|
|
}).encode("utf-8")
|
|
|
|
req = urllib.request.Request(
|
|
API_URL,
|
|
data=data,
|
|
headers={"Content-Type": "application/json"},
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=120) as resp:
|
|
result = json.loads(resp.read().decode("utf-8"))
|
|
return result["choices"][0]["message"]["content"]
|
|
except Exception as e:
|
|
print(f"API error: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
def main():
|
|
conn = sqlite3.connect(DB_PATH)
|
|
c = conn.cursor()
|
|
|
|
# Get untranslated titles
|
|
c.execute("SELECT id, title_en, category_en FROM cases WHERE title_ko = '' OR title_ko IS NULL ORDER BY id")
|
|
rows = c.fetchall()
|
|
|
|
if not rows:
|
|
print("All titles translated!")
|
|
return
|
|
|
|
print(f"Translating {len(rows)} remaining titles...")
|
|
|
|
batch_size = 20
|
|
total_translated = 0
|
|
|
|
for i in range(0, len(rows), batch_size):
|
|
batch = rows[i:i+batch_size]
|
|
print(f"\nBatch {i//batch_size + 1}/{(len(rows) + batch_size - 1)//batch_size} ({len(batch)} titles)...")
|
|
|
|
result = translate_batch(batch)
|
|
if not result:
|
|
print("Translation failed, skipping batch")
|
|
continue
|
|
|
|
# Parse translations
|
|
for line in result.strip().split("\n"):
|
|
line = line.strip()
|
|
if "|" in line:
|
|
parts = line.split("|", 1)
|
|
try:
|
|
id_ = int(parts[0].strip())
|
|
title_ko = parts[1].strip()
|
|
# Remove markdown formatting if present
|
|
title_ko = title_ko.strip("*").strip()
|
|
c.execute("UPDATE cases SET title_ko = ? WHERE id = ?", (title_ko, id_))
|
|
total_translated += 1
|
|
except (ValueError, IndexError):
|
|
print(f" Skipping malformed line: {line}")
|
|
continue
|
|
|
|
conn.commit()
|
|
print(f" Translated so far: {total_translated}")
|
|
time.sleep(2) # Rate limiting
|
|
|
|
# Final stats
|
|
c.execute("SELECT COUNT(*) FROM cases WHERE title_ko != '' AND title_ko IS NOT NULL")
|
|
translated = c.fetchone()[0]
|
|
c.execute("SELECT COUNT(*) FROM cases WHERE title_ko = '' OR title_ko IS NULL")
|
|
remaining = c.fetchone()[0]
|
|
|
|
print(f"\nDone! Translated: {translated}, Remaining: {remaining}")
|
|
|
|
# Sample
|
|
print("\n--- Sample translations ---")
|
|
c.execute("SELECT title_en, title_ko FROM cases WHERE title_ko != '' AND title_ko IS NOT NULL ORDER BY RANDOM() LIMIT 10")
|
|
for row in c.fetchall():
|
|
en = row[0][:60]
|
|
ko = row[1][:60]
|
|
print(f" EN: {en}...")
|
|
print(f" KO: {ko}...")
|
|
print()
|
|
|
|
conn.close()
|
|
|
|
if __name__ == "__main__":
|
|
main() |