avwx.base
Report parent classes
1""" 2Report parent classes 3""" 4 5# stdlib 6import asyncio as aio 7from abc import ABCMeta, abstractmethod 8from contextlib import suppress 9from datetime import date, datetime, timezone 10from typing import List, Optional, Type, TypeVar, Union 11 12# module 13from avwx.exceptions import BadStation 14from avwx.service import Service 15from avwx.station import Station 16from avwx.structs import ReportData, Units 17 18 19def find_station(report: str) -> Optional[Station]: 20 """Returns the first Station found in a report string""" 21 for item in report.split(): 22 with suppress(BadStation): 23 return Station.from_code(item.upper()) 24 return None 25 26 27T = TypeVar("T", bound="AVWXBase") # pylint: disable=invalid-name 28MT = TypeVar("MT", bound="ManagedReport") # pylint: disable=invalid-name 29 30 31class AVWXBase(metaclass=ABCMeta): 32 """Abstract base class for AVWX report types""" 33 34 #: UTC datetime object when the report was last updated 35 last_updated: Optional[datetime] = None 36 37 #: UTC date object when the report was issued 38 issued: Optional[date] = None 39 40 #: Root URL used to retrieve the current report 41 source: Optional[str] = None 42 43 #: The original report string 44 raw: Optional[str] = None 45 46 #: ReportData dataclass of parsed data values and units 47 data: Optional[ReportData] = None 48 49 #: Units inferred from the station location and report contents 50 units: Optional[Units] = None 51 52 def __repr__(self) -> str: 53 return f"<avwx.{self.__class__.__name__}>" 54 55 def _set_meta(self) -> None: 56 """Update timestamps after parsing""" 57 self.last_updated = datetime.now(tz=timezone.utc) 58 with suppress(AttributeError): 59 self.issued = self.data.time.dt.date() # type: ignore 60 61 @abstractmethod 62 def _post_parse(self) -> None: 63 pass 64 65 @classmethod 66 def from_report( 67 cls: Type[T], report: str, issued: Optional[date] = None 68 ) -> Optional[T]: 69 """Returns an updated report object based on an existing report""" 70 report = report.strip() 71 obj = cls() 72 obj.parse(report, issued=issued) 73 return obj 74 75 def parse(self, report: str, issued: Optional[date] = None) -> bool: 76 """Updates report data by parsing a given report 77 78 Can accept a report issue date if not a recent report string 79 """ 80 self.source = None 81 if not report or report == self.raw: 82 return False 83 self.raw = report 84 self.issued = issued 85 self._post_parse() 86 self._set_meta() 87 return True 88 89 @staticmethod 90 def sanitize(report: str) -> str: 91 """Sanitizes the report string 92 93 This has not been overridden and returns the raw report 94 """ 95 return report 96 97 98class ManagedReport(AVWXBase, metaclass=ABCMeta): 99 """Abstract base class for reports types associated with a single station""" 100 101 #: 4-character station code the report was initialized with 102 code: Optional[str] = None 103 104 #: Provide basic station info if given at init 105 station: Optional[Station] = None 106 107 #: Service object used to fetch the report string 108 service: Service 109 110 def __init__(self, code: str): 111 code = code.upper() 112 self.code = code 113 self.station = Station.from_code(code) 114 115 def __repr__(self) -> str: 116 return f"<avwx.{self.__class__.__name__} code={self.code}>" 117 118 @abstractmethod 119 async def _post_update(self) -> None: 120 pass 121 122 @classmethod 123 def from_report( 124 cls: Type[MT], report: str, issued: Optional[date] = None 125 ) -> Optional[MT]: 126 """Returns an updated report object based on an existing report""" 127 report = report.strip() 128 station = find_station(report) 129 if not station: 130 return None 131 obj = cls(station.lookup_code) 132 obj.parse(report, issued=issued) 133 return obj 134 135 async def _update( 136 self, report: Union[str, List[str]], issued: Optional[date], disable_post: bool 137 ) -> bool: 138 if not report or report == self.raw: 139 return False 140 self.raw = report # type: ignore 141 self.issued = issued 142 if not disable_post: 143 await self._post_update() 144 self._set_meta() 145 return True 146 147 def update(self, timeout: int = 10, disable_post: bool = False) -> bool: 148 """Updates report data by fetching and parsing the report 149 150 Returns True if a new report is available, else False 151 """ 152 report = self.service.fetch(self.code, timeout=timeout) # type: ignore 153 self.source = self.service.root 154 return aio.run(self._update(report, None, disable_post)) 155 156 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 157 """Async updates report data by fetching and parsing the report 158 159 Returns True if a new report is available, else False 160 """ 161 report = await self.service.async_fetch(self.code, timeout=timeout) # type: ignore 162 self.source = self.service.root 163 return await self._update(report, None, disable_post)
20def find_station(report: str) -> Optional[Station]: 21 """Returns the first Station found in a report string""" 22 for item in report.split(): 23 with suppress(BadStation): 24 return Station.from_code(item.upper()) 25 return None
Returns the first Station found in a report string
class
AVWXBase:
32class AVWXBase(metaclass=ABCMeta): 33 """Abstract base class for AVWX report types""" 34 35 #: UTC datetime object when the report was last updated 36 last_updated: Optional[datetime] = None 37 38 #: UTC date object when the report was issued 39 issued: Optional[date] = None 40 41 #: Root URL used to retrieve the current report 42 source: Optional[str] = None 43 44 #: The original report string 45 raw: Optional[str] = None 46 47 #: ReportData dataclass of parsed data values and units 48 data: Optional[ReportData] = None 49 50 #: Units inferred from the station location and report contents 51 units: Optional[Units] = None 52 53 def __repr__(self) -> str: 54 return f"<avwx.{self.__class__.__name__}>" 55 56 def _set_meta(self) -> None: 57 """Update timestamps after parsing""" 58 self.last_updated = datetime.now(tz=timezone.utc) 59 with suppress(AttributeError): 60 self.issued = self.data.time.dt.date() # type: ignore 61 62 @abstractmethod 63 def _post_parse(self) -> None: 64 pass 65 66 @classmethod 67 def from_report( 68 cls: Type[T], report: str, issued: Optional[date] = None 69 ) -> Optional[T]: 70 """Returns an updated report object based on an existing report""" 71 report = report.strip() 72 obj = cls() 73 obj.parse(report, issued=issued) 74 return obj 75 76 def parse(self, report: str, issued: Optional[date] = None) -> bool: 77 """Updates report data by parsing a given report 78 79 Can accept a report issue date if not a recent report string 80 """ 81 self.source = None 82 if not report or report == self.raw: 83 return False 84 self.raw = report 85 self.issued = issued 86 self._post_parse() 87 self._set_meta() 88 return True 89 90 @staticmethod 91 def sanitize(report: str) -> str: 92 """Sanitizes the report string 93 94 This has not been overridden and returns the raw report 95 """ 96 return report
Abstract base class for AVWX report types
@classmethod
def
from_report( cls: Type[~T], report: str, issued: Optional[datetime.date] = None) -> Optional[~T]:
66 @classmethod 67 def from_report( 68 cls: Type[T], report: str, issued: Optional[date] = None 69 ) -> Optional[T]: 70 """Returns an updated report object based on an existing report""" 71 report = report.strip() 72 obj = cls() 73 obj.parse(report, issued=issued) 74 return obj
Returns an updated report object based on an existing report
def
parse(self, report: str, issued: Optional[datetime.date] = None) -> bool:
76 def parse(self, report: str, issued: Optional[date] = None) -> bool: 77 """Updates report data by parsing a given report 78 79 Can accept a report issue date if not a recent report string 80 """ 81 self.source = None 82 if not report or report == self.raw: 83 return False 84 self.raw = report 85 self.issued = issued 86 self._post_parse() 87 self._set_meta() 88 return True
Updates report data by parsing a given report
Can accept a report issue date if not a recent report string
@staticmethod
def
sanitize(report: str) -> str:
90 @staticmethod 91 def sanitize(report: str) -> str: 92 """Sanitizes the report string 93 94 This has not been overridden and returns the raw report 95 """ 96 return report
Sanitizes the report string
This has not been overridden and returns the raw report
99class ManagedReport(AVWXBase, metaclass=ABCMeta): 100 """Abstract base class for reports types associated with a single station""" 101 102 #: 4-character station code the report was initialized with 103 code: Optional[str] = None 104 105 #: Provide basic station info if given at init 106 station: Optional[Station] = None 107 108 #: Service object used to fetch the report string 109 service: Service 110 111 def __init__(self, code: str): 112 code = code.upper() 113 self.code = code 114 self.station = Station.from_code(code) 115 116 def __repr__(self) -> str: 117 return f"<avwx.{self.__class__.__name__} code={self.code}>" 118 119 @abstractmethod 120 async def _post_update(self) -> None: 121 pass 122 123 @classmethod 124 def from_report( 125 cls: Type[MT], report: str, issued: Optional[date] = None 126 ) -> Optional[MT]: 127 """Returns an updated report object based on an existing report""" 128 report = report.strip() 129 station = find_station(report) 130 if not station: 131 return None 132 obj = cls(station.lookup_code) 133 obj.parse(report, issued=issued) 134 return obj 135 136 async def _update( 137 self, report: Union[str, List[str]], issued: Optional[date], disable_post: bool 138 ) -> bool: 139 if not report or report == self.raw: 140 return False 141 self.raw = report # type: ignore 142 self.issued = issued 143 if not disable_post: 144 await self._post_update() 145 self._set_meta() 146 return True 147 148 def update(self, timeout: int = 10, disable_post: bool = False) -> bool: 149 """Updates report data by fetching and parsing the report 150 151 Returns True if a new report is available, else False 152 """ 153 report = self.service.fetch(self.code, timeout=timeout) # type: ignore 154 self.source = self.service.root 155 return aio.run(self._update(report, None, disable_post)) 156 157 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 158 """Async updates report data by fetching and parsing the report 159 160 Returns True if a new report is available, else False 161 """ 162 report = await self.service.async_fetch(self.code, timeout=timeout) # type: ignore 163 self.source = self.service.root 164 return await self._update(report, None, disable_post)
Abstract base class for reports types associated with a single station
service: avwx.service.base.Service
@classmethod
def
from_report( cls: Type[~MT], report: str, issued: Optional[datetime.date] = None) -> Optional[~MT]:
123 @classmethod 124 def from_report( 125 cls: Type[MT], report: str, issued: Optional[date] = None 126 ) -> Optional[MT]: 127 """Returns an updated report object based on an existing report""" 128 report = report.strip() 129 station = find_station(report) 130 if not station: 131 return None 132 obj = cls(station.lookup_code) 133 obj.parse(report, issued=issued) 134 return obj
Returns an updated report object based on an existing report
def
update(self, timeout: int = 10, disable_post: bool = False) -> bool:
148 def update(self, timeout: int = 10, disable_post: bool = False) -> bool: 149 """Updates report data by fetching and parsing the report 150 151 Returns True if a new report is available, else False 152 """ 153 report = self.service.fetch(self.code, timeout=timeout) # type: ignore 154 self.source = self.service.root 155 return aio.run(self._update(report, None, disable_post))
Updates report data by fetching and parsing the report
Returns True if a new report is available, else False
async def
async_update(self, timeout: int = 10, disable_post: bool = False) -> bool:
157 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 158 """Async updates report data by fetching and parsing the report 159 160 Returns True if a new report is available, else False 161 """ 162 report = await self.service.async_fetch(self.code, timeout=timeout) # type: ignore 163 self.source = self.service.root 164 return await self._update(report, None, disable_post)
Async updates report data by fetching and parsing the report
Returns True if a new report is available, else False