feat(backend): add cyclone.store with BatchRecord and InMemoryStore
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""In-memory batch store for parsed X12 files.
|
||||
|
||||
Lives at module scope so route handlers and tests can import a single
|
||||
singleton. Single-process only — no cross-process coordination. Threading:
|
||||
a re-entrant lock guards every public method so FastAPI's threadpool
|
||||
request handlers can call them concurrently.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from datetime import datetime, timezone
|
||||
from typing import Iterable, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from cyclone.parsers.models import ParseResult
|
||||
from cyclone.parsers.models_835 import ParseResult835
|
||||
|
||||
|
||||
BatchKind = Literal["837p", "835"]
|
||||
|
||||
|
||||
class BatchRecord(BaseModel):
|
||||
"""One parsed file, with a stable uuid4 id and the full ParseResult."""
|
||||
|
||||
model_config = ConfigDict(extra="ignore")
|
||||
|
||||
id: str
|
||||
kind: BatchKind
|
||||
input_filename: str
|
||||
parsed_at: str # ISO 8601 UTC, e.g. "2026-06-19T12:00:00Z"
|
||||
result: ParseResult | ParseResult835 = Field(discriminator=None)
|
||||
|
||||
|
||||
class InMemoryStore:
|
||||
"""Thread-safe list of BatchRecord, newest-first on `list`."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._batches: list[BatchRecord] = []
|
||||
self._lock = threading.RLock()
|
||||
|
||||
def add(self, record: BatchRecord) -> None:
|
||||
with self._lock:
|
||||
self._batches.append(record)
|
||||
|
||||
def list(self, *, limit: int = 100) -> list[BatchRecord]:
|
||||
with self._lock:
|
||||
return list(reversed(self._batches[-limit:]))
|
||||
|
||||
def get(self, batch_id: str) -> BatchRecord | None:
|
||||
with self._lock:
|
||||
for b in self._batches:
|
||||
if b.id == batch_id:
|
||||
return b
|
||||
return None
|
||||
|
||||
def all(self) -> list[BatchRecord]:
|
||||
with self._lock:
|
||||
return list(self._batches)
|
||||
|
||||
|
||||
store = InMemoryStore()
|
||||
|
||||
|
||||
def utcnow_iso() -> str:
|
||||
"""ISO 8601 UTC timestamp, second precision, suffixed with 'Z'."""
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
Reference in New Issue
Block a user