Initial commit: ABAP indexing pipeline (ingest, parser, summarize, index, query, wiki)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
byeongwook.choi
2026-09-21 13:23:37 +09:00
co-authored by Claude Fable 5.1
commit 11ae3629b2
453 changed files with 259183 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
"""위키 생성 CLI (계획서 v4 §5.7, §11.6).
python -m wiki_out.run [--program X] [--all] [--limit N] [--dry-run]
- 요약(Stage 3) 이 없어도 파서 구조 사실만으로 문서를 만든다(조각이 생기면 재실행 시 채워짐).
- programs/packages/concepts 는 사람 교정 보존 병합(merge.py). 로직 조각은 프로그램 문서 안의 섹션.
"""
from __future__ import annotations
import argparse
import json
import re
from config.glossary import load_glossary
from config.settings import settings
from index.db import connect
from .entities import write_entity_wiki
from .merge import merge_write
from .okf_writer import PIPELINE, _fm, _lst, _now, _q, write_manifest, write_program_wiki
def write_concepts() -> int:
"""도메인 용어 사전을 concepts/ 문서로 출력.
사전 로딩은 config.glossary 로 옮겼다 — 질의 확장(query/expand.py)이 같은 사전을 써야 하고,
파싱 규칙이 두 곳에 있으면 갈라진다.
"""
glossary = load_glossary()
count = 0
for concept, related in glossary.items():
fm = [
"type: concept",
f"title: {_q(concept)}",
f"description: {_q(concept + ' — 도메인 개념 (질의 확장용)')}",
f"tags: {_lst(related[:10])}",
f"generated: {{ by: {_q(PIPELINE)}, at: {_q(_now())} }}",
]
body = ["## 관련 용어", "",
"이 목록은 검색 질의 확장에도 쓰인다 — `/search/logic?q=` 에 아래 어느 말로 물어도 걸린다.",
""] + [f"- {t}" for t in related] + [""]
rel = f"concepts/{re.sub(r'[^0-9A-Za-z가-힣_.-]', '_', concept)}.md"
merge_write(settings.wiki_dir / rel, _fm(fm) + "\n" + "\n".join(body),
code_hash=None, wiki_dir=settings.wiki_dir, doc_rel=rel)
count += 1
return count
def main() -> None:
ap = argparse.ArgumentParser(description="OKF 위키 생성")
ap.add_argument("--program", default=None, help="프로그램 하나만")
ap.add_argument("--all", action="store_true", help="소스 있는 전체 프로그램")
ap.add_argument("--limit", type=int, default=None)
ap.add_argument("--no-entities", action="store_true",
help="tables/ functions/ tcodes/ 엔티티 페이지를 만들지 않는다")
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
con = connect()
try:
if args.program:
names = [args.program.upper()]
elif args.all:
rows = con.execute(
"SELECT name FROM program WHERE has_source=1 ORDER BY name").fetchall()
names = [r["name"] for r in rows]
else:
ap.error("--program 또는 --all 필요")
if args.limit:
names = names[: args.limit]
if args.dry_run:
print(json.dumps({"would_process": len(names)}, ensure_ascii=False))
return
totals = {"programs": 0, "chunks": 0, "packages": 0, "failed": 0}
for name in names:
try:
s = write_program_wiki(name, con=con)
totals["programs"] += s["programs"]
totals["chunks"] += s["chunks"]
totals["packages"] += s["packages"]
except Exception as e: # noqa: BLE001
totals["failed"] += 1
print(f"[FAIL] {name}: {type(e).__name__}: {e}")
totals["concepts"] = write_concepts()
if not args.no_entities:
# tables/ functions/ tcodes/ — 파서 사실만으로 만들어진다 (수정사항 8번)
totals.update(write_entity_wiki(con, settings.wiki_dir))
write_manifest(settings.wiki_dir, con)
print(json.dumps(totals, ensure_ascii=False))
finally:
con.close()
if __name__ == "__main__":
main()