avwx.structs

Contains dataclasses to hold report data.

  1"""Contains dataclasses to hold report data."""
  2
  3# stdlib
  4from __future__ import annotations
  5
  6from dataclasses import dataclass, field
  7from typing import TYPE_CHECKING, TypeAlias
  8
  9if TYPE_CHECKING:
 10    from datetime import datetime
 11
 12# module
 13from avwx.exceptions import MissingExtraModule
 14from avwx.load_utils import LazyLoad
 15from avwx.static.core import IN_UNITS, NA_UNITS
 16
 17try:
 18    from typing import Self
 19except ImportError:
 20    from typing_extensions import Self
 21try:
 22    from shapely.geometry import Point, Polygon
 23except ModuleNotFoundError:
 24    Point, Polygon = TypeAlias, TypeAlias  # type: ignore
 25
 26AIRCRAFT = LazyLoad("aircraft")
 27
 28
 29@dataclass
 30class Aircraft:
 31    code: str
 32    type: str
 33
 34    @classmethod
 35    def from_icao(cls, code: str) -> Self:
 36        """Load an Aircraft from an ICAO aircraft code."""
 37        try:
 38            return cls(code=code, type=AIRCRAFT[code])
 39        except KeyError as key_error:
 40            msg = f"{code} is not a known aircraft code"
 41            raise ValueError(msg) from key_error
 42
 43
 44@dataclass
 45class Units:
 46    accumulation: str
 47    altimeter: str
 48    altitude: str
 49    temperature: str
 50    visibility: str
 51    wind_speed: str
 52
 53    @classmethod
 54    def international(cls) -> Self:
 55        """Create default internation units."""
 56        return cls(**IN_UNITS)
 57
 58    @classmethod
 59    def north_american(cls) -> Self:
 60        """Create default North American units."""
 61        return cls(**NA_UNITS)
 62
 63
 64@dataclass
 65class Number:
 66    repr: str
 67    value: int | float | None
 68    spoken: str
 69
 70
 71@dataclass
 72class Fraction(Number):
 73    numerator: int
 74    denominator: int
 75    normalized: str
 76
 77
 78@dataclass
 79class Altitude(Number):
 80    """A vertical limit that knows whether it is a flight level.
 81
 82    ``value`` is in hundreds of feet when ``flight_level`` is set, and in
 83    ``Units.altitude`` otherwise.
 84    """
 85
 86    flight_level: bool = False
 87
 88
 89@dataclass
 90class Timestamp:
 91    repr: str
 92    dt: datetime | None
 93
 94
 95@dataclass
 96class Code:
 97    repr: str
 98    value: str
 99
100    @classmethod
101    def from_dict(
102        cls,
103        key: str | None,
104        codes: dict[str, str],
105        *,
106        default: str | None = None,
107        error: bool = True,
108    ) -> Self | None:
109        """Load a code from a known key and value dict."""
110        value: str | None
111        if not key:
112            return None
113        try:
114            value = codes[key]
115        except KeyError as exc:
116            if error:
117                msg = f"No code found for {key}"
118                raise KeyError(msg) from exc
119            value = default
120        return cls(key, value or "Unknown")
121
122    @classmethod
123    def from_list(
124        cls,
125        keys: str | None,
126        codes: dict[str, str],
127        *,
128        exclusive: bool = False,
129    ) -> list[Self]:
130        """Load a list of codes from string characters."""
131        if not keys:
132            return []
133        out = []
134        for key in keys.strip():
135            if value := codes.get(key):
136                out.append(cls(key, value))
137            elif exclusive:
138                return []
139        return out
140
141
142@dataclass
143class Coord:
144    lat: float
145    lon: float
146    repr: str | None = None
147
148    @property
149    def pair(self) -> tuple[float, float]:
150        return self.lat, self.lon
151
152    @property
153    def point(self) -> Point:
154        if Point is None:
155            extra = "shape"
156            raise MissingExtraModule(extra)
157        return Point(self.lat, self.lon)
158
159    @staticmethod
160    def to_dms(value: float) -> tuple[int, int, int]:
161        """Convert a coordinate decimal value to degree, minute, second."""
162        minute, second = divmod(abs(value) * 3600, 60)
163        degree, minute = divmod(minute, 60)
164        if value < 0:
165            degree *= -1
166        return int(degree), int(minute), int(second)
167
168
169@dataclass
170class Cloud:
171    repr: str
172    type: str | None = None
173    base: int | None = None
174    top: int | None = None
175    modifier: str | None = None
176
177
178@dataclass
179class RunwayVisibility:
180    repr: str
181    runway: str
182    visibility: Number | None
183    variable_visibility: list[Number]
184    trend: Code | None
185
186
187@dataclass
188class Location:
189    repr: str
190    station: str | None
191    direction: Number | None
192    distance: Number | None
193
194
195@dataclass
196class PressureTendency:
197    repr: str
198    tendency: str
199    change: float
200
201
202@dataclass
203class FiveDigitCodes:
204    maximum_temperature_6: Number | None = None  # 1
205    minimum_temperature_6: Number | None = None  # 2
206    pressure_tendency: PressureTendency | None = None  # 5
207    precip_36_hours: Number | None = None  # 6
208    precip_24_hours: Number | None = None  # 7
209    sunshine_minutes: Number | None = None  # 9
210
211
212@dataclass
213class RemarksData(FiveDigitCodes):
214    codes: list[Code] = field(default_factory=list)
215    dewpoint_decimal: Number | None = None
216    maximum_temperature_24: Number | None = None
217    minimum_temperature_24: Number | None = None
218    precip_hourly: Number | None = None
219    sea_level_pressure: Number | None = None
220    snow_depth: Number | None = None
221    temperature_decimal: Number | None = None
222
223
224@dataclass
225class ReportData:
226    raw: str
227    sanitized: str
228    station: str | None
229    time: Timestamp | None
230    remarks: str | None
231
232
233@dataclass
234class SharedData:
235    altimeter: Number | None
236    clouds: list[Cloud]
237    flight_rules: str
238    other: list[str]
239    visibility: Number | None
240    wind_direction: Number | None
241    wind_gust: Number | None
242    wind_speed: Number | None
243    wx_codes: list[Code]
244
245
246@dataclass
247class MetarData(ReportData, SharedData):
248    dewpoint: Number | None
249    relative_humidity: float | None
250    remarks_info: RemarksData | None
251    runway_visibility: list[RunwayVisibility]
252    temperature: Number | None
253    wind_variable_direction: list[Number]
254    density_altitude: int | None = None
255    pressure_altitude: int | None = None
256
257
258@dataclass
259class TafLineData(SharedData):
260    end_time: Timestamp | None
261    icing: list[str]
262    probability: Number | None
263    raw: str
264    sanitized: str
265    start_time: Timestamp | None
266    transition_start: Timestamp | None
267    turbulence: list[str]
268    type: str
269    wind_shear: str | None
270    wind_variable_direction: list[Number] | None
271
272
273@dataclass
274class TafData(ReportData):
275    forecast: list[TafLineData]
276    start_time: Timestamp | None
277    end_time: Timestamp | None
278    is_amended: bool
279    is_correction: bool
280    max_temp: str | None = None
281    min_temp: str | None = None
282    alts: list[str] | None = None
283    temps: list[str] | None = None
284    remarks_info: RemarksData | None = None
285
286
287@dataclass
288class ReportTrans:
289    altimeter: str
290    clouds: str
291    wx_codes: str
292    visibility: str
293
294
295@dataclass
296class MetarTrans(ReportTrans):
297    dewpoint: str
298    remarks: dict
299    temperature: str
300    wind: str
301
302
303@dataclass
304class TafLineTrans(ReportTrans):
305    icing: str
306    turbulence: str
307    wind: str
308    wind_shear: str
309
310
311@dataclass
312class TafTrans:
313    forecast: list[TafLineTrans]
314    max_temp: str
315    min_temp: str
316    remarks: dict
317
318
319@dataclass
320class Turbulence:
321    severity: str
322    floor: Number | None = None
323    ceiling: Number | None = None
324
325
326@dataclass
327class Icing(Turbulence):
328    type: str | None = None
329
330
331@dataclass
332class PirepData(ReportData):
333    aircraft: Aircraft | str | None = None
334    altitude: Number | str | None = None
335    clouds: list[Cloud] | None = None
336    flight_visibility: Number | None = None
337    icing: Icing | None = None
338    location: Location | None = None
339    other: list[str] | None = None
340    temperature: Number | None = None
341    turbulence: Turbulence | None = None
342    type: str | None = None
343    wx_codes: list[Code] | None = None
344
345
346@dataclass
347class AirepData(ReportData):
348    pass
349
350
351@dataclass
352class Bulletin:
353    repr: str
354    type: Code
355    country: str
356    number: int
357
358
359@dataclass
360class Movement:
361    repr: str
362    direction: Number | None
363    speed: Number | None
364
365
366MIN_POLY_SIZE = 2
367
368
369@dataclass
370class AirSigObservation:
371    type: Code | None
372    start_time: Timestamp | None
373    end_time: Timestamp | None
374    position: Coord | None
375    floor: Number | None
376    ceiling: Number | None
377    coords: list[Coord]
378    bounds: list[str]
379    movement: Movement | None
380    intensity: Code | None
381    other: list[str]
382
383    @property
384    def poly(self) -> Polygon | None:
385        if Polygon is None:
386            extra = "shape"
387            raise MissingExtraModule(extra)
388        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
389
390
391@dataclass
392class AirSigmetData(ReportData):
393    bulletin: Bulletin
394    issuer: str
395    correction: str | None
396    area: str
397    type: str
398    start_time: Timestamp | None
399    end_time: Timestamp | None
400    body: str
401    region: str
402    observation: AirSigObservation | None
403    forecast: AirSigObservation | None
404
405
406@dataclass
407class Qualifiers:
408    repr: str
409    fir: str
410    subject: Code | None
411    condition: Code | None
412    traffic: Code | None
413    purpose: list[Code]
414    scope: list[Code]
415    lower: Altitude | None
416    upper: Altitude | None
417    coord: Coord | None
418    radius: Number | None
419
420
421@dataclass
422class NotamData(ReportData):
423    number: str | None
424    replaces: str | None
425    type: Code | None
426    qualifiers: Qualifiers | None
427    start_time: Timestamp | Code | None
428    end_time: Timestamp | Code | None
429    schedule: str | None
430    body: str
431    lower: Altitude | None
432    upper: Altitude | None
433
434
435@dataclass
436class GfsPeriod:
437    time: Timestamp
438    temperature: Number
439    dewpoint: Number
440    cloud: Code
441    temperature_minmax: Number | None = None
442    precip_chance_12: Number | None = None
443    precip_amount_12: Code | None = None
444    thunderstorm_12: Number | None = None
445    severe_storm_12: Number | None = None
446    freezing_precip: Number | None = None
447    precip_type: Code | None = None
448    snow: Number | None = None
449
450
451@dataclass
452class MavPeriod(GfsPeriod):
453    wind_direction: Number | None = None
454    wind_speed: Number | None = None
455    precip_chance_6: Number | None = None
456    precip_amount_6: Code | None = None
457    thunderstorm_6: Number | None = None
458    severe_storm_6: Number | None = None
459    ceiling: Code | None = None
460    visibility: Code | None = None
461    vis_obstruction: Code | None = None
462
463
464@dataclass
465class MexPeriod(GfsPeriod):
466    precip_chance_24: Number | None = None
467    precip_amount_24: Code | None = None
468    thunderstorm_24: Number | None = None
469    severe_storm_24: Number | None = None
470    rain_snow_mix: Number | None = None
471    snow_amount_24: Code | None = None
472
473
474@dataclass
475class MavData(ReportData):
476    forecast: list[MavPeriod]
477
478
479@dataclass
480class MexData(ReportData):
481    forecast: list[MexPeriod]
482
483
484@dataclass
485class NbmUnits(Units):
486    duration: str
487    solar_radiation: str
488    wave_height: str
489
490
491@dataclass
492class NbmPeriod:
493    time: Timestamp
494    temperature: Number | None = None
495    dewpoint: Number | None = None
496    sky_cover: Number | None = None
497    wind_direction: Number | None = None
498    wind_speed: Number | None = None
499    wind_gust: Number | None = None
500    snow_level: Number | None = None
501    precip_duration: Number | None = None
502    freezing_precip: Number | None = None
503    snow: Number | None = None
504    sleet: Number | None = None
505    rain: Number | None = None
506    solar_radiation: Number | None = None
507    wave_height: Number | None = None
508
509
510@dataclass
511class NbhsShared(NbmPeriod):
512    ceiling: Number | None = None
513    visibility: Number | None = None
514    cloud_base: Number | None = None
515    mixing_height: Number | None = None
516    transport_wind_direction: Number | None = None
517    transport_wind_speed: Number | None = None
518    haines: list[Number] | None = None
519
520
521@dataclass
522class NbhPeriod(NbhsShared):
523    precip_chance_1: Number | None = None
524    precip_chance_6: Number | None = None
525    precip_amount_1: Number | None = None
526    thunderstorm_1: Number | None = None
527    snow_amount_1: Number | None = None
528    icing_amount_1: Number | None = None
529
530
531@dataclass
532class NbsPeriod(NbhsShared):
533    temperature_minmax: Number | None = None
534    precip_chance_6: Number | None = None
535    precip_chance_12: Number | None = None
536    precip_amount_6: Number | None = None
537    precip_amount_12: Number | None = None
538    precip_duration: Number | None = None
539    thunderstorm_3: Number | None = None
540    thunderstorm_6: Number | None = None
541    thunderstorm_12: Number | None = None
542    snow_amount_6: Number | None = None
543    icing_amount_6: Number | None = None
544
545
546@dataclass
547class NbePeriod(NbmPeriod):
548    temperature_minmax: Number | None = None
549    precip_chance_12: Number | None = None
550    precip_amount_12: Number | None = None
551    precip_amount_24: Number | None = None
552    thunderstorm_12: Number | None = None
553    snow_amount_12: Number | None = None
554    snow_amount_24: Number | None = None
555    icing_amount_12: Number | None = None
556
557
558@dataclass
559class NbxPeriod(NbmPeriod):
560    precip_chance_12: Number | None = None
561    precip_amount_12: Number | None = None
562    precip_amount_24: Number | None = None
563    snow_amount_12: Number | None = None
564    icing_amount_12: Number | None = None
565
566
567@dataclass
568class NbhData(ReportData):
569    forecast: list[NbhPeriod]
570
571
572@dataclass
573class NbsData(ReportData):
574    forecast: list[NbsPeriod]
575
576
577@dataclass
578class NbeData(ReportData):
579    forecast: list[NbePeriod]
580
581
582@dataclass
583class NbxData(ReportData):
584    forecast: list[NbxPeriod]
585
586
587# @dataclass
588# class GfsPeriodTrans:
589#     temperature: str
590#     dewpoint: str
591#     cloud: str
592#     precip_chance_12: str
593#     precip_amount_12: str
594#     thunderstorm_12: str
595#     severe_storm_12: str
596#     freezing_precip: str
597#     precip_type: str
598#     snow: str
599
600
601# @dataclass
602# class MavPeriodTrans(GfsPeriodTrans):
603#     wind_direction: str
604#     wind_speed: str
605#     precip_chance_6: str
606#     precip_amount_6: str
607#     thunderstorm_6: str
608#     severe_storm_6: str
609#     ceiling: str
610#     visibility: str
611#     vis_obstruction: str
612
613
614# @dataclass
615# class MexPeriodTrans(GfsPeriodTrans):
616#     precip_chance_24: str
617#     precip_amount_24: str
618#     thunderstorm_24: str
619#     severe_storm_24: str
620#     rain_snow_mix: str
621#     snow_amount_24: str
622
623
624@dataclass
625class Sanitization:
626    """Tracks changes made during the sanitization process."""
627
628    removed: list[str] = field(default_factory=list)
629    replaced: dict[str, str] = field(default_factory=dict)
630    duplicates_found: bool = False
631    extra_spaces_found: bool = False
632    extra_spaces_needed: bool = False
633
634    @property
635    def errors_found(self) -> bool:
636        return bool(
637            self.removed
638            or self.replaced
639            or self.duplicates_found
640            or self.extra_spaces_found
641            or self.extra_spaces_needed
642        )
643
644    def log(self, item: str, replacement: str | None = None) -> None:
645        """Log a changed item. Calling without a replacement assumes removal."""
646        item = item.strip()
647        if not item:
648            return
649        if replacement is None:
650            self.removed.insert(0, item)
651            return
652        replacement = replacement.strip()
653        if not replacement:
654            self.removed.insert(0, item)
655        elif item != replacement:
656            self.replaced[item] = replacement
657
658    def log_list(self, before: list[str], after: list[str]) -> None:
659        """Log list differences. Assumes that list length and order haven't changed."""
660        for item, replacement in zip(before, after, strict=True):
661            if item != replacement:
662                self.log(item, replacement)
AIRCRAFT = <avwx.load_utils.LazyLoad object>
@dataclass
class Aircraft:
30@dataclass
31class Aircraft:
32    code: str
33    type: str
34
35    @classmethod
36    def from_icao(cls, code: str) -> Self:
37        """Load an Aircraft from an ICAO aircraft code."""
38        try:
39            return cls(code=code, type=AIRCRAFT[code])
40        except KeyError as key_error:
41            msg = f"{code} is not a known aircraft code"
42            raise ValueError(msg) from key_error
Aircraft(code: str, type: str)
code: str
type: str
@classmethod
def from_icao(cls, code: str) -> Self:
35    @classmethod
36    def from_icao(cls, code: str) -> Self:
37        """Load an Aircraft from an ICAO aircraft code."""
38        try:
39            return cls(code=code, type=AIRCRAFT[code])
40        except KeyError as key_error:
41            msg = f"{code} is not a known aircraft code"
42            raise ValueError(msg) from key_error

