From 346e36ca5dee5242939d26fa48a2ac93f52f301c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:16:43 +0000 Subject: [PATCH 1/2] feat(runway): add runway visual range unit to parsed model Agent-Logs-Url: https://github.com/mivek/python-metar-taf-parser/sessions/9972eb91-dccc-4527-a84e-8813674158ce Co-authored-by: mivek <9912558+mivek@users.noreply.github.com> --- metar_taf_parser/command/metar.py | 4 +++- metar_taf_parser/model/enum.py | 8 ++++++++ metar_taf_parser/model/model.py | 10 +++++++++- metar_taf_parser/tests/command/test_metar.py | 8 +++++++- metar_taf_parser/tests/parser/test_parser.py | 11 ++++++++++- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/metar_taf_parser/command/metar.py b/metar_taf_parser/command/metar.py index faeaa98..58500bc 100644 --- a/metar_taf_parser/command/metar.py +++ b/metar_taf_parser/command/metar.py @@ -2,7 +2,7 @@ from metar_taf_parser.commons import converter from metar_taf_parser.commons.exception import ParseError -from metar_taf_parser.model.enum import DepositType, DepositCoverage +from metar_taf_parser.model.enum import DepositType, DepositCoverage, LengthUnit from metar_taf_parser.model.model import RunwayInfo, Metar from metar_taf_parser.commons.i18n import _ @@ -47,6 +47,7 @@ def _parse_runway(matches, metar, runway): runway.indicator = matches[0][1] runway.min_range = int(matches[0][2]) runway.trend = matches[0][3] + runway.unit = LengthUnit.FEET if matches[0][4] == LengthUnit.FEET.value else LengthUnit.METERS metar.add_runway_info(runway) @@ -55,6 +56,7 @@ def _parse_runway_max_range(matches, metar, runway): runway.min_range = int(matches[0][1]) runway.max_range = int(matches[0][2]) runway.trend = matches[0][3] + runway.unit = LengthUnit.FEET if matches[0][4] == LengthUnit.FEET.value else LengthUnit.METERS metar.add_runway_info(runway) diff --git a/metar_taf_parser/model/enum.py b/metar_taf_parser/model/enum.py index c9de352..ea9a69e 100644 --- a/metar_taf_parser/model/enum.py +++ b/metar_taf_parser/model/enum.py @@ -61,6 +61,14 @@ def __repr__(self): return _('DepositType.' + self.name) +class LengthUnit(enum.Enum): + METERS = 'M' + FEET = 'FT' + + def __repr__(self): + return self.value + + class Descriptive(enum.Enum): SHOWERS = 'SH' SHALLOW = 'MI' diff --git a/metar_taf_parser/model/model.py b/metar_taf_parser/model/model.py index 02651a0..83c908b 100644 --- a/metar_taf_parser/model/model.py +++ b/metar_taf_parser/model/model.py @@ -211,6 +211,7 @@ def __init__(self): self._name = None self._min_range = None self._max_range = None + self._unit = None self._trend = None self._indicator = None self._deposit_type = None @@ -236,6 +237,12 @@ def _get_max_range(self): def _set_max_range(self, value): self._max_range = value + def _get_unit(self): + return self._unit + + def _set_unit(self, value): + self._unit = value + def _get_trend(self): return self._trend @@ -273,13 +280,14 @@ def _set_braking_capacity(self, value): self._braking_capacity = value def __repr__(self): - return f'RunwayInfo[name={self.name}, min_range={self.min_range}, max_range={self.max_range}, '\ + return f'RunwayInfo[name={self.name}, min_range={self.min_range}, max_range={self.max_range}, unit={self.unit}, '\ f'trend={self.trend}, indicator={self.indicator}, deposit_type={self.deposit_type}, '\ f'coverage={self.coverage}, thickness={self.thickness}, braking_capacity={self.braking_capacity}]'\ name = property(_get_name, _set_name) min_range = property(_get_min_range, _set_min_range) max_range = property(_get_max_range, _set_max_range) + unit = property(_get_unit, _set_unit) trend = property(_get_trend, _set_trend) indicator = property(_get_indicator, _set_indicator) deposit_type = property(_get_deposit_type, _set_deposit_type) diff --git a/metar_taf_parser/tests/command/test_metar.py b/metar_taf_parser/tests/command/test_metar.py index 126fdf2..a0786a3 100644 --- a/metar_taf_parser/tests/command/test_metar.py +++ b/metar_taf_parser/tests/command/test_metar.py @@ -2,7 +2,7 @@ from metar_taf_parser.command.metar import RunwayCommand, CommandSupplier from metar_taf_parser.commons.exception import ParseError -from metar_taf_parser.model.enum import DepositType, DepositCoverage +from metar_taf_parser.model.enum import DepositType, DepositCoverage, LengthUnit from metar_taf_parser.model.model import Metar from metar_taf_parser.commons.i18n import _ @@ -20,6 +20,7 @@ def test_runway_command_execute(self): self.assertEqual('26', runway_info.name) self.assertEqual(600, runway_info.min_range) self.assertEqual('U', runway_info.trend) + self.assertEqual(LengthUnit.METERS, runway_info.unit) def test_runway_command_execute_runway(self): metar = Metar() @@ -34,6 +35,7 @@ def test_runway_command_execute_runway(self): self.assertEqual(550, runway_info.min_range) self.assertEqual(700, runway_info.max_range) self.assertEqual('U', runway_info.trend) + self.assertEqual(LengthUnit.METERS, runway_info.unit) def test_runway_command_execute_wrong_runway(self): metar = Metar() @@ -54,6 +56,7 @@ def test_parse_runwway_visual_range_feet_variable(self): self.assertEqual(600, metar.runways_info[0].min_range) self.assertEqual(1000, metar.runways_info[0].max_range) self.assertEqual('', metar.runways_info[0].trend) + self.assertEqual(LengthUnit.FEET, metar.runways_info[0].unit) def test_parse_runway_visual_range_feet_simple(self): metar = Metar() @@ -65,6 +68,7 @@ def test_parse_runway_visual_range_feet_simple(self): self.assertEqual('01L', metar.runways_info[0].name) self.assertEqual(800, metar.runways_info[0].min_range) self.assertEqual('', metar.runways_info[0].trend) + self.assertEqual(LengthUnit.FEET, metar.runways_info[0].unit) def test_parse_runway_deposit(self): metar = Metar() @@ -101,6 +105,7 @@ def test_parse_runway_with_less_than_indicator_and_unit(self): self.assertEqual('01L', metar.runways_info[0].name) self.assertEqual('M', metar.runways_info[0].indicator) self.assertEqual(600, metar.runways_info[0].min_range) + self.assertEqual(LengthUnit.FEET, metar.runways_info[0].unit) def test_parse_runway_with_greater_than_indicator(self): metar = Metar() @@ -108,6 +113,7 @@ def test_parse_runway_with_greater_than_indicator(self): self.assertEqual('01L', metar.runways_info[0].name) self.assertEqual('P', metar.runways_info[0].indicator) self.assertEqual(600, metar.runways_info[0].min_range) + self.assertEqual(LengthUnit.FEET, metar.runways_info[0].unit) def test_parse_runway_missing_info(self): metar = Metar() diff --git a/metar_taf_parser/tests/parser/test_parser.py b/metar_taf_parser/tests/parser/test_parser.py index 88c9c29..97b3856 100644 --- a/metar_taf_parser/tests/parser/test_parser.py +++ b/metar_taf_parser/tests/parser/test_parser.py @@ -3,7 +3,7 @@ from parameterized import parameterized from metar_taf_parser.model.enum import Intensity, Phenomenon, Descriptive, DepositType, DepositCoverage, WeatherChangeType, CloudQuantity, CloudType, \ - TimeIndicator, TurbulenceIntensity, IcingIntensity + TimeIndicator, TurbulenceIntensity, IcingIntensity, LengthUnit from metar_taf_parser.model.model import AbstractWeatherContainer, Visibility, Wind from metar_taf_parser.parser.parser import AbstractParser, MetarParser, _parse_validity, _parse_temperature, TAFParser, \ RemarkParser @@ -90,6 +90,15 @@ def test_parse(self): self.assertEqual('27L', metar.runways_info[0].name) self.assertEqual(375, metar.runways_info[0].min_range) self.assertEqual('N', metar.runways_info[0].trend) + self.assertEqual(LengthUnit.METERS, metar.runways_info[0].unit) + + def test_parse_with_runway_visual_range_in_feet(self): + metar = MetarParser().parse('KJFK 121651Z 18012KT 9999 R24L/2400FT SCT015 18/12 A2992') + + self.assertEqual(1, len(metar.runways_info)) + self.assertEqual('24L', metar.runways_info[0].name) + self.assertEqual(2400, metar.runways_info[0].min_range) + self.assertEqual(LengthUnit.FEET, metar.runways_info[0].unit) def test_parse_with_tempo(self): metar = MetarParser().parse( From 0cc12e4f010ffbb4f9da8b7e84fb54754746f1f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:18:43 +0000 Subject: [PATCH 2/2] refactor(runway): centralize runway unit parsing Agent-Logs-Url: https://github.com/mivek/python-metar-taf-parser/sessions/9972eb91-dccc-4527-a84e-8813674158ce Co-authored-by: mivek <9912558+mivek@users.noreply.github.com> --- metar_taf_parser/command/metar.py | 8 ++++++-- metar_taf_parser/model/enum.py | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/metar_taf_parser/command/metar.py b/metar_taf_parser/command/metar.py index 58500bc..cd97fe8 100644 --- a/metar_taf_parser/command/metar.py +++ b/metar_taf_parser/command/metar.py @@ -42,12 +42,16 @@ def execute(self, metar: Metar, input: str): metar.altimeter = int(converter.convert_inches_mercury_to_pascal(mercury)) +def _parse_runway_unit(input: str): + return LengthUnit.FEET if input == 'FT' else LengthUnit.METERS + + def _parse_runway(matches, metar, runway): runway.name = matches[0][0] runway.indicator = matches[0][1] runway.min_range = int(matches[0][2]) runway.trend = matches[0][3] - runway.unit = LengthUnit.FEET if matches[0][4] == LengthUnit.FEET.value else LengthUnit.METERS + runway.unit = _parse_runway_unit(matches[0][4]) metar.add_runway_info(runway) @@ -56,7 +60,7 @@ def _parse_runway_max_range(matches, metar, runway): runway.min_range = int(matches[0][1]) runway.max_range = int(matches[0][2]) runway.trend = matches[0][3] - runway.unit = LengthUnit.FEET if matches[0][4] == LengthUnit.FEET.value else LengthUnit.METERS + runway.unit = _parse_runway_unit(matches[0][4]) metar.add_runway_info(runway) diff --git a/metar_taf_parser/model/enum.py b/metar_taf_parser/model/enum.py index ea9a69e..4642b06 100644 --- a/metar_taf_parser/model/enum.py +++ b/metar_taf_parser/model/enum.py @@ -65,6 +65,9 @@ class LengthUnit(enum.Enum): METERS = 'M' FEET = 'FT' + def __str__(self): + return self.value + def __repr__(self): return self.value