Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Run log.
|
||||
|
||||
Python buffers stdout when it is not a terminal, so a background run that hung
|
||||
produced a zero-byte log and there was no way to see how far it had got — the
|
||||
only evidence left was a screenshot. Every run now tees to
|
||||
`Logs\\run_<timestamp>.log`, flushed per line, so a stuck or killed run still
|
||||
leaves a readable trail.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from .. import settings as cfg
|
||||
|
||||
|
||||
class _Tee:
|
||||
def __init__(self, stream, handle):
|
||||
self._stream = stream
|
||||
self._handle = handle
|
||||
|
||||
def write(self, text):
|
||||
try:
|
||||
self._stream.write(text)
|
||||
self._stream.flush()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self._handle.write(text)
|
||||
self._handle.flush() # a killed run must still have its log
|
||||
except Exception:
|
||||
pass
|
||||
return len(text)
|
||||
|
||||
def flush(self):
|
||||
for target in (self._stream, self._handle):
|
||||
try:
|
||||
target.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def isatty(self):
|
||||
return getattr(self._stream, "isatty", lambda: False)()
|
||||
|
||||
|
||||
def _force_utf8_console() -> None:
|
||||
"""
|
||||
Make the console accept the characters this bot actually prints.
|
||||
|
||||
Windows hands Python a cp1252 console, which cannot encode an em dash or an
|
||||
arrow — and nearly every log line here has one. The tee below swallows the
|
||||
resulting UnicodeEncodeError so a run never dies of it, which is right but
|
||||
means those lines were being **silently dropped from the console** while
|
||||
landing correctly in the log file. Watching a run and reading its log
|
||||
afterwards showed different things, which is a nasty way to debug.
|
||||
|
||||
`errors="replace"` as well as the encoding: a console that still cannot
|
||||
represent a character should print a box, not raise.
|
||||
"""
|
||||
for stream in (sys.__stdout__, sys.__stderr__):
|
||||
try:
|
||||
stream.reconfigure(encoding="utf-8", errors="replace")
|
||||
except Exception:
|
||||
pass # not a real console, or too old to care
|
||||
|
||||
def start() -> "object":
|
||||
"""Begin teeing stdout/stderr. Returns the log path."""
|
||||
_force_utf8_console()
|
||||
cfg.LOGS.mkdir(parents=True, exist_ok=True)
|
||||
path = cfg.LOGS / f"run_{datetime.now():%Y%m%d_%H%M%S}.log"
|
||||
handle = open(path, "a", encoding="utf-8", buffering=1)
|
||||
handle.write(f"\n{'=' * 70}\n{datetime.now():%Y-%m-%d %H:%M:%S} — run start\n"
|
||||
f"{'=' * 70}\n")
|
||||
sys.stdout = _Tee(sys.__stdout__, handle)
|
||||
sys.stderr = _Tee(sys.__stderr__, handle)
|
||||
return path
|
||||
Reference in new issue
Block a user