- 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>
125 lines
4.0 KiB
Python
125 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Translate psychotherapy_cases abstract_en to abstract_ko using the Anthropic API."""
|
|
|
|
import sqlite3
|
|
import json
|
|
import time
|
|
import sys
|
|
import re
|
|
import html
|
|
import urllib.request
|
|
|
|
DB_PATH = "/home/kim/homeclaw/.smallclaw/databases/psychotherapy_cases.db"
|
|
API_URL = "http://localhost:11434/v1/chat/completions"
|
|
MODEL = "kimi-k2.6:cloud"
|
|
|
|
|
|
def clean_abstract(text):
|
|
"""Remove HTML entities and scraped noise from abstract text."""
|
|
text = html.unescape(text)
|
|
text = re.sub(r'\s*(Downloads|PDF|Published|[0-9]{2}/[0-9]{2}/[0-9]{4})\s*', ' ', text)
|
|
text = re.sub(r'\t+', ' ', text)
|
|
text = re.sub(r'\n{3,}', '\n\n', text)
|
|
text = re.sub(r'[ \t]{2,}', ' ', text)
|
|
return text.strip()
|
|
|
|
|
|
def translate_abstract(id_, title_en, abstract_en):
|
|
cleaned = clean_abstract(abstract_en)
|
|
if len(cleaned) < 50:
|
|
return None
|
|
|
|
prompt = f"""Translate the following psychotherapy research article abstract from English to Korean.
|
|
|
|
Article title: {title_en}
|
|
|
|
Abstract:
|
|
{cleaned}
|
|
|
|
Rules:
|
|
- Translate the full abstract accurately and professionally
|
|
- Keep therapy acronyms (DBT, CBT, AEDP, EFT, ACT, OCD, BPD, PTSD, etc.) as-is
|
|
- Keep person names in quotes as-is (e.g., "Jane", "Tommy")
|
|
- Keep statistical values and measurements as-is
|
|
- Use professional Korean psychology/psychotherapy terminology
|
|
- Return ONLY the Korean translation, no explanations
|
|
"""
|
|
|
|
data = json.dumps({
|
|
"model": MODEL,
|
|
"messages": [
|
|
{"role": "system", "content": "You are a professional Korean-English translator specializing in psychotherapy and clinical psychology. Translate accurately and naturally using standard Korean psychological terminology."},
|
|
{"role": "user", "content": prompt},
|
|
],
|
|
"temperature": 0.3,
|
|
}).encode("utf-8")
|
|
|
|
req = urllib.request.Request(
|
|
API_URL,
|
|
data=data,
|
|
headers={"Content-Type": "application/json"},
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=180) as resp:
|
|
result = json.loads(resp.read().decode("utf-8"))
|
|
return result["choices"][0]["message"]["content"].strip()
|
|
except Exception as e:
|
|
print(f" API error for id={id_}: {e}", file=sys.stderr, flush=True)
|
|
return None
|
|
|
|
|
|
def main():
|
|
conn = sqlite3.connect(DB_PATH)
|
|
c = conn.cursor()
|
|
|
|
c.execute("""SELECT id, title_en, abstract_en FROM cases
|
|
WHERE (abstract_ko = '' OR abstract_ko IS NULL)
|
|
AND abstract_en IS NOT NULL AND abstract_en != ''
|
|
ORDER BY id""")
|
|
rows = c.fetchall()
|
|
|
|
if not rows:
|
|
print("All abstracts translated!")
|
|
conn.close()
|
|
return
|
|
|
|
print(f"Translating {len(rows)} abstracts...", flush=True)
|
|
|
|
total_translated = 0
|
|
for i, (id_, title_en, abstract_en) in enumerate(rows):
|
|
print(f"\n[{i+1}/{len(rows)}] id={id_}: {title_en[:60]}...", flush=True)
|
|
|
|
abstract_ko = translate_abstract(id_, title_en, abstract_en)
|
|
if not abstract_ko:
|
|
print(" Failed, skipping", flush=True)
|
|
continue
|
|
|
|
c.execute("UPDATE cases SET abstract_ko = ? WHERE id = ?", (abstract_ko, id_))
|
|
conn.commit()
|
|
total_translated += 1
|
|
print(f" Done ({len(abstract_ko)} chars)", flush=True)
|
|
time.sleep(3)
|
|
|
|
c.execute("SELECT COUNT(*) FROM cases WHERE abstract_ko != '' AND abstract_ko IS NOT NULL")
|
|
done = c.fetchone()[0]
|
|
c.execute("SELECT COUNT(*) FROM cases WHERE (abstract_ko = '' OR abstract_ko IS NULL) AND abstract_en IS NOT NULL AND abstract_en != ''")
|
|
remaining = c.fetchone()[0]
|
|
|
|
print(f"\nDone! Translated: {done} total, {remaining} remaining with English abstracts", flush=True)
|
|
|
|
print("\n--- Sample translations ---")
|
|
c.execute("""SELECT title_en, abstract_ko FROM cases
|
|
WHERE abstract_ko != '' AND abstract_ko IS NOT NULL
|
|
ORDER BY RANDOM() LIMIT 3""")
|
|
for row in c.fetchall():
|
|
print(f" Title: {row[0][:70]}")
|
|
print(f" KO: {row[1][:200]}...")
|
|
print()
|
|
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|