From 31981b8f34b693d8406c1778ca4b3b9517c9fdc8 Mon Sep 17 00:00:00 2001 From: Sean Breckenridge Date: Fri, 27 Jan 2023 11:46:22 -0800 Subject: [PATCH] add fallback_locations for via_ip --- my/ip/common.py | 8 +++++++- my/location/fallback/common.py | 36 +++++++++++++++++++++++++++++++--- my/location/fallback/via_ip.py | 28 ++++++++++++++++++++------ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/my/ip/common.py b/my/ip/common.py index 82008e2..b4bfc8e 100644 --- a/my/ip/common.py +++ b/my/ip/common.py @@ -7,7 +7,7 @@ REQUIRES = ["git+https://github.com/seanbreckenridge/ipgeocache"] from my.core import __NOT_HPI_MODULE__ import ipaddress -from typing import NamedTuple, Iterator +from typing import NamedTuple, Iterator, Tuple from datetime import datetime import ipgeocache @@ -23,6 +23,12 @@ class IP(NamedTuple): def ipgeocache(self) -> Json: return ipgeocache.get(self.addr) + @property + def latlon(self) -> Tuple[float, float]: + loc: str = self.ipgeocache()["loc"] + lat, _, lon = loc.partition(",") + return float(lat), float(lon) + @property def tzname(self) -> str: tz: str = self.ipgeocache()["timezone"] diff --git a/my/location/fallback/common.py b/my/location/fallback/common.py index 42c342d..7c7bbfc 100644 --- a/my/location/fallback/common.py +++ b/my/location/fallback/common.py @@ -10,16 +10,16 @@ class FallbackLocation: lat: float lon: float dt: datetime - duration: int # time in seconds for how long this is valid + duration: float # time in seconds for how long this is valid accuracy: Optional[float] = None elevation: Optional[float] = None datasource: Optional[str] = None # which module provided this, useful for debugging def to_location(self, end: bool = False) -> Location: - """ + ''' by default the start date is used for the location If end is True, the start date + duration is used - """ + ''' dt: datetime = self.dt if end: dt += timedelta(self.duration) @@ -31,3 +31,33 @@ class FallbackLocation: elevation=self.elevation, datasource=self.datasource, ) + + @classmethod + def from_end_date( + cls, + lat: float, + lon: float, + dt: datetime, + end_dt: datetime, + accuracy: Optional[float] = None, + elevation: Optional[float] = None, + datasource: Optional[str] = None, + ) -> 'FallbackLocation': + ''' + Create FallbackLocation from a start date and an end date + ''' + if end_dt < dt: + raise ValueError('end_date must be after dt') + duration = (end_dt - dt).total_seconds() + return cls( + lat=lat, + lon=lon, + dt=dt, + duration=duration, + accuracy=accuracy, + elevation=elevation, + datasource=datasource, + ) + + +# TODO: create estimate location which uses other fallback_locations to estimate a location diff --git a/my/location/fallback/via_ip.py b/my/location/fallback/via_ip.py index a6d9a1a..0e8fb05 100644 --- a/my/location/fallback/via_ip.py +++ b/my/location/fallback/via_ip.py @@ -6,6 +6,8 @@ REQUIRES = ["git+https://github.com/seanbreckenridge/ipgeocache"] from my.core import dataclass, Stats from my.config import location +from my.core.warnings import medium +from datetime import datetime @dataclass @@ -13,26 +15,40 @@ class config(location.via_ip): # no real science to this, just a guess of ~15km accuracy for IP addresses accuracy: float = 15_000.0 + for_duration: float = 60 * 10 # default to being accurate for ~10 minutes + from typing import Iterator from ..common import Location +from .common import FallbackLocation from my.ip.all import ips -def locations() -> Iterator[Location]: +def fallback_locations() -> Iterator[FallbackLocation]: for ip in ips(): - loc: str = ip.ipgeocache()["loc"] - lat, _, lon = loc.partition(",") - yield Location( - lat=float(lat), - lon=float(lon), + lat, lon = ip.latlon + yield FallbackLocation( + lat=lat, + lon=lon, dt=ip.dt, accuracy=config.accuracy, + duration=config.for_duration, elevation=None, + datasource="ip", ) +# for compatibility with my.location.via_ip, this shouldnt be used by other modules +def locations() -> Iterator[Location]: + medium("via_ip.locations is deprecated, use via_ip.fallback_locations instead") + yield from map(FallbackLocation.to_location, fallback_locations()) + + +def estimate_location(dt: datetime) -> Location: + raise NotImplementedError("not implemented yet") + + def stats() -> Stats: from my.core import stat