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    is_amended: bool
268    is_correction: bool
269    max_temp: str | None = None
270    min_temp: str | None = None
271    alts: list[str] | None = None
272    temps: list[str] | None = None
273    remarks_info: RemarksData | None = None
274
275
276@dataclass
277class ReportTrans:
278    altimeter: str
279    clouds: str
280    wx_codes: str
281    visibility: str
282
283
284@dataclass
285class MetarTrans(ReportTrans):
286    dewpoint: str
287    remarks: dict
288    temperature: str
289    wind: str
290
291
292@dataclass
293class TafLineTrans(ReportTrans):
294    icing: str
295    turbulence: str
296    wind: str
297    wind_shear: str
298
299
300@dataclass
301class TafTrans:
302    forecast: list[TafLineTrans]
303    max_temp: str
304    min_temp: str
305    remarks: dict
306
307
308@dataclass
309class Turbulence:
310    severity: str
311    floor: Number | None = None
312    ceiling: Number | None = None
313
314
315@dataclass
316class Icing(Turbulence):
317    type: str | None = None
318
319
320@dataclass
321class PirepData(ReportData):
322    aircraft: Aircraft | str | None = None
323    altitude: Number | str | None = None
324    clouds: list[Cloud] | None = None
325    flight_visibility: Number | None = None
326    icing: Icing | None = None
327    location: Location | None = None
328    other: list[str] | None = None
329    temperature: Number | None = None
330    turbulence: Turbulence | None = None
331    type: str | None = None
332    wx_codes: list[Code] | None = None
333
334
335@dataclass
336class AirepData(ReportData):
337    pass
338
339
340@dataclass
341class Bulletin:
342    repr: str
343    type: Code
344    country: str
345    number: int
346
347
348@dataclass
349class Movement:
350    repr: str
351    direction: Number | None
352    speed: Number | None
353
354
355MIN_POLY_SIZE = 2
356
357
358@dataclass
359class AirSigObservation:
360    type: Code | None
361    start_time: Timestamp | None
362    end_time: Timestamp | None
363    position: Coord | None
364    floor: Number | None
365    ceiling: Number | None
366    coords: list[Coord]
367    bounds: list[str]
368    movement: Movement | None
369    intensity: Code | None
370    other: list[str]
371
372    @property
373    def poly(self) -> Polygon | None:
374        if Polygon is None:
375            extra = "shape"
376            raise MissingExtraModule(extra)
377        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
378
379
380@dataclass
381class AirSigmetData(ReportData):
382    bulletin: Bulletin
383    issuer: str
384    correction: str | None
385    area: str
386    type: str
387    start_time: Timestamp | None
388    end_time: Timestamp | None
389    body: str
390    region: str
391    observation: AirSigObservation | None
392    forecast: AirSigObservation | None
393
394
395@dataclass
396class Qualifiers:
397    repr: str
398    fir: str
399    subject: Code | None
400    condition: Code | None
401    traffic: Code | None
402    purpose: list[Code]
403    scope: list[Code]
404    lower: Number | None
405    upper: Number | None
406    coord: Coord | None
407    radius: Number | None
408
409
410@dataclass
411class NotamData(ReportData):
412    number: str | None
413    replaces: str | None
414    type: Code | None
415    qualifiers: Qualifiers | None
416    start_time: Timestamp | Code | None
417    end_time: Timestamp | Code | None
418    schedule: str | None
419    body: str
420    lower: Number | None
421    upper: Number | None
422
423
424@dataclass
425class GfsPeriod:
426    time: Timestamp
427    temperature: Number
428    dewpoint: Number
429    cloud: Code
430    temperature_minmax: Number | None = None
431    precip_chance_12: Number | None = None
432    precip_amount_12: Code | None = None
433    thunderstorm_12: Number | None = None
434    severe_storm_12: Number | None = None
435    freezing_precip: Number | None = None
436    precip_type: Code | None = None
437    snow: Number | None = None
438
439
440@dataclass
441class MavPeriod(GfsPeriod):
442    wind_direction: Number | None = None
443    wind_speed: Number | None = None
444    precip_chance_6: Number | None = None
445    precip_amount_6: Code | None = None
446    thunderstorm_6: Number | None = None
447    severe_storm_6: Number | None = None
448    ceiling: Code | None = None
449    visibility: Code | None = None
450    vis_obstruction: Code | None = None
451
452
453@dataclass
454class MexPeriod(GfsPeriod):
455    precip_chance_24: Number | None = None
456    precip_amount_24: Code | None = None
457    thunderstorm_24: Number | None = None
458    severe_storm_24: Number | None = None
459    rain_snow_mix: Number | None = None
460    snow_amount_24: Code | None = None
461
462
463@dataclass
464class MavData(ReportData):
465    forecast: list[MavPeriod]
466
467
468@dataclass
469class MexData(ReportData):
470    forecast: list[MexPeriod]
471
472
473@dataclass
474class NbmUnits(Units):
475    duration: str
476    solar_radiation: str
477    wave_height: str
478
479
480@dataclass
481class NbmPeriod:
482    time: Timestamp
483    temperature: Number | None = None
484    dewpoint: Number | None = None
485    sky_cover: Number | None = None
486    wind_direction: Number | None = None
487    wind_speed: Number | None = None
488    wind_gust: Number | None = None
489    snow_level: Number | None = None
490    precip_duration: Number | None = None
491    freezing_precip: Number | None = None
492    snow: Number | None = None
493    sleet: Number | None = None
494    rain: Number | None = None
495    solar_radiation: Number | None = None
496    wave_height: Number | None = None
497
498
499@dataclass
500class NbhsShared(NbmPeriod):
501    ceiling: Number | None = None
502    visibility: Number | None = None
503    cloud_base: Number | None = None
504    mixing_height: Number | None = None
505    transport_wind_direction: Number | None = None
506    transport_wind_speed: Number | None = None
507    haines: list[Number] | None = None
508
509
510@dataclass
511class NbhPeriod(NbhsShared):
512    precip_chance_1: Number | None = None
513    precip_chance_6: Number | None = None
514    precip_amount_1: Number | None = None
515    thunderstorm_1: Number | None = None
516    snow_amount_1: Number | None = None
517    icing_amount_1: Number | None = None
518
519
520@dataclass
521class NbsPeriod(NbhsShared):
522    temperature_minmax: Number | None = None
523    precip_chance_6: Number | None = None
524    precip_chance_12: Number | None = None
525    precip_amount_6: Number | None = None
526    precip_amount_12: Number | None = None
527    precip_duration: Number | None = None
528    thunderstorm_3: Number | None = None
529    thunderstorm_6: Number | None = None
530    thunderstorm_12: Number | None = None
531    snow_amount_6: Number | None = None
532    icing_amount_6: Number | None = None
533
534
535@dataclass
536class NbePeriod(NbmPeriod):
537    temperature_minmax: Number | None = None
538    precip_chance_12: Number | None = None
539    precip_amount_12: Number | None = None
540    precip_amount_24: Number | None = None
541    thunderstorm_12: Number | None = None
542    snow_amount_12: Number | None = None
543    snow_amount_24: Number | None = None
544    icing_amount_12: Number | None = None
545
546
547@dataclass
548class NbxPeriod(NbmPeriod):
549    precip_chance_12: Number | None = None
550    precip_amount_12: Number | None = None
551    precip_amount_24: Number | None = None
552    snow_amount_12: Number | None = None
553    icing_amount_12: Number | None = None
554
555
556@dataclass
557class NbhData(ReportData):
558    forecast: list[NbhPeriod]
559
560
561@dataclass
562class NbsData(ReportData):
563    forecast: list[NbsPeriod]
564
565
566@dataclass
567class NbeData(ReportData):
568    forecast: list[NbePeriod]
569
570
571@dataclass
572class NbxData(ReportData):
573    forecast: list[NbxPeriod]
574
575
576# @dataclass
577# class GfsPeriodTrans:
578#     temperature: str
579#     dewpoint: str
580#     cloud: str
581#     precip_chance_12: str
582#     precip_amount_12: str
583#     thunderstorm_12: str
584#     severe_storm_12: str
585#     freezing_precip: str
586#     precip_type: str
587#     snow: str
588
589
590# @dataclass
591# class MavPeriodTrans(GfsPeriodTrans):
592#     wind_direction: str
593#     wind_speed: str
594#     precip_chance_6: str
595#     precip_amount_6: str
596#     thunderstorm_6: str
597#     severe_storm_6: str
598#     ceiling: str
599#     visibility: str
600#     vis_obstruction: str
601
602
603# @dataclass
604# class MexPeriodTrans(GfsPeriodTrans):
605#     precip_chance_24: str
606#     precip_amount_24: str
607#     thunderstorm_24: str
608#     severe_storm_24: str
609#     rain_snow_mix: str
610#     snow_amount_24: str
611
612
613@dataclass
614class Sanitization:
615    """Tracks changes made during the sanitization process."""
616
617    removed: list[str] = field(default_factory=list)
618    replaced: dict[str, str] = field(default_factory=dict)
619    duplicates_found: bool = False
620    extra_spaces_found: bool = False
621    extra_spaces_needed: bool = False
622
623    @property
624    def errors_found(self) -> bool:
625        return bool(
626            self.removed
627            or self.replaced
628            or self.duplicates_found
629            or self.extra_spaces_found
630            or self.extra_spaces_needed
631        )
632
633    def log(self, item: str, replacement: str | None = None) -> None:
634        """Log a changed item. Calling without a replacement assumes removal."""
635        item = item.strip()
636        if not item:
637            return
638        if replacement is None:
639            self.removed.insert(0, item)
640            return
641        replacement = replacement.strip()
642        if not replacement:
643            self.removed.insert(0, item)
644        elif item != replacement:
645            self.replaced[item] = replacement
646
647    def log_list(self, before: list[str], after: list[str]) -> None:
648        """Log list differences. Assumes that list length and order haven't changed."""
649        for item, replacement in zip(before, after, strict=True):
650            if item != replacement:
651                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    is_amended: bool
269    is_correction: bool
270    max_temp: str | None = None
271    min_temp: str | None = None
272    alts: list[str] | None = None
273    temps: list[str] | None = None
274    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:
277@dataclass
278class ReportTrans:
279    altimeter: str
280    clouds: str
281    wx_codes: str
282    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):
285@dataclass
286class MetarTrans(ReportTrans):
287    dewpoint: str
288    remarks: dict
289    temperature: str
290    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):
293@dataclass
294class TafLineTrans(ReportTrans):
295    icing: str
296    turbulence: str
297    wind: str
298    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:
301@dataclass
302class TafTrans:
303    forecast: list[TafLineTrans]
304    max_temp: str
305    min_temp: str
306    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:
309@dataclass
310class Turbulence:
311    severity: str
312    floor: Number | None = None
313    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):
316@dataclass
317class Icing(Turbulence):
318    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):
321@dataclass
322class PirepData(ReportData):
323    aircraft: Aircraft | str | None = None
324    altitude: Number | str | None = None
325    clouds: list[Cloud] | None = None
326    flight_visibility: Number | None = None
327    icing: Icing | None = None
328    location: Location | None = None
329    other: list[str] | None = None
330    temperature: Number | None = None
331    turbulence: Turbulence | None = None
332    type: str | None = None
333    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):
336@dataclass
337class AirepData(ReportData):
338    pass
AirepData( raw: str, sanitized: str, station: str | None, time: Timestamp | None, remarks: str | None)
@dataclass
class Bulletin:
341@dataclass
342class Bulletin:
343    repr: str
344    type: Code
345    country: str
346    number: int
Bulletin(repr: str, type: Code, country: str, number: int)
repr: str
type: Code
country: str
number: int
@dataclass
class Movement:
349@dataclass
350class Movement:
351    repr: str
352    direction: Number | None
353    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:
359@dataclass
360class AirSigObservation:
361    type: Code | None
362    start_time: Timestamp | None
363    end_time: Timestamp | None
364    position: Coord | None
365    floor: Number | None
366    ceiling: Number | None
367    coords: list[Coord]
368    bounds: list[str]
369    movement: Movement | None
370    intensity: Code | None
371    other: list[str]
372
373    @property
374    def poly(self) -> Polygon | None:
375        if Polygon is None:
376            extra = "shape"
377            raise MissingExtraModule(extra)
378        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
373    @property
374    def poly(self) -> Polygon | None:
375        if Polygon is None:
376            extra = "shape"
377            raise MissingExtraModule(extra)
378        return Polygon([c.pair for c in self.coords]) if len(self.coords) > MIN_POLY_SIZE else None
@dataclass
class AirSigmetData(ReportData):
381@dataclass
382class AirSigmetData(ReportData):
383    bulletin: Bulletin
384    issuer: str
385    correction: str | None
386    area: str
387    type: str
388    start_time: Timestamp | None
389    end_time: Timestamp | None
390    body: str
391    region: str
392    observation: AirSigObservation | None
393    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:
396@dataclass
397class Qualifiers:
398    repr: str
399    fir: str
400    subject: Code | None
401    condition: Code | None
402    traffic: Code | None
403    purpose: list[Code]
404    scope: list[Code]
405    lower: Number | None
406    upper: Number | None
407    coord: Coord | None
408    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):
411@dataclass
412class NotamData(ReportData):
413    number: str | None
414    replaces: str | None
415    type: Code | None
416    qualifiers: Qualifiers | None
417    start_time: Timestamp | Code | None
418    end_time: Timestamp | Code | None
419    schedule: str | None
420    body: str
421    lower: Number | None
422    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:
425@dataclass
426class GfsPeriod:
427    time: Timestamp
428    temperature: Number
429    dewpoint: Number
430    cloud: Code
431    temperature_minmax: Number | None = None
432    precip_chance_12: Number | None = None
433    precip_amount_12: Code | None = None
434    thunderstorm_12: Number | None = None
435    severe_storm_12: Number | None = None
436    freezing_precip: Number | None = None
437    precip_type: Code | None = None
438    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):
441@dataclass
442class MavPeriod(GfsPeriod):
443    wind_direction: Number | None = None
444    wind_speed: Number | None = None
445    precip_chance_6: Number | None = None
446    precip_amount_6: Code | None = None
447    thunderstorm_6: Number | None = None
448    severe_storm_6: Number | None = None
449    ceiling: Code | None = None
450    visibility: Code | None = None
451    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):
454@dataclass
455class MexPeriod(GfsPeriod):
456    precip_chance_24: Number | None = None
457    precip_amount_24: Code | None = None
458    thunderstorm_24: Number | None = None
459    severe_storm_24: Number | None = None
460    rain_snow_mix: Number | None = None
461    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):
464@dataclass
465class MavData(ReportData):
466    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):
469@dataclass
470class MexData(ReportData):
471    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):
474@dataclass
475class NbmUnits(Units):
476    duration: str
477    solar_radiation: str
478    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:
481@dataclass
482class NbmPeriod:
483    time: Timestamp
484    temperature: Number | None = None
485    dewpoint: Number | None = None
486    sky_cover: Number | None = None
487    wind_direction: Number | None = None
488    wind_speed: Number | None = None
489    wind_gust: Number | None = None
490    snow_level: Number | None = None
491    precip_duration: Number | None = None
492    freezing_precip: Number | None = None
493    snow: Number | None = None
494    sleet: Number | None = None
495    rain: Number | None = None
496    solar_radiation: Number | None = None
497    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):
500@dataclass
501class NbhsShared(NbmPeriod):
502    ceiling: Number | None = None
503    visibility: Number | None = None
504    cloud_base: Number | None = None
505    mixing_height: Number | None = None
506    transport_wind_direction: Number | None = None
507    transport_wind_speed: Number | None = None
508    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):
511@dataclass
512class NbhPeriod(NbhsShared):
513    precip_chance_1: Number | None = None
514    precip_chance_6: Number | None = None
515    precip_amount_1: Number | None = None
516    thunderstorm_1: Number | None = None
517    snow_amount_1: Number | None = None
518    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):
521@dataclass
522class NbsPeriod(NbhsShared):
523    temperature_minmax: Number | None = None
524    precip_chance_6: Number | None = None
525    precip_chance_12: Number | None = None
526    precip_amount_6: Number | None = None
527    precip_amount_12: Number | None = None
528    precip_duration: Number | None = None
529    thunderstorm_3: Number | None = None
530    thunderstorm_6: Number | None = None
531    thunderstorm_12: Number | None = None
532    snow_amount_6: Number | None = None
533    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):
536@dataclass
537class NbePeriod(NbmPeriod):
538    temperature_minmax: Number | None = None
539    precip_chance_12: Number | None = None
540    precip_amount_12: Number | None = None
541    precip_amount_24: Number | None = None
542    thunderstorm_12: Number | None = None
543    snow_amount_12: Number | None = None
544    snow_amount_24: Number | None = None
545    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):
548@dataclass
549class NbxPeriod(NbmPeriod):
550    precip_chance_12: Number | None = None
551    precip_amount_12: Number | None = None
552    precip_amount_24: Number | None = None
553    snow_amount_12: Number | None = None
554    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):
557@dataclass
558class NbhData(ReportData):
559    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):
562@dataclass
563class NbsData(ReportData):
564    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):
567@dataclass
568class NbeData(ReportData):
569    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):
572@dataclass
573class NbxData(ReportData):
574    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:
614@dataclass
615class Sanitization:
616    """Tracks changes made during the sanitization process."""
617
618    removed: list[str] = field(default_factory=list)
619    replaced: dict[str, str] = field(default_factory=dict)
620    duplicates_found: bool = False
621    extra_spaces_found: bool = False
622    extra_spaces_needed: bool = False
623
624    @property
625    def errors_found(self) -> bool:
626        return bool(
627            self.removed
628            or self.replaced
629            or self.duplicates_found
630            or self.extra_spaces_found
631            or self.extra_spaces_needed
632        )
633
634    def log(self, item: str, replacement: str | None = None) -> None:
635        """Log a changed item. Calling without a replacement assumes removal."""
636        item = item.strip()
637        if not item:
638            return
639        if replacement is None:
640            self.removed.insert(0, item)
641            return
642        replacement = replacement.strip()
643        if not replacement:
644            self.removed.insert(0, item)
645        elif item != replacement:
646            self.replaced[item] = replacement
647
648    def log_list(self, before: list[str], after: list[str]) -> None:
649        """Log list differences. Assumes that list length and order haven't changed."""
650        for item, replacement in zip(before, after, strict=True):
651            if item != replacement:
652                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
624    @property
625    def errors_found(self) -> bool:
626        return bool(
627            self.removed
628            or self.replaced
629            or self.duplicates_found
630            or self.extra_spaces_found
631            or self.extra_spaces_needed
632        )
def log(self, item: str, replacement: str | None = None) -> None:
634    def log(self, item: str, replacement: str | None = None) -> None:
635        """Log a changed item. Calling without a replacement assumes removal."""
636        item = item.strip()
637        if not item:
638            return
639        if replacement is None:
640            self.removed.insert(0, item)
641            return
642        replacement = replacement.strip()
643        if not replacement:
644            self.removed.insert(0, item)
645        elif item != replacement:
646            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:
648    def log_list(self, before: list[str], after: list[str]) -> None:
649        """Log list differences. Assumes that list length and order haven't changed."""
650        for item, replacement in zip(before, after, strict=True):
651            if item != replacement:
652                self.log(item, replacement)

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