Prompt-Builder / src /profanity /profanity_server.py
ArielJoe's picture
feat: cross-detector language policy + tidy structure & file naming
5ddfd1f
Raw
History Blame Contribute Delete
4 kB
"""
Server HTTP untuk Profanity Checker — http://127.0.0.1:8005
Endpoint:
GET /api/status — health-check
POST /api/profanity — {"text": "..."}
→ {"findings": [...], "finding_count": int}
Jalankan:
python src/profanity_server.py
"""
from __future__ import annotations
import json
import logging
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from profanity_detector import ProfanityChecker
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-7s %(message)s",
datefmt="%H:%M:%S",
)
logger = logging.getLogger(__name__)
_checker: ProfanityChecker | None = None
def _get() -> ProfanityChecker:
global _checker
if _checker is None:
_checker = ProfanityChecker()
_checker.load()
return _checker
class Handler(BaseHTTPRequestHandler):
def _cors(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
def do_OPTIONS(self):
self.send_response(204); self._cors(); self.end_headers()
def _json(self, status: int, body: object):
payload = json.dumps(body, ensure_ascii=False).encode()
try:
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(payload)))
self._cors()
self.end_headers()
self.wfile.write(payload)
except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
pass
def _body(self) -> dict | None:
n = int(self.headers.get("Content-Length", 0))
if not n:
return {}
try:
return json.loads(self.rfile.read(n))
except (json.JSONDecodeError, ValueError):
return None
def log_message(self, fmt, *args):
logger.info("%-6s %s", args[0] if args else "", args[1] if len(args) > 1 else "")
def do_GET(self):
if self.path == "/api/status":
chk = _get()
self._json(200, {"ready": chk.is_loaded, "lexicon_size": chk.lexicon_size})
else:
self._json(404, {"error": "Not found"})
def do_POST(self):
if self.path != "/api/profanity":
self._json(404, {"error": "Not found"}); return
body = self._body()
if body is None:
self._json(400, {"error": "JSON tidak valid."}); return
text = str(body.get("text", "")).strip()
if not text:
self._json(400, {"error": "Field 'text' kosong."}); return
findings = _get().check(text)
self._json(200, {
"text": text,
"finding_count": len(findings),
"findings": [
{
"word": f.word,
"normalized": f.normalized,
"start": f.start,
"end": f.end,
"severity": f.severity,
"reason": f.reason,
"confidence": f.confidence,
}
for f in findings
],
})
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=8005)
parser.add_argument("--host", default="127.0.0.1")
args = parser.parse_args()
logger.info("Memuat lexicon profanity…")
_get()
server = HTTPServer((args.host, args.port), Handler)
logger.info("Profanity server berjalan di http://%s:%d", args.host, args.port)
logger.info("Buka web/profanity-test.html untuk pengujian.")
try:
server.serve_forever()
except KeyboardInterrupt:
server.server_close()