File handling for API.

This commit is contained in:
Mikael CAPELLE
2024-12-10 15:38:00 +01:00
parent 3c544c559b
commit 46558672e8
18 changed files with 288 additions and 160 deletions

View File

@@ -0,0 +1,13 @@
from .answer import dump_answer
from .base import dump_api_message
from .files import FileHandlerAPI
from .logger import LoggerAPIHandler
from .progress import ProgressAPI
__all__ = [
"dump_answer",
"dump_api_message",
"FileHandlerAPI",
"LoggerAPIHandler",
"ProgressAPI",
]

View File

@@ -0,0 +1,16 @@
from datetime import timedelta
from typing import Any
from .base import dump_api_message
def dump_answer(part: int, answer: Any, answer_time: timedelta, total_time: timedelta):
dump_api_message(
"answer",
{
"answer": part,
"value": str(answer),
"answerTime_s": answer_time.total_seconds(),
"totalTime_s": total_time.total_seconds(),
},
)

View File

@@ -0,0 +1,28 @@
import json
import sys
from datetime import datetime
from typing import Any, Literal, TextIO
def _datetime_formatter(value: Any) -> Any:
if isinstance(value, datetime):
return value.isoformat()
else:
return value
def dump_api_message(
type: Literal[
"log", "answer", "file", "progress-start", "progress-step", "progress-end"
],
content: Any,
file: TextIO = sys.stdout,
):
print(
json.dumps(
{"type": type, "time": datetime.now(), "content": content},
default=_datetime_formatter,
),
flush=True,
file=file,
)

View File

@@ -0,0 +1,15 @@
from pathlib import Path
from typing import Final
from .base import dump_api_message
class FileHandlerAPI:
def __init__(self, folder: Path):
self.folder: Final = folder
def create(self, filename: str, content: bytes, text: bool = False):
self.folder.mkdir(exist_ok=True)
with open(self.folder.joinpath(filename), "wb") as fp:
fp.write(content)
dump_api_message("file", {"filename": filename, "size": len(content)})

View File

@@ -0,0 +1,16 @@
import logging.handlers
import sys
from typing import TextIO
from .base import dump_api_message
class LoggerAPIHandler(logging.Handler):
def __init__(self, output: TextIO = sys.stdout):
super().__init__()
self.output = output
def emit(self, record: logging.LogRecord):
dump_api_message(
"log", {"level": record.levelname, "message": record.getMessage()}
)

View File

@@ -0,0 +1,57 @@
import sys
from datetime import datetime, timedelta
from typing import Iterable, Iterator, Sequence, TextIO, TypeVar
from .base import dump_api_message
_T = TypeVar("_T")
class ProgressAPI:
def __init__(
self,
min_step: int = 1,
min_time: timedelta = timedelta(milliseconds=100),
output: TextIO = sys.stdout,
):
super().__init__()
self.counter = 0
self.output = output
self.min_step = min_step
self.min_time = min_time
def wrap(
self, values: Sequence[_T] | Iterable[_T], total: int | None = None
) -> Iterator[_T]:
total = total or len(values) # type: ignore
current = self.counter
self.counter += 1
dump_api_message("progress-start", {"counter": current, "total": total})
try:
percent = 0
time = datetime.now()
for i_value, value in enumerate(values):
yield value
if datetime.now() - time < self.min_time:
continue
time = datetime.now()
c_percent = round(i_value / total * 100)
if c_percent >= percent + self.min_step:
dump_api_message(
"progress-step", {"counter": current, "percent": c_percent}
)
percent = c_percent
finally:
dump_api_message(
"progress-end",
{"counter": current},
)