Files
Community_Nursing_Atlas/server/cli.py
T
jsahn 4e943700fc feat: add common utilities and feedback handling scripts
- Introduced common.js for shared functions including API calls, error reporting, and UI helpers.
- Implemented feedback.js for the feedback reporting page, including form handling and displaying feedback status.
- Added layout.js for managing galaxy and section layouts in the visualization.
- Created login.js for user authentication with session management.
- Developed search.js for term searching based on user input and synonyms.
- Implemented signup.js for user registration with validation and immediate login.
2026-10-06 23:14:45 +09:00

101 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""피드백·오류를 터미널에서 확인하고 처리하는 도구.
.venv/bin/python -m server.cli feedback # 처리 대기(new, in_progress) 목록
.venv/bin/python -m server.cli feedback --all # 전체
.venv/bin/python -m server.cli show 12 # 한 건 자세히 (대상 용어 현재 값 포함)
.venv/bin/python -m server.cli resolve 12 "정의 문장을 교재 p.188 기준으로 수정"
.venv/bin/python -m server.cli status 12 in_progress
.venv/bin/python -m server.cli term 지역사회 간호사정 # 용어 현재 값 확인
"""
import argparse
import json
from . import db
def _print_fb(r, full=False):
tgt = f" [{r['target_type']}:{r['target_label'] or r['target_id']}]" if r["target_type"] else ""
occ = f" ×{r['occurrences']}" if (r["occurrences"] or 1) > 1 else ""
print(f"#{r['id']:<4} {r['status']:<11} {r['kind']:<12} {r['category'] or ''}{tgt}{occ} {r['created_at']}")
msg = r["message"] or ""
print(" " + (msg if full else msg[:160].replace("\n", " ")))
if r["suggestion"]:
print(" → 제안: " + (r["suggestion"] if full else r["suggestion"][:160]))
if full:
for k in ("page_url", "reporter", "contact", "admin_note", "resolution", "handled_by", "user_agent"):
if r[k]:
print(f" {k}: {r[k]}")
if r["context"]:
print(" context:", r["context"][:3000])
def main():
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
f = sub.add_parser("feedback")
f.add_argument("--all", action="store_true")
f.add_argument("--kind")
s = sub.add_parser("show")
s.add_argument("id", type=int)
r = sub.add_parser("resolve")
r.add_argument("id", type=int)
r.add_argument("resolution")
st = sub.add_parser("status")
st.add_argument("id", type=int)
st.add_argument("status", choices=["new", "in_progress", "resolved", "rejected", "duplicate"])
t = sub.add_parser("term")
t.add_argument("name", nargs="+")
a = ap.parse_args()
con = db.connect()
if a.cmd == "feedback":
where = "1=1" if a.all else "status IN ('new','in_progress')"
args = []
if a.kind:
where += " AND kind=?"
args.append(a.kind)
rows = con.execute(f"SELECT * FROM feedback WHERE {where} ORDER BY id", args).fetchall()
print(f"{len(rows)}건")
for r in rows:
_print_fb(r)
elif a.cmd == "show":
r = con.execute("SELECT * FROM feedback WHERE id=?", (a.id,)).fetchone()
if not r:
return print("없음")
_print_fb(r, full=True)
if r["target_type"] == "term" and r["target_id"]:
_show_term(con, r["target_id"])
elif a.cmd in ("resolve", "status"):
status = "resolved" if a.cmd == "resolve" else a.status
with con:
if a.cmd == "resolve":
con.execute("UPDATE feedback SET status=?, resolution=?, handled_by='developer', updated_at=? WHERE id=?",
(status, a.resolution, db.now(), a.id))
else:
con.execute("UPDATE feedback SET status=?, handled_by='developer', updated_at=? WHERE id=?", (status, db.now(), a.id))
db.audit(con, "developer(cli)", "update", "feedback", str(a.id), None, {"status": status})
print("완료")
elif a.cmd == "term":
name = " ".join(a.name)
r = con.execute("SELECT term_id FROM terms WHERE name=? OR term_id=?", (name, name)).fetchone()
if not r:
rows = con.execute("SELECT term_id,name FROM terms WHERE name LIKE ? LIMIT 20", (f"%{name}%",)).fetchall()
for x in rows:
print(x["term_id"], x["name"])
return
_show_term(con, r["term_id"])
def _show_term(con, term_id):
t = db.row(con.execute("SELECT * FROM terms WHERE term_id=?", (term_id,)).fetchone())
print("\n--- 대상 용어 현재 값")
print(json.dumps({k: t[k] for k in ("term_id", "name", "aliases", "galaxy_id", "section_id", "definition_text",
"definition_book", "definition_page", "easy", "example", "review_status")},
ensure_ascii=False, indent=1))
for s in con.execute("SELECT book_id,page,path,snippet FROM term_sources WHERE term_id=?", (term_id,)):
print(f" 출처 {s['book_id']} p.{s['page']} {s['path']}\n {(s['snippet'] or '')[:150]}")
if __name__ == "__main__":
main()