convert fallback location estimators to be iterators
This commit is contained in:
parent
1cf9dfe5dd
commit
da8f541cdc
4 changed files with 32 additions and 20 deletions
|
@ -1,17 +1,24 @@
|
||||||
# TODO: add config here which passes kwargs to estimate_from (under_accuracy)
|
# TODO: add config here which passes kwargs to estimate_from (under_accuracy)
|
||||||
# overwritable by passing the kwarg name here to the top-level estimate_location
|
# overwritable by passing the kwarg name here to the top-level estimate_location
|
||||||
|
|
||||||
from typing import Union
|
from typing import Iterator
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from my.location.fallback.common import estimate_from, FallbackLocation
|
from my.core.source import import_source
|
||||||
|
from my.location.fallback.common import estimate_from, FallbackLocation, DateExact
|
||||||
|
|
||||||
def estimate_location(dt: Union[datetime, float, int]) -> FallbackLocation:
|
# note: the import_source returns an iterator
|
||||||
|
@import_source(module_name="my.location.fallback.via_home")
|
||||||
|
def _home_estimate(dt: DateExact) -> Iterator[FallbackLocation]:
|
||||||
from my.location.fallback.via_home import estimate_location as via_home
|
from my.location.fallback.via_home import estimate_location as via_home
|
||||||
|
|
||||||
|
yield from via_home(dt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def estimate_location(dt: DateExact) -> FallbackLocation:
|
||||||
loc = estimate_from(
|
loc = estimate_from(
|
||||||
dt,
|
dt,
|
||||||
estimators=(via_home,)
|
estimators=(_home_estimate,)
|
||||||
)
|
)
|
||||||
if loc is None:
|
if loc is None:
|
||||||
raise ValueError("Could not estimate location")
|
raise ValueError("Could not estimate location")
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, Callable, Sequence, Iterator, List, Union
|
from typing import Optional, Callable, Sequence, Iterator, List, Union
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, date
|
||||||
|
|
||||||
from ..common import LocationProtocol, Location
|
from ..common import LocationProtocol, Location
|
||||||
DateIsh = Union[datetime, float, int]
|
DateExact = Union[datetime, float, int] # float/int as epoch timestamps
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FallbackLocation(LocationProtocol):
|
class FallbackLocation(LocationProtocol):
|
||||||
|
@ -62,28 +62,31 @@ class FallbackLocation(LocationProtocol):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
LocationEstimator = Callable[[DateIsh], Optional[FallbackLocation]]
|
# a location estimator can return multiple fallbacks, incase there are
|
||||||
|
# differing accuracies/to allow for possible matches to be computed
|
||||||
|
# iteratively
|
||||||
|
LocationEstimator = Callable[[DateExact], Iterator[FallbackLocation]]
|
||||||
LocationEstimators = Sequence[LocationEstimator]
|
LocationEstimators = Sequence[LocationEstimator]
|
||||||
|
|
||||||
# helper function, instead of dealing with datetimes while comparing, just use epoch timestamps
|
# helper function, instead of dealing with datetimes while comparing, just use epoch timestamps
|
||||||
def _datetime_timestamp(dt: DateIsh) -> float:
|
def _datetime_timestamp(dt: DateExact) -> float:
|
||||||
if isinstance(dt, datetime):
|
if isinstance(dt, datetime):
|
||||||
return dt.timestamp()
|
return dt.timestamp()
|
||||||
return float(dt)
|
return float(dt)
|
||||||
|
|
||||||
def _iter_estimate_from(
|
def _iter_estimate_from(
|
||||||
dt: DateIsh,
|
dt: DateExact,
|
||||||
estimators: LocationEstimators,
|
estimators: LocationEstimators,
|
||||||
) -> Iterator[FallbackLocation]:
|
) -> Iterator[FallbackLocation]:
|
||||||
for est in estimators:
|
for est in estimators:
|
||||||
loc = est(dt)
|
loc = list(est(dt))
|
||||||
if loc is None:
|
if not loc:
|
||||||
continue
|
continue
|
||||||
yield loc
|
yield from loc
|
||||||
|
|
||||||
|
|
||||||
def estimate_from(
|
def estimate_from(
|
||||||
dt: DateIsh,
|
dt: DateExact,
|
||||||
estimators: LocationEstimators,
|
estimators: LocationEstimators,
|
||||||
*,
|
*,
|
||||||
first_match: bool = False,
|
first_match: bool = False,
|
||||||
|
|
|
@ -5,12 +5,12 @@ Simple location provider, serving as a fallback when more detailed data isn't av
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, time, timezone
|
from datetime import datetime, time, timezone
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Sequence, Tuple, Union, cast, List
|
from typing import Sequence, Tuple, Union, cast, List, Iterator
|
||||||
|
|
||||||
from my.config import location as user_config
|
from my.config import location as user_config
|
||||||
|
|
||||||
from my.location.common import LatLon, DateIsh
|
from my.location.common import LatLon, DateIsh
|
||||||
from my.location.fallback.common import FallbackLocation
|
from my.location.fallback.common import FallbackLocation, DateExact
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Config(user_config):
|
class Config(user_config):
|
||||||
|
@ -85,24 +85,26 @@ def homes_cached() -> List[Tuple[datetime, LatLon]]:
|
||||||
return list(config._history)
|
return list(config._history)
|
||||||
|
|
||||||
|
|
||||||
def estimate_location(dt: Union[datetime, int, float]) -> FallbackLocation:
|
def estimate_location(dt: DateExact) -> Iterator[FallbackLocation]:
|
||||||
from my.location.fallback.common import _datetime_timestamp
|
from my.location.fallback.common import _datetime_timestamp
|
||||||
d: float = _datetime_timestamp(dt)
|
d: float = _datetime_timestamp(dt)
|
||||||
hist = list(reversed(homes_cached()))
|
hist = list(reversed(homes_cached()))
|
||||||
for pdt, (lat, lon) in hist:
|
for pdt, (lat, lon) in hist:
|
||||||
if d >= pdt.timestamp():
|
if d >= pdt.timestamp():
|
||||||
return FallbackLocation(
|
yield FallbackLocation(
|
||||||
lat=lat,
|
lat=lat,
|
||||||
lon=lon,
|
lon=lon,
|
||||||
accuracy=config.home_accuracy,
|
accuracy=config.home_accuracy,
|
||||||
dt=datetime.fromtimestamp(d, timezone.utc),
|
dt=datetime.fromtimestamp(d, timezone.utc),
|
||||||
datasource='via_home')
|
datasource='via_home')
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
# I guess the most reasonable is to fallback on the first location
|
# I guess the most reasonable is to fallback on the first location
|
||||||
lat, lon = hist[-1][1]
|
lat, lon = hist[-1][1]
|
||||||
return FallbackLocation(
|
yield FallbackLocation(
|
||||||
lat=lat,
|
lat=lat,
|
||||||
lon=lon,
|
lon=lon,
|
||||||
accuracy=config.home_accuracy,
|
accuracy=config.home_accuracy,
|
||||||
dt=datetime.fromtimestamp(d, timezone.utc),
|
dt=datetime.fromtimestamp(d, timezone.utc),
|
||||||
datasource='via_home')
|
datasource='via_home')
|
||||||
|
return
|
||||||
|
|
|
@ -199,7 +199,7 @@ def _get_day_tz(d: date) -> Optional[pytz.BaseTzInfo]:
|
||||||
|
|
||||||
# ok to cache, there are only a few home locations?
|
# ok to cache, there are only a few home locations?
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=None)
|
||||||
def _get_home_tz(loc) -> Optional[pytz.BaseTzInfo]:
|
def _get_home_tz(loc: LatLon) -> Optional[pytz.BaseTzInfo]:
|
||||||
(lat, lng) = loc
|
(lat, lng) = loc
|
||||||
finder = _timezone_finder(fast=False) # ok to use slow here for better precision
|
finder = _timezone_finder(fast=False) # ok to use slow here for better precision
|
||||||
zone = finder.timezone_at(lat=lat, lng=lng)
|
zone = finder.timezone_at(lat=lat, lng=lng)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue