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

Load a list of codes from string characters.

@dataclass
class Coord:
132@dataclass
133class Coord:
134    lat: float
135    lon: float
136    repr: str | None = None
137
138    @property
139    def pair(self) -> tuple[float, float]:
140        return self.lat, self.lon
141
142    @property
143    def point(self) -> Point:
144        if Point is None:
145            extra = "shape"
146            raise MissingExtraModule(extra)
147        return Point(self.lat, self.lon)
148
149    @staticmethod
150    def to_dms(value: float) -> tuple[int, int, int]:
151        """Convert a coordinate decimal value to degree, minute, second."""
152        minute, second = divmod(abs(value) * 3600, 60)
153        degree, minute = divmod(minute, 60)
154        if value < 0:
155            degree *= -1
156        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]
138    @property
139    def pair(self) -> tuple[float, float]:
140        return self.lat, self.lon
point: shapely.geometry.point.Point
142    @property
143    def point(self) -> Point:
144        if Point is None:
145            extra = "shape"
146            raise MissingExtraModule(extra)
147        return Point(self.lat, self.lon)
@staticmethod
def to_dms(value: float) -> tuple[int, int, int]:
149    @staticmethod
150    def to_dms(value: float) -> tuple[int, int, int]:
151        """Convert a coordinate decimal value to degree, minute, second."""
152        minute, second = divmod(abs(value) * 3600, 60)
153        degree, minute = divmod(minute, 60)
154        if value < 0:
155            degree *= -1
156        return int(degree), int(minute), int(second)

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

@dataclass
class Cloud:
159@dataclass
160class Cloud:
161    repr: str
162    type: str | None = None
163    base: int | None = None
164    top: int | None = None
165    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:
168@dataclass
169class RunwayVisibility:
170    repr: str
171    runway: str
172    visibility: Number | None
173    variable_visibility: list[Number]
174    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:
177@dataclass
178class Location:
179    repr: str
180    station: str | None
181    direction: Number | None
182    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:
185@dataclass
186class PressureTendency:
187    repr: str
188    tendency: str
189    change: float
PressureTendency(repr: str, tendency: str, change: float)
repr: str
tendency: str
change: float
@dataclass
class FiveDigitCodes:
192@dataclass
193class FiveDigitCodes:
194    maximum_temperature_6: Number | None = None  # 1
195    minimum_temperature_6: Number | None = None  # 2
196    pressure_tendency: PressureTendency | None = None  # 5
197    precip_36_hours: Number | None = None  # 6
198    precip_24_hours: Number | None = None  # 7
199    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):
202@dataclass
203class RemarksData(FiveDigitCodes):
204    codes: list[Code] = field(default_factory=list)
205    dewpoint_decimal: Number | None = None
206    maximum_temperature_24: Number | None = None
207    minimum_temperature_24: Number | None = None
208    precip_hourly: Number | None = None
209    sea_level_pressure: Number | None = None
210    snow_depth: Number | None = None
211    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:
214@dataclass
215class ReportData:
216    raw: str
217    sanitized: str
218    station: str | None
219    time: Timestamp | None
220    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:
223@dataclass
224class SharedData:
225    altimeter: Number | None
226    clouds: list[Cloud]
227    flight_rules: str
228    other: list[str]
229    visibility: Number | None
230    wind_direction: Number | None
231    wind_gust: Number | None
232    wind_speed: Number | None
233    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):
236@dataclass
237class MetarData(ReportData, SharedData):
238    dewpoint: Number | None
239    relative_humidity: float | None
240    remarks_info: RemarksData | None
241    runway_visibility: list[RunwayVisibility]
242    temperature: Number | None
243    wind_variable_direction: list[Number]
244    density_altitude: int | None = None
245    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):
248@dataclass
249class TafLineData(SharedData):
250    end_time: Timestamp | None
251    icing: list[str]
252    probability: Number | None
253    raw: str
254    sanitized: str
255    start_time: Timestamp | None
256    transition_start: Timestamp | None
257    turbulence: list[str]
258    type: str
259    wind_shear: str | None
260    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):
263@dataclass
264class TafData(ReportData):
265    forecast: list[TafLineData]
266    start_time: Timestamp | None
267    end_time: Timestamp | None
268    max_temp: str | None = None
269    min_temp: str | None = None
270    alts: list[str] | None = None
271    temps: list[str] | None = None
272    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, 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
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:
275@dataclass
276class ReportTrans:
277    altimeter: str
278    clouds: str
279    wx_codes: str
280    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):
283@dataclass
284class MetarTrans(ReportTrans):
285    dewpoint: str
286    remarks: dict
287    temperature: str
288    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):
291@dataclass
292class TafLineTrans(ReportTrans):
293    icing: str
294    turbulence: str
295    wind: str
296    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:
299@dataclass
300class TafTrans:
301    forecast: list[TafLineTrans]
302    max_temp: str
303    min_temp: str
304    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:
307@dataclass
308class Turbulence:
309    severity: str
310    floor: Number | None = None
311    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):
314@dataclass
315class Icing(Turbulence):
316    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):
319@dataclass
320class PirepData(ReportData):
321    aircraft: Aircraft | str | None = None
322    altitude: Number | str | None = None
323    clouds: list[Cloud] | None = None
324    flight_visibility: Number | None = None
325    icing: Icing | None = None
326    location: Location | None = None
327    other: list[str] | None = None
328    temperature: Number | None = None
329    turbulence: Turbulence | None = None
330    type: str | None = None
331    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):
334@dataclass
335class AirepData(ReportData):
336    pass
AirepData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None)
@dataclass
class Bulletin:
339@dataclass
340class Bulletin:
341    repr: str
342    type: Code
343    country: str
344    number: int
Bulletin(repr: str, type: Code, country: str, number: int)
repr: str
type: Code
country: str
number: int
@dataclass
class Movement:
347@dataclass
348class Movement:
349    repr: str
350    direction: Number | None
351    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:
357@dataclass
358class AirSigObservation:
359    type: Code | None
360    start_time: Timestamp | None
361    end_time: Timestamp | None
362    position: Coord | None
363    floor: Number | None
364    ceiling: Number | None
365    coords: list[Coord]
366    bounds: list[str]
367    movement: Movement | None
368    intensity: Code | None
369    other: list[str]
370
371    @property
372    def poly(self) -> Polygon | None:
373        if Polygon is None:
374            extra = "shape"
375            raise MissingExtraModule(extra)
376        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
371    @property
372    def poly(self) -> Polygon | None:
373        if Polygon is None:
374            extra = "shape"
375            raise MissingExtraModule(extra)
376        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
@dataclass
class AirSigmetData(ReportData):
379@dataclass
380class AirSigmetData(ReportData):
381    bulletin: Bulletin
382    issuer: str
383    correction: str | None
384    area: str
385    type: str
386    start_time: Timestamp | None
387    end_time: Timestamp | None
388    body: str
389    region: str
390    observation: AirSigObservation | None
391    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:
394@dataclass
395class Qualifiers:
396    repr: str
397    fir: str
398    subject: Code | None
399    condition: Code | None
400    traffic: Code | None
401    purpose: list[Code]
402    scope: list[Code]
403    lower: Number | None
404    upper: Number | None
405    coord: Coord | None
406    radius: Number | None
Qualifiers( repr: str, fir: str, subject: Code | None, condition: Code | None, traffic: Code | None, purpose: list[Code], scope: list[Code], lower: Number | None, upper: Number | 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: Number | None
upper: Number | None
coord: Coord | None
radius: Number | None
@dataclass
class NotamData(ReportData):
409@dataclass
410class NotamData(ReportData):
411    number: str | None
412    replaces: str | None
413    type: Code | None
414    qualifiers: Qualifiers | None
415    start_time: Timestamp | Code | None
416    end_time: Timestamp | Code | None
417    schedule: str | None
418    body: str
419    lower: Number | None
420    upper: Number | 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: Number | None, upper: Number | 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: Number | None
upper: Number | None
@dataclass
class GfsPeriod:
423@dataclass
424class GfsPeriod:
425    time: Timestamp
426    temperature: Number
427    dewpoint: Number
428    cloud: Code
429    temperature_minmax: Number | None = None
430    precip_chance_12: Number | None = None
431    precip_amount_12: Code | None = None
432    thunderstorm_12: Number | None = None
433    severe_storm_12: Number | None = None
434    freezing_precip: Number | None = None
435    precip_type: Code | None = None
436    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):
439@dataclass
440class MavPeriod(GfsPeriod):
441    wind_direction: Number | None = None
442    wind_speed: Number | None = None
443    precip_chance_6: Number | None = None
444    precip_amount_6: Code | None = None
445    thunderstorm_6: Number | None = None
446    severe_storm_6: Number | None = None
447    ceiling: Code | None = None
448    visibility: Code | None = None
449    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):
452@dataclass
453class MexPeriod(GfsPeriod):
454    precip_chance_24: Number | None = None
455    precip_amount_24: Code | None = None
456    thunderstorm_24: Number | None = None
457    severe_storm_24: Number | None = None
458    rain_snow_mix: Number | None = None
459    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):
462@dataclass
463class MavData(ReportData):
464    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):
467@dataclass
468class MexData(ReportData):
469    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):
472@dataclass
473class NbmUnits(Units):
474    duration: str
475    solar_radiation: str
476    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:
479@dataclass
480class NbmPeriod:
481    time: Timestamp
482    temperature: Number | None = None
483    dewpoint: Number | None = None
484    sky_cover: Number | None = None
485    wind_direction: Number | None = None
486    wind_speed: Number | None = None
487    wind_gust: Number | None = None
488    snow_level: Number | None = None
489    precip_duration: Number | None = None
490    freezing_precip: Number | None = None
491    snow: Number | None = None
492    sleet: Number | None = None
493    rain: Number | None = None
494    solar_radiation: Number | None = None
495    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):
498@dataclass
499class NbhsShared(NbmPeriod):
500    ceiling: Number | None = None
501    visibility: Number | None = None
502    cloud_base: Number | None = None
503    mixing_height: Number | None = None
504    transport_wind_direction: Number | None = None
505    transport_wind_speed: Number | None = None
506    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):
509@dataclass
510class NbhPeriod(NbhsShared):
511    precip_chance_1: Number | None = None
512    precip_chance_6: Number | None = None
513    precip_amount_1: Number | None = None
514    thunderstorm_1: Number | None = None
515    snow_amount_1: Number | None = None
516    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):
519@dataclass
520class NbsPeriod(NbhsShared):
521    temperature_minmax: Number | None = None
522    precip_chance_6: Number | None = None
523    precip_chance_12: Number | None = None
524    precip_amount_6: Number | None = None
525    precip_amount_12: Number | None = None
526    precip_duration: Number | None = None
527    thunderstorm_3: Number | None = None
528    thunderstorm_6: Number | None = None
529    thunderstorm_12: Number | None = None
530    snow_amount_6: Number | None = None
531    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):
534@dataclass
535class NbePeriod(NbmPeriod):
536    temperature_minmax: Number | None = None
537    precip_chance_12: Number | None = None
538    precip_amount_12: Number | None = None
539    precip_amount_24: Number | None = None
540    thunderstorm_12: Number | None = None
541    snow_amount_12: Number | None = None
542    snow_amount_24: Number | None = None
543    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):
546@dataclass
547class NbxPeriod(NbmPeriod):
548    precip_chance_12: Number | None = None
549    precip_amount_12: Number | None = None
550    precip_amount_24: Number | None = None
551    snow_amount_12: Number | None = None
552    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):
555@dataclass
556class NbhData(ReportData):
557    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):
560@dataclass
561class NbsData(ReportData):
562    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):
565@dataclass
566class NbeData(ReportData):
567    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):
570@dataclass
571class NbxData(ReportData):
572    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:
612@dataclass
613class Sanitization:
614    """Tracks changes made during the sanitization process."""
615
616    removed: list[str] = field(default_factory=list)
617    replaced: dict[str, str] = field(default_factory=dict)
618    duplicates_found: bool = False
619    extra_spaces_found: bool = False
620    extra_spaces_needed: bool = False
621
622    @property
623    def errors_found(self) -> bool:
624        return bool(
625            self.removed
626            or self.replaced
627            or self.duplicates_found
628            or self.extra_spaces_found
629            or self.extra_spaces_needed
630        )
631
632    def log(self, item: str, replacement: str | None = None) -> None:
633        """Log a changed item. Calling without a replacement assumes removal."""
634        item = item.strip()
635        if not item:
636            return
637        if replacement is None:
638            self.removed.insert(0, item)
639            return
640        replacement = replacement.strip()
641        if not replacement:
642            self.removed.insert(0, item)
643        elif item != replacement:
644            self.replaced[item] = replacement
645
646    def log_list(self, before: list[str], after: list[str]) -> None:
647        """Log list differences. Assumes that list length and order haven't changed."""
648        for item, replacement in zip(before, after, strict=True):
649            if item != replacement:
650                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
622    @property
623    def errors_found(self) -> bool:
624        return bool(
625            self.removed
626            or self.replaced
627            or self.duplicates_found
628            or self.extra_spaces_found
629            or self.extra_spaces_needed
630        )
def log(self, item: str, replacement: str | None = None) -> None:
632    def log(self, item: str, replacement: str | None = None) -> None:
633        """Log a changed item. Calling without a replacement assumes removal."""
634        item = item.strip()
635        if not item:
636            return
637        if replacement is None:
638            self.removed.insert(0, item)
639            return
640        replacement = replacement.strip()
641        if not replacement:
642            self.removed.insert(0, item)
643        elif item != replacement:
644            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:
646    def log_list(self, before: list[str], after: list[str]) -> None:
647        """Log list differences. Assumes that list length and order haven't changed."""
648        for item, replacement in zip(before, after, strict=True):
649            if item != replacement:
650                self.log(item, replacement)

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