Load an Aircraft from an ICAO aircraft code.

@dataclass
class Units:
45@dataclass
46class Units:
47    accumulation: str
48    altimeter: str
49    altitude: str
50    temperature: str
51    visibility: str
52    wind_speed: str
53
54    @classmethod
55    def international(cls) -> Self:
56        """Create default internation units."""
57        return cls(**IN_UNITS)
58
59    @classmethod
60    def north_american(cls) -> Self:
61        """Create default North American units."""
62        return cls(**NA_UNITS)
Units( accumulation: str, altimeter: str, altitude: str, temperature: str, visibility: str, wind_speed: str)
accumulation: str
altimeter: str
altitude: str
temperature: str
visibility: str
wind_speed: str
@classmethod
def international(cls) -> Self:
54    @classmethod
55    def international(cls) -> Self:
56        """Create default internation units."""
57        return cls(**IN_UNITS)

Create default internation units.

@classmethod
def north_american(cls) -> Self:
59    @classmethod
60    def north_american(cls) -> Self:
61        """Create default North American units."""
62        return cls(**NA_UNITS)

Create default North American units.

@dataclass
class Number:
65@dataclass
66class Number:
67    repr: str
68    value: int | float | None
69    spoken: str
Number(repr: str, value: int | float | None, spoken: str)
repr: str
value: int | float | None
spoken: str
@dataclass
class Fraction(Number):
72@dataclass
73class Fraction(Number):
74    numerator: int
75    denominator: int
76    normalized: str
Fraction( repr: str, value: int | float | None, spoken: str, numerator: int, denominator: int, normalized: str)
numerator: int
denominator: int
normalized: str
Inherited Members
Number
repr
value
spoken
@dataclass
class Altitude(Number):
79@dataclass
80class Altitude(Number):
81    """A vertical limit that knows whether it is a flight level.
82
83    ``value`` is in hundreds of feet when ``flight_level`` is set, and in
84    ``Units.altitude`` otherwise.
85    """
86
87    flight_level: bool = False

A vertical limit that knows whether it is a flight level.

value is in hundreds of feet when flight_level is set, and in Units.altitude otherwise.

Altitude( repr: str, value: int | float | None, spoken: str, flight_level: bool = False)
flight_level: bool = False
Inherited Members
Number
repr
value
spoken
@dataclass
class Timestamp:
90@dataclass
91class Timestamp:
92    repr: str
93    dt: datetime | None
Timestamp(repr: str, dt: datetime.datetime | None)
repr: str
dt: datetime.datetime | None
@dataclass
class Code:
 96@dataclass
 97class Code:
 98    repr: str
 99    value: str
100
101    @classmethod
102    def from_dict(
103        cls,
104        key: str | None,
105        codes: dict[str, str],
106        *,
107        default: str | None = None,
108        error: bool = True,
109    ) -> Self | None:
110        """Load a code from a known key and value dict."""
111        value: str | None
112        if not key:
113            return None
114        try:
115            value = codes[key]
116        except KeyError as exc:
117            if error:
118                msg = f"No code found for {key}"
119                raise KeyError(msg) from exc
120            value = default
121        return cls(key, value or "Unknown")
122
123    @classmethod
124    def from_list(
125        cls,
126        keys: str | None,
127        codes: dict[str, str],
128        *,
129        exclusive: bool = False,
130    ) -> list[Self]:
131        """Load a list of codes from string characters."""
132        if not keys:
133            return []
134        out = []
135        for key in keys.strip():
136            if value := codes.get(key):
137                out.append(cls(key, value))
138            elif exclusive:
139                return []
140        return out
Code(repr: str, value: str)
repr: str
value: str
@classmethod
def from_dict( cls, key: str | None, codes: dict[str, str], *, default: str | None = None, error: bool = True) -> Optional[Self]:
101    @classmethod
102    def from_dict(
103        cls,
104        key: str | None,
105        codes: dict[str, str],
106        *,
107        default: str | None = None,
108        error: bool = True,
109    ) -> Self | None:
110        """Load a code from a known key and value dict."""
111        value: str | None
112        if not key:
113            return None
114        try:
115            value = codes[key]
116        except KeyError as exc:
117            if error:
118                msg = f"No code found for {key}"
119                raise KeyError(msg) from exc
120            value = default
121        return cls(key, value or "Unknown")

Load a code from a known key and value dict.

@classmethod
def from_list( cls, keys: str | None, codes: dict[str, str], *, exclusive: bool = False) -> list[typing.Self]:
123    @classmethod
124    def from_list(
125        cls,
126        keys: str | None,
127        codes: dict[str, str],
128        *,
129        exclusive: bool = False,
130    ) -> list[Self]:
131        """Load a list of codes from string characters."""
132        if not keys:
133            return []
134        out = []
135        for key in keys.strip():
136            if value := codes.get(key):
137                out.append(cls(key, value))
138            elif exclusive:
139                return []
140        return out

Load a list of codes from string characters.

@dataclass
class Coord:
143@dataclass
144class Coord:
145    lat: float
146    lon: float
147    repr: str | None = None
148
149    @property
150    def pair(self) -> tuple[float, float]:
151        return self.lat, self.lon
152
153    @property
154    def point(self) -> Point:
155        if Point is None:
156            extra = "shape"
157            raise MissingExtraModule(extra)
158        return Point(self.lat, self.lon)
159
160    @staticmethod
161    def to_dms(value: float) -> tuple[int, int, int]:
162        """Convert a coordinate decimal value to degree, minute, second."""
163        minute, second = divmod(abs(value) * 3600, 60)
164        degree, minute = divmod(minute, 60)
165        if value < 0:
166            degree *= -1
167        return int(degree), int(minute), int(second)
Coord(lat: float, lon: float, repr: str | None = None)
lat: float
lon: float
repr: str | None = None
pair: tuple[float, float]
149    @property
150    def pair(self) -> tuple[float, float]:
151        return self.lat, self.lon
point: shapely.geometry.point.Point
153    @property
154    def point(self) -> Point:
155        if Point is None:
156            extra = "shape"
157            raise MissingExtraModule(extra)
158        return Point(self.lat, self.lon)
@staticmethod
def to_dms(value: float) -> tuple[int, int, int]:
160    @staticmethod
161    def to_dms(value: float) -> tuple[int, int, int]:
162        """Convert a coordinate decimal value to degree, minute, second."""
163        minute, second = divmod(abs(value) * 3600, 60)
164        degree, minute = divmod(minute, 60)
165        if value < 0:
166            degree *= -1
167        return int(degree), int(minute), int(second)

Convert a coordinate decimal value to degree, minute, second.

