from __future__ import annotations
from typing import TypeVar, Generic, NamedTuple, Literal, Any, Iterator, Self, overload
from .types import DriveFileMetaFull
from ..clips.model import CLIP_ID, ClipFileUploadKey
from ..legistar.types import GUID, REAL_GUID, LegistarFileUID
IdType = TypeVar('IdType', CLIP_ID, GUID, REAL_GUID)
"""Id of an item within the top-level of :class:`MetaDict`"""
Kt = TypeVar('Kt', ClipFileUploadKey, LegistarFileUID)
"""Sub key for values within an item"""
Vt = TypeVar('Vt', bound=DriveFileMetaFull)
MetaKey = Literal['clips', 'legistar', 'legistar_rguid']
"""Top-level key for :class:`FileCache`"""
ClipCacheKey = tuple[Literal['clips'], CLIP_ID, ClipFileUploadKey]
"""Cache key for clip items"""
LegistarCacheKey = tuple[Literal['legistar'], GUID, LegistarFileUID]
"""Cache key for legistar items"""
RGuidLegistarCacheKey = tuple[Literal['legistar_rguid'], REAL_GUID, LegistarFileUID]
"""Cache key for real guid legistar items"""
MetaCacheKey = ClipCacheKey|LegistarCacheKey|RGuidLegistarCacheKey
"""Unique cache item key (union of :obj:`ClipCacheKey` and :obj:`LegistarCacheKey`)"""
_ClipMetaDict = MetaDict[CLIP_ID, ClipFileUploadKey, DriveFileMetaFull]
_LegistarMetaDict = MetaDict[GUID, LegistarFileUID, DriveFileMetaFull]
_RGuidLegistarMetaDict = MetaDict[REAL_GUID, LegistarFileUID, DriveFileMetaFull]
[docs]
class CacheCounts(NamedTuple):
clips: MetaCount
legistar: MetaCount
legistar_rguid: MetaCount
[docs]
class FileCache:
"""Container for multiple :class:`MetaDict` objects
"""
_meta_keys: list[MetaKey] = ['clips', 'legistar', 'legistar_rguid']
def __init__(self) -> None:
self.clips = _ClipMetaDict()
self.legistar = _LegistarMetaDict()
self.legistar_rguid = _RGuidLegistarMetaDict()
def get_counts(self) -> CacheCounts:
counts: dict[MetaKey, MetaCount] = {}
for key in self._meta_keys:
mdict = self[key]
counts[key] = MetaCount(
items=len(mdict),
files=mdict.count(),
)
return CacheCounts(**counts)
def get(self, cache_key: MetaCacheKey) -> DriveFileMetaFull|None:
try:
result = self[cache_key]
except KeyError:
result = None
return result
@overload
def __getitem__(self, cache_key: MetaCacheKey) -> DriveFileMetaFull: ...
@overload
def __getitem__(self, cache_key: Literal['clips']) -> _ClipMetaDict: ...
@overload
def __getitem__(self, cache_key: Literal['legistar']) -> _LegistarMetaDict: ...
@overload
def __getitem__(self, cache_key: Literal['legistar_rguid']) -> _RGuidLegistarMetaDict: ...
def __getitem__(self, cache_key: MetaCacheKey|MetaKey) -> DriveFileMetaFull|_ClipMetaDict|_LegistarMetaDict|_RGuidLegistarMetaDict:
if not isinstance(cache_key, tuple):
assert cache_key in self._meta_keys
return getattr(self, cache_key)
if cache_key[0] == 'clips':
return self.clips[cache_key[1:]]
elif cache_key[0] == 'legistar':
return self.legistar[cache_key[1:]]
else:
assert cache_key[0] == 'legistar_rguid'
return self.legistar_rguid[cache_key[1:]]
def __setitem__(self, cache_key: MetaCacheKey, value: DriveFileMetaFull) -> None:
if cache_key[0] == 'clips':
self.clips[cache_key[1:]] = value
elif cache_key[0] == 'legistar':
self.legistar[cache_key[1:]] = value
else:
assert cache_key[0] == 'legistar_rguid'
self.legistar_rguid[cache_key[1:]] = value
def __contains__(self, cache_key: MetaCacheKey) -> bool:
if cache_key[0] == 'clips':
return cache_key[1:] in self.clips
elif cache_key[0] == 'legistar':
return cache_key[1:] in self.legistar
else:
assert cache_key[0] == 'legistar_rguid'
return cache_key[1:] in self.legistar_rguid
def update(self, other: Self) -> None:
self.clips.update(other.clips)
self.legistar.update(other.legistar)
self.legistar_rguid.update(other.legistar_rguid)
def keys(self) -> Iterator[MetaKey]:
yield from self._meta_keys
def serialize(self):
return {k: self[k].serialize() for k in self._meta_keys}
@classmethod
def deserialize(cls, data: dict[MetaKey, dict]) -> Self:
obj = cls()
for key in cls._meta_keys:
mdict = obj[key]
if key not in data:
continue
mdict._items.update(data[key])
return obj