File handling for API.
This commit is contained in:
13
src/holt59/aoc/utils/api/__init__.py
Normal file
13
src/holt59/aoc/utils/api/__init__.py
Normal 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",
|
||||
]
|
16
src/holt59/aoc/utils/api/answer.py
Normal file
16
src/holt59/aoc/utils/api/answer.py
Normal 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(),
|
||||
},
|
||||
)
|
28
src/holt59/aoc/utils/api/base.py
Normal file
28
src/holt59/aoc/utils/api/base.py
Normal 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,
|
||||
)
|
15
src/holt59/aoc/utils/api/files.py
Normal file
15
src/holt59/aoc/utils/api/files.py
Normal 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)})
|
16
src/holt59/aoc/utils/api/logger.py
Normal file
16
src/holt59/aoc/utils/api/logger.py
Normal 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()}
|
||||
)
|
57
src/holt59/aoc/utils/api/progress.py
Normal file
57
src/holt59/aoc/utils/api/progress.py
Normal 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},
|
||||
)
|
Reference in New Issue
Block a user