@dataclass
class Cloud:
170@dataclass
171class Cloud:
172    repr: str
173    type: str | None = None
174    base: int | None = None
175    top: int | None = None
176    modifier: str | None = None
Cloud( repr: str, type: str | None = None, base: int | None = None, top: int | None = None, modifier: str | None = None)
repr: str
type: str | None = None
base: int | None = None
top: int | None = None
modifier: str | None = None
@dataclass
class RunwayVisibility:
179@dataclass
180class RunwayVisibility:
181    repr: str
182    runway: str
183    visibility: Number | None
184    variable_visibility: list[Number]
185    trend: Code | None
RunwayVisibility( repr: str, runway: str, visibility: Number | None, variable_visibility: list[Number], trend: Code | None)
repr: str
runway: str
visibility: Number | None
variable_visibility: list[Number]
trend: Code | None
@dataclass
class Location:
188@dataclass
189class Location:
190    repr: str
191    station: str | None
192    direction: Number | None
193    distance: Number | None
Location( repr: str, station: str | None, direction: Number | None, distance: Number | None)
repr: str
station: str | None
direction: Number | None
distance: Number | None
@dataclass
class PressureTendency:
196@dataclass
197class PressureTendency:
198    repr: str
199    tendency: str
200    change: float
PressureTendency(repr: str, tendency: str, change: float)
repr: str
tendency: str
change: float
@dataclass
class FiveDigitCodes:
203@dataclass
204class FiveDigitCodes:
205    maximum_temperature_6: Number | None = None  # 1
206    minimum_temperature_6: Number | None = None  # 2
207    pressure_tendency: PressureTendency | None = None  # 5
208    precip_36_hours: Number | None = None  # 6
209    precip_24_hours: Number | None = None  # 7
210    sunshine_minutes: Number | None = None  # 9
FiveDigitCodes( maximum_temperature_6: Number | None = None, minimum_temperature_6: Number | None = None, pressure_tendency: PressureTendency | None = None, precip_36_hours: Number | None = None, precip_24_hours: Number | None = None, sunshine_minutes: Number | None = None)
maximum_temperature_6: Number | None = None
minimum_temperature_6: Number | None = None
pressure_tendency: PressureTendency | None = None
precip_36_hours: Number | None = None
precip_24_hours: Number | None = None
sunshine_minutes: Number | None = None
@dataclass
class RemarksData(FiveDigitCodes):
213@dataclass
214class RemarksData(FiveDigitCodes):
215    codes: list[Code] = field(default_factory=list)
216    dewpoint_decimal: Number | None = None
217    maximum_temperature_24: Number | None = None
218    minimum_temperature_24: Number | None = None
219    precip_hourly: Number | None = None
220    sea_level_pressure: Number | None = None
221    snow_depth: Number | None = None
222    temperature_decimal: Number | None = None
RemarksData( maximum_temperature_6: Number | None = None, minimum_temperature_6: Number | None = None, pressure_tendency: PressureTendency | None = None, precip_36_hours: Number | None = None, precip_24_hours: Number | None = None, sunshine_minutes: Number | None = None, codes: list[Code] = <factory>, dewpoint_decimal: Number | None = None, maximum_temperature_24: Number | None = None, minimum_temperature_24: Number | None = None, precip_hourly: Number | None = None, sea_level_pressure: Number | None = None, snow_depth: Number | None = None, temperature_decimal: Number | None = None)
codes: list[Code]
dewpoint_decimal: Number | None = None
maximum_temperature_24: Number | None = None
minimum_temperature_24: Number | None = None
precip_hourly: Number | None = None
sea_level_pressure: Number | None = None
snow_depth: Number | None = None
temperature_decimal: Number | None = None
@dataclass
class ReportData:
225@dataclass
226class ReportData:
227    raw: str
228    sanitized: str
229    station: str | None
230    time: Timestamp | None
231    remarks: str | None
ReportData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None)
raw: str
sanitized: str
station: str | None
time: Timestamp | None
remarks: str | None
@dataclass
class SharedData:
234@dataclass
235class SharedData:
236    altimeter: Number | None
237    clouds: list[Cloud]
238    flight_rules: str
239    other: list[str]
240    visibility: Number | None
241    wind_direction: Number | None
242    wind_gust: Number | None
243    wind_speed: Number | None
244    wx_codes: list[Code]
SharedData( altimeter: Number | None, clouds: list[Cloud], flight_rules: str, other: list[str], visibility: Number | None, wind_direction: Number | None, wind_gust: Number | None, wind_speed: Number | None, wx_codes: list[Code])
altimeter: Number | None
clouds: list[Cloud]
flight_rules: str
other: list[str]
visibility: Number | None
wind_direction: Number | None
wind_gust: Number | None
wind_speed: Number | None
wx_codes: list[Code]
@dataclass
class MetarData(ReportData, SharedData):
247@dataclass
248class MetarData(ReportData, SharedData):
249    dewpoint: Number | None
250    relative_humidity: float | None
251    remarks_info: RemarksData | None
252    runway_visibility: list[RunwayVisibility]
253    temperature: Number | None
254    wind_variable_direction: list[Number]
255    density_altitude: int | None = None
256    pressure_altitude: int | None = None
MetarData( altimeter: Number | None, clouds: list[Cloud], flight_rules: str, other: list[str], visibility: Number | None, wind_direction: Number | None, wind_gust: Number | None, wind_speed: Number | None, wx_codes: list[Code], raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, dewpoint: Number | None, relative_humidity: float | None, remarks_info: RemarksData | None, runway_visibility: list[RunwayVisibility], temperature: Number | None, wind_variable_direction: list[Number], density_altitude: int | None = None, pressure_altitude: int | None = None)
dewpoint: Number | None
relative_humidity: float | None
remarks_info: RemarksData | None
runway_visibility: list[RunwayVisibility]
temperature: Number | None
wind_variable_direction: list[Number]
density_altitude: int | None = None
pressure_altitude: int | None = None
@dataclass
class TafLineData(SharedData):
259@dataclass
260class TafLineData(SharedData):
261    end_time: Timestamp | None
262    icing: list[str]
263    probability: Number | None
264    raw: str
265    sanitized: str
266    start_time: Timestamp | None
267    transition_start: Timestamp | None
268    turbulence: list[str]
269    type: str
270    wind_shear: str | None
271    wind_variable_direction: list[Number] | None
TafLineData( altimeter: Number | None, clouds: list[Cloud], flight_rules: str, other: list[str], visibility: Number | None, wind_direction: Number | None, wind_gust: Number | None, wind_speed: Number | None, wx_codes: list[Code], end_time: Timestamp | None, icing: list[str], probability: Number | None, raw: str, sanitized: str, start_time: Timestamp | None, transition_start: Timestamp | None, turbulence: list[str], type: str, wind_shear: str | None, wind_variable_direction: list[Number] | None)
end_time: Timestamp | None
icing: list[str]
probability: Number | None
raw: str
sanitized: str
start_time: Timestamp | None
transition_start: Timestamp | None
turbulence: list[str]
type: str
wind_shear: str | None
wind_variable_direction: list[Number] | None
@dataclass
class TafData(ReportData):
274@dataclass
275class TafData(ReportData):
276    forecast: list[TafLineData]
277    start_time: Timestamp | None
278    end_time: Timestamp | None
279    is_amended: bool
280    is_correction: bool
281    max_temp: str | None = None
282    min_temp: str | None = None
283    alts: list[str] | None = None
284    temps: list[str] | None = None
285    remarks_info: RemarksData | None = None
TafData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[TafLineData], start_time: Timestamp | None, end_time: Timestamp | None, is_amended: bool, is_correction: bool, max_temp: str | None = None, min_temp: str | None = None, alts: list[str] | None = None, temps: list[str] | None = None, remarks_info: RemarksData | None = None)
forecast: list[TafLineData]
start_time: Timestamp | None
end_time: Timestamp | None
is_amended: bool
is_correction: bool
max_temp: str | None = None
min_temp: str | None = None
alts: list[str] | None = None
temps: list[str] | None = None
remarks_info: RemarksData | None = None
@dataclass
class ReportTrans:
288@dataclass
289class ReportTrans:
290    altimeter: str
291    clouds: str
292    wx_codes: str
293    visibility: str
ReportTrans(altimeter: str, clouds: str, wx_codes: str, visibility: str)
altimeter: str
clouds: str
wx_codes: str
visibility: str
@dataclass
class MetarTrans(ReportTrans):
296@dataclass
297class MetarTrans(ReportTrans):
298    dewpoint: str
299    remarks: dict
300    temperature: str
301    wind: str
MetarTrans( altimeter: str, clouds: str, wx_codes: str, visibility: str, dewpoint: str, remarks: dict, temperature: str, wind: str)
dewpoint: str
remarks: dict
temperature: str
wind: str
@dataclass
class TafLineTrans(ReportTrans):
304@dataclass
305class TafLineTrans(ReportTrans):
306    icing: str
307    turbulence: str
308    wind: str
309    wind_shear: str
TafLineTrans( altimeter: str, clouds: str, wx_codes: str, visibility: str, icing: str, turbulence: str, wind: str, wind_shear: str)
icing: str
turbulence: str
wind: str
wind_shear: str
@dataclass
class TafTrans:
312@dataclass
313class TafTrans:
314    forecast: list[TafLineTrans]
315    max_temp: str
316    min_temp: str
317    remarks: dict
TafTrans( forecast: list[TafLineTrans], max_temp: str, min_temp: str, remarks: dict)
forecast: list[TafLineTrans]
max_temp: str
min_temp: str
remarks: dict
@dataclass
class Turbulence:
320@dataclass
321class Turbulence:
322    severity: str
323    floor: Number | None = None
324    ceiling: Number | None = None
Turbulence( severity: str, floor: Number | None = None, ceiling: Number | None = None)
severity: str
floor: Number | None = None
ceiling: Number | None = None
@dataclass
class Icing(Turbulence):
327@dataclass
328class Icing(Turbulence):
329    type: str | None = None
Icing( severity: str, floor: Number | None = None, ceiling: Number | None = None, type: str | None = None)
type: str | None = None
Inherited Members
Turbulence
severity
floor
ceiling
@dataclass
class PirepData(ReportData):
332@dataclass
333class PirepData(ReportData):
334    aircraft: Aircraft | str | None = None
335    altitude: Number | str | None = None
336    clouds: list[Cloud] | None = None
337    flight_visibility: Number | None = None
338    icing: Icing | None = None
339    location: Location | None = None
340    other: list[str] | None = None
341    temperature: Number | None = None
342    turbulence: Turbulence | None = None
343    type: str | None = None
344    wx_codes: list[Code] | None = None
PirepData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, aircraft: Aircraft | str | None = None, altitude: Number | str | None = None, clouds: list[Cloud] | None = None, flight_visibility: Number | None = None, icing: Icing | None = None, location: Location | None = None, other: list[str] | None = None, temperature: Number | None = None, turbulence: Turbulence | None = None, type: str | None = None, wx_codes: list[Code] | None = None)
aircraft: Aircraft | str | None = None
altitude: Number | str | None = None
clouds: list[Cloud] | None = None
flight_visibility: Number | None = None
icing: Icing | None = None
location: Location | None = None
other: list[str] | None = None
temperature: Number | None = None
turbulence: Turbulence | None = None
type: str | None = None
wx_codes: list[Code] | None = None
@dataclass
class AirepData(ReportData):
347@dataclass
348class AirepData(ReportData):
349    pass
AirepData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None)
@dataclass
class Bulletin:
352@dataclass
353class Bulletin:
354    repr: str
355    type: Code
356    country: str
357    number: int
Bulletin(repr: str, type: Code, country: str, number: int)
repr: str
type: Code
country: str
number: int
@dataclass
class Movement:
360@dataclass
361class Movement:
362    repr: str
363    direction: Number | None
364    speed: Number | None
Movement( repr: str, direction: Number | None, speed: Number | None)
repr: str
direction: Number | None
speed: Number | None
MIN_POLY_SIZE = 2
@dataclass
class AirSigObservation:
370@dataclass
371class AirSigObservation:
372    type: Code | None
373    start_time: Timestamp | None
374    end_time: Timestamp | None
375    position: Coord | None
376    floor: Number | None
377    ceiling: Number | None
378    coords: list[Coord]
379    bounds: list[str]
380    movement: Movement | None
381    intensity: Code | None
382    other: list[str]
383
384    @property
385    def poly(self) -> Polygon | None:
386        if Polygon is None:
387            extra = "shape"
388            raise MissingExtraModule(extra)
389        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
AirSigObservation( type: Code | None, start_time: Timestamp | None, end_time: Timestamp | None, position: Coord | None, floor: Number | None, ceiling: Number | None, coords: list[Coord], bounds: list[str], movement: Movement | None, intensity: Code | None, other: list[str])
type: Code | None
start_time: Timestamp | None
end_time: Timestamp | None
position: Coord | None
floor: Number | None
ceiling: Number | None
coords: list[Coord]
bounds: list[str]
movement: Movement | None
intensity: Code | None
other: list[str]
poly: shapely.geometry.polygon.Polygon | None
384    @property
385    def poly(self) -> Polygon | None:
386        if Polygon is None:
387            extra = "shape"
388            raise MissingExtraModule(extra)
389        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
@dataclass
class AirSigmetData(ReportData):
392@dataclass
393class AirSigmetData(ReportData):
394    bulletin: Bulletin
395    issuer: str
396    correction: str | None
397    area: str
398    type: str
399    start_time: Timestamp | None
400    end_time: Timestamp | None
401    body: str
402    region: str
403    observation: AirSigObservation | None
404    forecast: AirSigObservation | None
AirSigmetData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, bulletin: Bulletin, issuer: str, correction: str | None, area: str, type: str, start_time: Timestamp | None, end_time: Timestamp | None, body: str, region: str, observation: AirSigObservation | None, forecast: AirSigObservation | None)
bulletin: Bulletin
issuer: str
correction: str | None
area: str
type: str
start_time: Timestamp | None
end_time: Timestamp | None
body: str
region: str
observation: AirSigObservation | None
forecast: AirSigObservation | None
@dataclass
class Qualifiers:
407@dataclass
408class Qualifiers:
409    repr: str
410    fir: str
411    subject: Code | None
412    condition: Code | None
413    traffic: Code | None
414    purpose: list[Code]
415    scope: list[Code]
416    lower: Altitude | None
417    upper: Altitude | None
418    coord: Coord | None
419    radius: Number | None
Qualifiers( repr: str, fir: str, subject: Code | None, condition: Code | None, traffic: Code | None, purpose: list[Code], scope: list[Code], lower: Altitude | None, upper: Altitude | None, coord: Coord | None, radius: Number | None)
repr: str
fir: str
subject: Code | None
condition: Code | None
traffic: Code | None
purpose: list[Code]
scope: list[Code]
lower: Altitude | None
upper: Altitude | None
coord: Coord | None
radius: Number | None
@dataclass
class NotamData(ReportData):
422@dataclass
423class NotamData(ReportData):
424    number: str | None
425    replaces: str | None
426    type: Code | None
427    qualifiers: Qualifiers | None
428    start_time: Timestamp | Code | None
429    end_time: Timestamp | Code | None
430    schedule: str | None
431    body: str
432    lower: Altitude | None
433    upper: Altitude | None
NotamData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, number: str | None, replaces: str | None, type: Code | None, qualifiers: Qualifiers | None, start_time: Timestamp | Code | None, end_time: Timestamp | Code | None, schedule: str | None, body: str, lower: Altitude | None, upper: Altitude | None)
number: str | None
replaces: str | None
type: Code | None
qualifiers: Qualifiers | None
start_time: Timestamp | Code | None
end_time: Timestamp | Code | None
schedule: str | None
body: str
lower: Altitude | None
upper: Altitude | None
@dataclass
class GfsPeriod:
436@dataclass
437class GfsPeriod:
438    time: Timestamp
439    temperature: Number
440    dewpoint: Number
441    cloud: Code
442    temperature_minmax: Number | None = None
443    precip_chance_12: Number | None = None
444    precip_amount_12: Code | None = None
445    thunderstorm_12: Number | None = None
446    severe_storm_12: Number | None = None
447    freezing_precip: Number | None = None
448    precip_type: Code | None = None
449    snow: Number | None = None
GfsPeriod( time: Timestamp, temperature: Number, dewpoint: Number, cloud: Code, temperature_minmax: Number | None = None, precip_chance_12: Number | None = None, precip_amount_12: Code | None = None, thunderstorm_12: Number | None = None, severe_storm_12: Number | None = None, freezing_precip: Number | None = None, precip_type: Code | None = None, snow: Number | None = None)
time: Timestamp
temperature: Number
dewpoint: Number
cloud: Code
temperature_minmax: Number | None = None
precip_chance_12: Number | None = None
precip_amount_12: Code | None = None
thunderstorm_12: Number | None = None
severe_storm_12: Number | None = None
freezing_precip: Number | None = None
precip_type: Code | None = None
snow: Number | None = None
@dataclass
class MavPeriod(GfsPeriod):
452@dataclass
453class MavPeriod(GfsPeriod):
454    wind_direction: Number | None = None
455    wind_speed: Number | None = None
456    precip_chance_6: Number | None = None
457    precip_amount_6: Code | None = None
458    thunderstorm_6: Number | None = None
459    severe_storm_6: Number | None = None
460    ceiling: Code | None = None
461    visibility: Code | None = None
462    vis_obstruction: Code | None = None
MavPeriod( time: Timestamp, temperature: Number, dewpoint: Number, cloud: Code, temperature_minmax: Number | None = None, precip_chance_12: Number | None = None, precip_amount_12: Code | None = None, thunderstorm_12: Number | None = None, severe_storm_12: Number | None = None, freezing_precip: Number | None = None, precip_type: Code | None = None, snow: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, precip_chance_6: Number | None = None, precip_amount_6: Code | None = None, thunderstorm_6: Number | None = None, severe_storm_6: Number | None = None, ceiling: Code | None = None, visibility: Code | None = None, vis_obstruction: Code | None = None)
wind_direction: Number | None = None
wind_speed: Number | None = None
precip_chance_6: Number | None = None
precip_amount_6: Code | None = None
thunderstorm_6: Number | None = None
severe_storm_6: Number | None = None
ceiling: Code | None = None
visibility: Code | None = None
vis_obstruction: Code | None = None
@dataclass
class MexPeriod(GfsPeriod):
465@dataclass
466class MexPeriod(GfsPeriod):
467    precip_chance_24: Number | None = None
468    precip_amount_24: Code | None = None
469    thunderstorm_24: Number | None = None
470    severe_storm_24: Number | None = None
471    rain_snow_mix: Number | None = None
472    snow_amount_24: Code | None = None
MexPeriod( time: Timestamp, temperature: Number, dewpoint: Number, cloud: Code, temperature_minmax: Number | None = None, precip_chance_12: Number | None = None, precip_amount_12: Code | None = None, thunderstorm_12: Number | None = None, severe_storm_12: Number | None = None, freezing_precip: Number | None = None, precip_type: Code | None = None, snow: Number | None = None, precip_chance_24: Number | None = None, precip_amount_24: Code | None = None, thunderstorm_24: Number | None = None, severe_storm_24: Number | None = None, rain_snow_mix: Number | None = None, snow_amount_24: Code | None = None)
precip_chance_24: Number | None = None
precip_amount_24: Code | None = None
thunderstorm_24: Number | None = None
severe_storm_24: Number | None = None
rain_snow_mix: Number | None = None
snow_amount_24: Code | None = None
@dataclass
class MavData(ReportData):
475@dataclass
476class MavData(ReportData):
477    forecast: list[MavPeriod]
MavData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[MavPeriod])
forecast: list[MavPeriod]
@dataclass
class MexData(ReportData):
480@dataclass
481class MexData(ReportData):
482    forecast: list[MexPeriod]
MexData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[MexPeriod])
forecast: list[MexPeriod]
@dataclass
class NbmUnits(Units):
485@dataclass
486class NbmUnits(Units):
487    duration: str
488    solar_radiation: str
489    wave_height: str
NbmUnits( accumulation: str, altimeter: str, altitude: str, temperature: str, visibility: str, wind_speed: str, duration: str, solar_radiation: str, wave_height: str)
duration: str
solar_radiation: str
wave_height: str
@dataclass
class NbmPeriod:
492@dataclass
493class NbmPeriod:
494    time: Timestamp
495    temperature: Number | None = None
496    dewpoint: Number | None = None
497    sky_cover: Number | None = None
498    wind_direction: Number | None = None
499    wind_speed: Number | None = None
500    wind_gust: Number | None = None
501    snow_level: Number | None = None
502    precip_duration: Number | None = None
503    freezing_precip: Number | None = None
504    snow: Number | None = None
505    sleet: Number | None = None
506    rain: Number | None = None
507    solar_radiation: Number | None = None
508    wave_height: Number | None = None
NbmPeriod( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None)
time: Timestamp
temperature: Number | None = None
dewpoint: Number | None = None
sky_cover: Number | None = None
wind_direction: Number | None = None
wind_speed: Number | None = None
wind_gust: Number | None = None
snow_level: Number | None = None
precip_duration: Number | None = None
freezing_precip: Number | None = None
snow: Number | None = None
sleet: Number | None = None
rain: Number | None = None
solar_radiation: Number | None = None
wave_height: Number | None = None
@dataclass
class NbhsShared(NbmPeriod):
511@dataclass
512class NbhsShared(NbmPeriod):
513    ceiling: Number | None = None
514    visibility: Number | None = None
515    cloud_base: Number | None = None
516    mixing_height: Number | None = None
517    transport_wind_direction: Number | None = None
518    transport_wind_speed: Number | None = None
519    haines: list[Number] | None = None
NbhsShared( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None, ceiling: Number | None = None, visibility: Number | None = None, cloud_base: Number | None = None, mixing_height: Number | None = None, transport_wind_direction: Number | None = None, transport_wind_speed: Number | None = None, haines: list[Number] | None = None)
ceiling: Number | None = None
visibility: Number | None = None
cloud_base: Number | None = None
mixing_height: Number | None = None
transport_wind_direction: Number | None = None
transport_wind_speed: Number | None = None
haines: list[Number] | None = None
@dataclass
class NbhPeriod(NbhsShared):
522@dataclass
523class NbhPeriod(NbhsShared):
524    precip_chance_1: Number | None = None
525    precip_chance_6: Number | None = None
526    precip_amount_1: Number | None = None
527    thunderstorm_1: Number | None = None
528    snow_amount_1: Number | None = None
529    icing_amount_1: Number | None = None
NbhPeriod( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None, ceiling: Number | None = None, visibility: Number | None = None, cloud_base: Number | None = None, mixing_height: Number | None = None, transport_wind_direction: Number | None = None, transport_wind_speed: Number | None = None, haines: list[Number] | None = None, precip_chance_1: Number | None = None, precip_chance_6: Number | None = None, precip_amount_1: Number | None = None, thunderstorm_1: Number | None = None, snow_amount_1: Number | None = None, icing_amount_1: Number | None = None)
precip_chance_1: Number | None = None
precip_chance_6: Number | None = None
precip_amount_1: Number | None = None
thunderstorm_1: Number | None = None
snow_amount_1: Number | None = None
icing_amount_1: Number | None = None
@dataclass
class NbsPeriod(NbhsShared):
532@dataclass
533class NbsPeriod(NbhsShared):
534    temperature_minmax: Number | None = None
535    precip_chance_6: Number | None = None
536    precip_chance_12: Number | None = None
537    precip_amount_6: Number | None = None
538    precip_amount_12: Number | None = None
539    precip_duration: Number | None = None
540    thunderstorm_3: Number | None = None
541    thunderstorm_6: Number | None = None
542    thunderstorm_12: Number | None = None
543    snow_amount_6: Number | None = None
544    icing_amount_6: Number | None = None
NbsPeriod( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None, ceiling: Number | None = None, visibility: Number | None = None, cloud_base: Number | None = None, mixing_height: Number | None = None, transport_wind_direction: Number | None = None, transport_wind_speed: Number | None = None, haines: list[Number] | None = None, temperature_minmax: Number | None = None, precip_chance_6: Number | None = None, precip_chance_12: Number | None = None, precip_amount_6: Number | None = None, precip_amount_12: Number | None = None, thunderstorm_3: Number | None = None, thunderstorm_6: Number | None = None, thunderstorm_12: Number | None = None, snow_amount_6: Number | None = None, icing_amount_6: Number | None = None)
temperature_minmax: Number | None = None
precip_chance_6: Number | None = None
precip_chance_12: Number | None = None
precip_amount_6: Number | None = None
precip_amount_12: Number | None = None
precip_duration: Number | None = None
thunderstorm_3: Number | None = None
thunderstorm_6: Number | None = None
thunderstorm_12: Number | None = None
snow_amount_6: Number | None = None
icing_amount_6: Number | None = None
@dataclass
class NbePeriod(NbmPeriod):
547@dataclass
548class NbePeriod(NbmPeriod):
549    temperature_minmax: Number | None = None
550    precip_chance_12: Number | None = None
551    precip_amount_12: Number | None = None
552    precip_amount_24: Number | None = None
553    thunderstorm_12: Number | None = None
554    snow_amount_12: Number | None = None
555    snow_amount_24: Number | None = None
556    icing_amount_12: Number | None = None
NbePeriod( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None, temperature_minmax: Number | None = None, precip_chance_12: Number | None = None, precip_amount_12: Number | None = None, precip_amount_24: Number | None = None, thunderstorm_12: Number | None = None, snow_amount_12: Number | None = None, snow_amount_24: Number | None = None, icing_amount_12: Number | None = None)
temperature_minmax: Number | None = None
precip_chance_12: Number | None = None
precip_amount_12: Number | None = None
precip_amount_24: Number | None = None
thunderstorm_12: Number | None = None
snow_amount_12: Number | None = None
snow_amount_24: Number | None = None
icing_amount_12: Number | None = None
@dataclass
class NbxPeriod(NbmPeriod):
559@dataclass
560class NbxPeriod(NbmPeriod):
561    precip_chance_12: Number | None = None
562    precip_amount_12: Number | None = None
563    precip_amount_24: Number | None = None
564    snow_amount_12: Number | None = None
565    icing_amount_12: Number | None = None
NbxPeriod( time: Timestamp, temperature: Number | None = None, dewpoint: Number | None = None, sky_cover: Number | None = None, wind_direction: Number | None = None, wind_speed: Number | None = None, wind_gust: Number | None = None, snow_level: Number | None = None, precip_duration: Number | None = None, freezing_precip: Number | None = None, snow: Number | None = None, sleet: Number | None = None, rain: Number | None = None, solar_radiation: Number | None = None, wave_height: Number | None = None, precip_chance_12: Number | None = None, precip_amount_12: Number | None = None, precip_amount_24: Number | None = None, snow_amount_12: Number | None = None, icing_amount_12: Number | None = None)
precip_chance_12: Number | None = None
precip_amount_12: Number | None = None
precip_amount_24: Number | None = None
snow_amount_12: Number | None = None
icing_amount_12: Number | None = None
@dataclass
class NbhData(ReportData):
568@dataclass
569class NbhData(ReportData):
570    forecast: list[NbhPeriod]
NbhData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[NbhPeriod])
forecast: list[NbhPeriod]
@dataclass
class NbsData(ReportData):
573@dataclass
574class NbsData(ReportData):
575    forecast: list[NbsPeriod]
NbsData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[NbsPeriod])
forecast: list[NbsPeriod]
@dataclass
class NbeData(ReportData):
578@dataclass
579class NbeData(ReportData):
580    forecast: list[NbePeriod]
NbeData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[NbePeriod])
forecast: list[NbePeriod]
@dataclass
class NbxData(ReportData):
583@dataclass
584class NbxData(ReportData):
585    forecast: list[NbxPeriod]
NbxData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None, forecast: list[NbxPeriod])
forecast: list[NbxPeriod]
@dataclass
class Sanitization:
625@dataclass
626class Sanitization:
627    """Tracks changes made during the sanitization process."""
628
629    removed: list[str] = field(default_factory=list)
630    replaced: dict[str, str] = field(default_factory=dict)
631    duplicates_found: bool = False
632    extra_spaces_found: bool = False
633    extra_spaces_needed: bool = False
634
635    @property
636    def errors_found(self) -> bool:
637        return bool(
638            self.removed
639            or self.replaced
640            or self.duplicates_found
641            or self.extra_spaces_found
642            or self.extra_spaces_needed
643        )
644
645    def log(self, item: str, replacement: str | None = None) -> None:
646        """Log a changed item. Calling without a replacement assumes removal."""
647        item = item.strip()
648        if not item:
649            return
650        if replacement is None:
651            self.removed.insert(0, item)
652            return
653        replacement = replacement.strip()
654        if not replacement:
655            self.removed.insert(0, item)
656        elif item != replacement:
657            self.replaced[item] = replacement
658
659    def log_list(self, before: list[str], after: list[str]) -> None:
660        """Log list differences. Assumes that list length and order haven't changed."""
661        for item, replacement in zip(before, after, strict=True):
662            if item != replacement:
663                self.log(item, replacement)

