HPI/my/topcoder.py

91 lines
2.5 KiB
Python

from my.config import topcoder as config # type: ignore[attr-defined]
from dataclasses import dataclass
from functools import cached_property
import json
from pathlib import Path
from typing import Iterator, Sequence
from my.core import get_files, Res, datetime_aware
from my.core.compat import fromisoformat, NoneType
def inputs() -> Sequence[Path]:
return get_files(config.export_path)
@dataclass
class Competition:
contest_id: str
contest: str
percentile: float
date_str: str
@cached_property
def uid(self) -> str:
return self.contest_id
@cached_property
def when(self) -> datetime_aware:
return fromisoformat(self.date_str)
@cached_property
def summary(self) -> str:
return f'participated in {self.contest}: {self.percentile:.0f}'
@classmethod
def make(cls, j) -> Iterator[Res['Competition']]:
assert isinstance(j.pop('rating'), float)
assert isinstance(j.pop('placement'), int)
cid = j.pop('challengeId')
cname = j.pop('challengeName')
percentile = j.pop('percentile')
date_str = j.pop('date')
yield cls(
contest_id=cid,
contest=cname,
percentile=percentile,
date_str=date_str,
)
def _parse_one(p: Path) -> Iterator[Res[Competition]]:
j = json.loads(p.read_text())
# this is kind of an experiment to parse it exhaustively, making sure we don't miss any data
assert isinstance(j.pop('version'), str)
assert isinstance(j.pop('id'), str)
[j] = j.values() # zoom in
assert j.pop('success') is True, j
assert j.pop('status') == 200, j
assert j.pop('metadata') is None, j
[j] = j.values() # zoom in
# todo hmm, potentially error handling could be nicer since .pop just reports key error
# also by the time error is reported, key is already removed?
for k in ['handle', 'handleLower', 'userId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']:
# check it's primitive
assert isinstance(j.pop(k), (str, bool, float, int, NoneType)), k
j.pop('DEVELOP') # TODO how to handle it?
[j] = j.values() # zoom in, DATA_SCIENCE section
mm = j.pop('MARATHON_MATCH')
[mm] = mm.values() # zoom into historu
srm = j.pop('SRM')
[srm] = srm.values() # zoom into history
assert len(j) == 0, j
for c in mm + srm:
yield from Competition.make(j=c)
def data() -> Iterator[Res[Competition]]:
*_, last = inputs()
return _parse_one(last)