From fc81fa9b83da502e4ece249f3a66382f6b669f51 Mon Sep 17 00:00:00 2001 From: XANTRONIX Industrial Date: Sat, 15 Feb 2025 17:10:42 -0500 Subject: [PATCH] Implement ArchiveProduct class --- lib/nexrad/archive.py | 110 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/lib/nexrad/archive.py b/lib/nexrad/archive.py index ea76d34..f9d0744 100644 --- a/lib/nexrad/archive.py +++ b/lib/nexrad/archive.py @@ -1,7 +1,12 @@ import os import re +import enum +import datetime -from nexrad.s3 import S3Bucket +from nexrad.db import Database +from nexrad.s3 import S3Bucket, S3_KEY_RE +from nexrad.coord import COORD_SYSTEM +from nexrad.radar import RADAR_RANGE class ArchiveDateError(Exception): def __init__(self, supplied: str, missing: str): @@ -9,7 +14,101 @@ class ArchiveDateError(Exception): self.missing = missing def __str__(self): - return "Date {self.supplied} was supplied, but required {self.missing} is missing" + return "Archive {self.supplied} was supplied, but required {self.missing} is missing" + +class ArchiveProductType(enum.Enum): + DEFAULT = 1 + V03 = 3 + +class ArchiveProduct(): + __slots__ = 'typeof', 'radar', 'timestamp', + + typeof: ArchiveProductType + radar: str + timestamp: datetime.datetime + + def __parts__(self): + return [ + "%04d" % (self.timestamp.year), + "%02d" % (self.timestamp.month), + "%02d" % (self.timestamp.day), + self.radar, + "%4s%04d%02d%02d_%02d%02d%02d" % ( + self.radar, + self.timestamp.year, self.timestamp.month, self.timestamp.day, + self.timestamp.hour, self.timestamp.minute, self.timestamp.second + ) + ] + + def __str__(self): + ret = '/'.join(self.__parts__()) + + if self.typeof == ArchiveProductType.V03: + ret += "_V03" + + ret += ".gz" + + return ret + + def __path__(self): + parts = self.__parts__() + ret = os.path.join(*parts) + + + if self.typeof == ArchiveProductType.V03: + ret += "_V03" + + ret += ".gz" + + return ret + + @staticmethod + def from_s3_key(key: str): + product = ArchiveProduct() + match = S3_KEY_RE.match(key) + + product.timestamp = datetime.datetime( + year = int(match[6]), + month = int(match[7]), + day = int(match[8]), + hour = int(match[9]), + minute = int(match[10]), + second = int(match[11]), + tzinfo = datetime.UTC + ) + + product.radar = match[4] + product.typeof = ArchiveProductType.V03 \ + if key[-7:] == '_V03.gz' else ArchiveProductType.DEFAULT + + return product + + def is_downloaded(self, path: str): + return os.path.isfile(os.path.join(path, self.__path__())) + + def is_reported(self, db: Database): + sql = """select count(( + select ST_Distance(MakeLine(report.coord_start, report.coord_end), + radar.coord, + true) as distance + from + nexrad_storm_report as report, + nexrad_radar as radar + where + distance <= :radius + and :timestamp between report.timestamp_start and report.timestamp_end + and radar.call = :call)) as num + """ + + st = db.execute(sql, { + 'radius': RADAR_RANGE, + 'timestamp': self.timestamp, + 'call': self.radar + }) + + result = st.fetchone() + + return result['num'] == 1 class Archive(): path: str @@ -94,3 +193,10 @@ class Archive(): parts.pop() parts.pop() + + def each_downloaded_product(self, + year: int=None, + month: int=None, + day: int=None): + for key in self.each_downloaded_key(year, month, day): + yield ArchiveProduct.from_s3_key(key)