Tracks changes made during the sanitization process.

Sanitization( removed: list[str] = <factory>, replaced: dict[str, str] = <factory>, duplicates_found: bool = False, extra_spaces_found: bool = False, extra_spaces_needed: bool = False)
removed: list[str]
replaced: dict[str, str]
duplicates_found: bool = False
extra_spaces_found: bool = False
extra_spaces_needed: bool = False
errors_found: bool
635    @property
636    def errors_found(self) -> bool:
637        return bool(
638            self.removed
639            or self.replaced
640            or self.duplicates_found
641            or self.extra_spaces_found
642            or self.extra_spaces_needed
643        )
def log(self, item: str, replacement: str | None = None) -> None:
645    def log(self, item: str, replacement: str | None = None) -> None:
646        """Log a changed item. Calling without a replacement assumes removal."""
647        item = item.strip()
648        if not item:
649            return
650        if replacement is None:
651            self.removed.insert(0, item)
652            return
653        replacement = replacement.strip()
654        if not replacement:
655            self.removed.insert(0, item)
656        elif item != replacement:
657            self.replaced[item] = replacement

Log a changed item. Calling without a replacement assumes removal.

def log_list(self, before: list[str], after: list[str]) -> None:
659    def log_list(self, before: list[str], after: list[str]) -> None:
660        """Log list differences. Assumes that list length and order haven't changed."""
661        for item, replacement in zip(before, after, strict=True):
662            if item != replacement:
663                self.log(item, replacement)

Log list differences. Assumes that list length and order haven't changed.