From f8e27b8562b91e69ff17eb3e32db3ed7f943cffe Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 16 Jan 2025 07:55:19 +0545 Subject: [PATCH] Add desinventar transformer --- pyproject.toml | 4 +- pystac_monty/sources/common.py | 42 ++++ pystac_monty/sources/desinventar.py | 362 +++++++++++++++++++++++++++ tests/extensions/test_desinventar.py | 58 +++++ uv.lock | 207 +++++++++++---- 5 files changed, 629 insertions(+), 44 deletions(-) create mode 100644 pystac_monty/sources/desinventar.py create mode 100644 tests/extensions/test_desinventar.py diff --git a/pyproject.toml b/pyproject.toml index b2b5b66..84da58b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,9 @@ dependencies = [ "markdownify>=0.14.1", "pytz>=2021.1", "pandas>=2.2.0", - "shapely>=2.0.0", + "geopandas>=1.0.1", + "lxml>=5.3.0", + "types-lxml>=2024.12.13", ] dynamic = ["version"] diff --git a/pystac_monty/sources/common.py b/pystac_monty/sources/common.py index 7718ee3..251d209 100644 --- a/pystac_monty/sources/common.py +++ b/pystac_monty/sources/common.py @@ -1,5 +1,8 @@ +import json from dataclasses import dataclass from typing import Any +import requests +from pystac import Collection @dataclass @@ -9,9 +12,48 @@ class MontyDataSource: def __init__(self, source_url: str, data: Any): self.source_url = source_url + self.data = data def get_source_url(self) -> str: return self.source_url def get_data(self) -> Any: return self.data + +base_collection_url = "https://github.com/IFRCGo/monty-stac-extension/raw/refs/heads/main/examples" +@dataclass +class MontyDataTransformer: + events_collection_id: str + events_collection_url: str + hazards_collection_id: str + hazards_collection_url: str + impacts_collection_id: str + impacts_collection_url: str + + # FIXME: we might have to get ids and urls manually + def __init__(self, source_name: str): + self.events_collection_id = f"{source_name}-events" + self.hazards_collection_id = f"{source_name}-hazards" + self.impacts_collection_id = f"{source_name}-impacts" + + self.events_collection_url = f"{base_collection_url}/{self.events_collection_id}/{self.events_collection_id}.json" + self.hazards_collection_url = f"{base_collection_url}/{self.hazards_collection_id}/{self.hazards_collection_id}.json" + self.impacts_collection_url = f"{base_collection_url}/{self.impacts_collection_id}/{self.impacts_collection_id}.json" + + def get_event_collection(self) -> Collection: + """Get event collection""" + response = requests.get(self.events_collection_url) + collection_dict = json.loads(response.text) + return Collection.from_dict(collection_dict) + + def get_hazard_collection(self) -> Collection: + """Get hazard collection""" + response = requests.get(self.hazards_collection_url) + collection_dict = json.loads(response.text) + return Collection.from_dict(collection_dict) + + def get_impact_collection(self) -> Collection: + """Get impact collection""" + response = requests.get(self.impacts_collection_url) + collection_dict = json.loads(response.text) + return Collection.from_dict(collection_dict) diff --git a/pystac_monty/sources/desinventar.py b/pystac_monty/sources/desinventar.py new file mode 100644 index 0000000..df429ff --- /dev/null +++ b/pystac_monty/sources/desinventar.py @@ -0,0 +1,362 @@ +from contextlib import contextmanager +from datetime import datetime +import re +from typing import Any, Dict, List, Optional, TypedDict +from zipfile import ZipFile +from pathlib import Path +from lxml import etree +from geopandas import gpd +from pystac.item import Item +import pytz + +from pystac_monty.extension import MontyExtension +from pystac_monty.sources.common import MontyDataTransformer +from pystac_monty.hazard_profiles import HazardProfiles + + +STAC_EVENT_ID_PREFIX = "desinventar-event-" +STAC_HAZARD_ID_PREFIX = "desinventar-hazard-" +STAC_IMPACT_ID_PREFIX = "desinventar-impact-" + +# TODO: move to common utils +def get_list_item_safe(list, index, default_value=None): + try: + return list[index] + except IndexError: + return default_value + +class DataRow(TypedDict): + # Properties extracted from desinventar + + serial: str + deaths: Optional[str] + flag_deaths: Optional[str] + injured: Optional[str] + flag_injured: Optional[str] + missing: Optional[str] + flag_missing: Optional[str] + houses_destroyed: Optional[str] + flag_houses_destroyed: Optional[str] + houses_damaged: Optional[str] + flag_houses_damaged: Optional[str] + directly_affected: Optional[str] + flag_directly_affected: Optional[str] + indirectly_affected: Optional[str] + flag_indirectly_affected: Optional[str] + relocated: Optional[str] + flag_relocated: Optional[str] + evacuated: Optional[str] + flag_evacuated: Optional[str] + losses_in_dollar: Optional[str] + losses_local_currency: Optional[str] + education_centers: Optional[str] + hospitals: Optional[str] + damages_in_crops_ha: Optional[str] + lost_cattle: Optional[str] + damages_in_roads_mts: Optional[str] + level0: Optional[str] + level1: Optional[str] + level2: Optional[str] + name0: Optional[str] + name1: Optional[str] + name2: Optional[str] + latitude: Optional[str] + longitude: Optional[str] + event: Optional[str] + glide: Optional[str] + location: Optional[str] + haz_maxvalue: Optional[str] + duration: Optional[str] + year: Optional[str] + month: Optional[str] + day: Optional[str] + + # Calculated properties + geometry: Optional[Dict[str, Any]] + bbox: Optional[list[float]] + + # Additional properties + iso3: str + desinventar_country_code: str + +hazard_mapping = { + "FLOOD": "hh", + "FIRE": "hh", + "LANDSLIDE": "hh", + "DROUGHT": "hh", + "FOREST FIRE": "hh", + "RAIN": "hh", + "WINDSTORM": "hh", + "STORM": "hh", + "EARTHQUAKE": "hh", + "ELECTRIC STORM": "hh", + "SNOWSTORM": "hh", + "FROST": "hh", + "HAILSTORM": "hh", + "STRUCT.COLLAPSE": "hh", + "FLASH FLOOD": "hh", + "CYCLONE": "hh", + "ALLUVION": "hh", + "SURGE": "hh", + "COLD WAVE": "hh", + "HEAT WAVE": "hh", + "ERUPTION": "hh", + "LIQUEFACTION": "hh", + "FOG": "hh", + "AVALANCHE": "hh", + "TSUNAMI": "hh", + "COASTAL EROSION": "hh", + "SUBSIDENCE": "hh", + "TORNADO": "hh", + "EROSION": "hh", + "SEDIMENTATION": "hh", + "EPIZOOTIC": "hh", + "SANDSTORM": "hh", + "LAHAR": "hh", + "OZONO": "hh" +} + +def strtoi(s: str | None, default: str | int | None = None): + if (s is None): + return default or s; + + try: + return int(s) + except ValueError: + return default or s + +def natural_keys(text): + return [strtoi(c) for c in re.split(r'(\d+)', text)] + +def get_lowest_level(all_levels_in_order: List[str], row_data: DataRow): + for level in all_levels_in_order: + if row_data[level] is not None: + return level + + return None + + +class DesinventarDataSource(): + zf_ref: ZipFile + country_code: str + iso3: str + + def __init__(self, zip_file: ZipFile, country_code: str, iso3: str): + self.zf_ref = zip_file + self.country_code = country_code + self.iso3 = iso3 + + @classmethod + def from_path(cls, zip_file_path: str, country_code: str, iso3: str): + zf_ref = ZipFile(zip_file_path) + return cls(zf_ref, country_code, iso3) + + # TODO: from_url + + @contextmanager + def with_xml_file(self): + try: + xml_file = self.zf_ref.open(f"DI_export_{self.country_code}.xml") + yield xml_file + except: + xml_file = None + finally: + if xml_file: + xml_file.close() + + @contextmanager + def open_file(self, filename): + try: + file = self.zf_ref.open(filename) + yield file + except: + file = None + finally: + if file: + file.close() + + +class DesinventarTransformer(MontyDataTransformer): + data_source: DesinventarDataSource + hazard_profiles = HazardProfiles() + + def __init__(self, data_source: DesinventarDataSource) -> None: + super().__init__('desiventar') + self.data_source = data_source + + def create_datetimes(self, row: DataRow) -> datetime | None: + start_year = strtoi(row["year"], None) + start_month = strtoi(row["month"], 1) + start_day = strtoi(row["day"], 1) + + if start_year and start_month and start_day: + # Note: the int cast here is safe + start_dt = datetime(int(start_year), int(start_month), int(start_day)) + return pytz.utc.localize(start_dt) + + return None + + def create_event_item_from_row(self, row: DataRow) -> Optional[Item]: + if not row['serial']: + return None + + start_date = self.create_datetimes(row) + + item = Item( + id=f"{STAC_EVENT_ID_PREFIX}{row['serial']}", + # FIXME: typings + geometry=row['geometry'], + # FIXME: typings + bbox=row['bbox'], + datetime=start_date, + start_datetime=start_date, + properties={ + 'title': f"{row['event']} in {row['location']}" + }, + ) + + MontyExtension.add_to(item) + monty = MontyExtension.ext(item) + monty.episode_number = 1 # Desinventar doesn't have episodes + + if row['event']: + # TODO: map event to standard codes + monty.hazard_codes = [row['event']] + + # TODO: map country to standard codes + monty.country_codes = row['iso3'] + + monty.compute_and_set_correlation_id(hazard_profiles=self.hazard_profiles) + + # TODO: set collection and roles + # TODO: add source link + + return item + + def create_hazard_item_from_row(self, row: DataRow) -> Optional[Item]: + pass + + def get_level_maps(self, root: etree._Element): + level_maps = root.xpath("//level_maps/TR") + + class GeoDataEntry(TypedDict): + level: Optional[str] + property_code: Optional[str] + shapefile_data: Optional[gpd.GeoDataFrame] + + geo_data: Dict[str, GeoDataEntry] = {} + + for level_row in level_maps: + file_path = get_list_item_safe(level_row.xpath("filename/text()"), 0) + level = get_list_item_safe(level_row.xpath("map_level/text()"), 0) + property_code = get_list_item_safe(level_row.xpath("lev_code/text()"), 0) + + if (file_path is not None): + shp_file_name = Path(str(file_path)).name + shapefile_data = gpd.read_file(f"zip://{self.data_source.zf_ref.filename}/{shp_file_name}") + else: + shapefile_data = None + + geo_data[f"level{level}"] = { + 'level': str(level) if level is not None else None, + 'property_code': str(property_code) if property_code is not None else None, + 'shapefile_data': shapefile_data + } + + return geo_data + + def load_data(self): + with self.data_source.with_xml_file() as xml_file: + tree = etree.parse(xml_file) + root = tree.getroot() + + # TODO: calculate it dynamically + geo_data = self.get_level_maps(root) + applicable_geo_levels = list(geo_data.keys()) + applicable_geo_levels.sort(key=natural_keys, reverse=True) + + events = root.xpath("//fichas/TR") + data = [] + + def extract_value(obj: etree._Element, key: str, default: str | None = None): + value = get_list_item_safe(obj.xpath(f"{key}/text()"), 0) + + return str(value) if value is not None else (default or value) + + for event_row in events: + serial = extract_value(event_row, 'serial') + + if (serial is None): + continue + + row_data: DataRow = { + 'serial': serial, + 'deaths': extract_value(event_row, 'muertos'), + 'flag_deaths': extract_value(event_row, 'hay_muertos'), + 'injured': extract_value(event_row, 'heridos'), + 'flag_injured': extract_value(event_row, 'hay_heridos'), + 'missing': extract_value(event_row, 'desaparece'), + 'flag_missing': extract_value(event_row, 'hay_deasparece'), + 'houses_destroyed': extract_value(event_row, 'vivdest'), + 'flag_houses_destroyed': extract_value(event_row, 'hay_vivdest'), + 'houses_damaged': extract_value(event_row, 'vivafec'), + 'flag_houses_damaged': extract_value(event_row, 'hay_vivafec'), + 'directly_affected': extract_value(event_row, 'damnificados'), + 'flag_directly_affected': extract_value(event_row, 'hay_damnificados'), + 'indirectly_affected': extract_value(event_row, 'afectados'), + 'flag_indirectly_affected': extract_value(event_row, 'hay_afectados'), + 'relocated': extract_value(event_row, 'reubicados'), + 'flag_relocated': extract_value(event_row, 'hay_reubicados'), + 'evacuated': extract_value(event_row, 'evacuados'), + 'flag_evacuated': extract_value(event_row, 'hay_evacuados'), + 'losses_in_dollar': extract_value(event_row, 'valorus'), + 'losses_local_currency': extract_value(event_row, 'valorloc'), + 'education_centers': extract_value(event_row, 'nescuelas'), + 'hospitals': extract_value(event_row, 'nhospitales'), + 'damages_in_crops_ha': extract_value(event_row, 'nhectareas'), + 'lost_cattle': extract_value(event_row, 'cabezas'), + 'damages_in_roads_mts': extract_value(event_row, 'kmvias'), + 'level0': extract_value(event_row, 'level0'), + 'level1': extract_value(event_row, 'level1'), + 'level2': extract_value(event_row, 'level2'), + 'name0': extract_value(event_row, 'name0'), + 'name1': extract_value(event_row, 'name1'), + 'name2': extract_value(event_row, 'name2'), + 'latitude': extract_value(event_row, 'latitude'), + 'longitude': extract_value(event_row, 'longitude'), + 'event': extract_value(event_row, 'evento'), + 'glide': extract_value(event_row, 'glide'), + 'location': extract_value(event_row, 'lugar'), + 'haz_maxvalue': extract_value(event_row, 'magnitud2'), + 'duration': extract_value(event_row, 'duracion'), + 'year': extract_value(event_row, 'fechano'), + 'month': extract_value(event_row, 'fechames'), + 'day': extract_value(event_row, 'fechadia'), + + 'geometry': None, + 'bbox': None, + + 'desinventar_country_code': self.data_source.country_code, + 'iso3': self.data_source.iso3 + } + + level = get_lowest_level(applicable_geo_levels, row_data) + if (level is not None): + gfd = geo_data[level]['shapefile_data'] + + if gfd is not None: + code = geo_data[level]['property_code'] + filtered_gfd = gfd[gfd[code] == row_data[level]].copy() + + if isinstance(filtered_gfd, gpd.GeoDataFrame): + # Use a tolerance value for simplification (smaller values will keep more detail) + filtered_gfd['geometry'] = filtered_gfd['geometry'].apply( + lambda geom: geom.simplify(tolerance=0.01, preserve_topology=True) + ) + + row_data['geometry'] = filtered_gfd.to_dict() + row_data['bbox'] = filtered_gfd.bounds.to_dict() + + data.append(row_data) + + return data diff --git a/tests/extensions/test_desinventar.py b/tests/extensions/test_desinventar.py new file mode 100644 index 0000000..fac2719 --- /dev/null +++ b/tests/extensions/test_desinventar.py @@ -0,0 +1,58 @@ +import json +from os import makedirs +from typing import TypedDict +from unittest import TestCase +from parameterized import parameterized +from pystac_monty.sources.desinventar import ( + DesinventarDataSource, + DesinventarTransformer +) +from tests.conftest import get_data_file + +class DesinventarData(TypedDict): + zip_file_path: str + country_code: str + iso3: str + +class DesinventarScenario(TypedDict): + name: str + data: DesinventarData + +nepal_data: DesinventarScenario = { + 'name': "Nepal data", + 'data': { + 'zip_file_path': '/tmp/DI_export_npl.zip', + 'country_code': 'npl', + 'iso3': 'npl', + } +} + +def load_scenarios( + scenarios: list[DesinventarScenario], +) -> list[DesinventarTransformer]: + transformers = [] + for scenario in scenarios: + data = scenario['data'] + data_source = DesinventarDataSource.from_path(data['zip_file_path'], data['country_code'], data['iso3']) + + transformers.append( + DesinventarTransformer(data_source) + ) + return transformers + +class DesinventarTest(TestCase): + scenarios = [nepal_data] + + def setUp(self) -> None: + super().setUp() + + # Create temporary folder for test outputs + makedirs(get_data_file("temp/desinventar"), exist_ok=True) + + @parameterized.expand(load_scenarios(scenarios)) + def test_transformer(self, transformer: DesinventarTransformer) -> None: + data = transformer.load_data() + + item_path = get_data_file(f"temp/desinventar/npl.json") + with open(item_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) diff --git a/uv.lock b/uv.lock index 6807262..208510d 100644 --- a/uv.lock +++ b/uv.lock @@ -464,6 +464,15 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] +[[package]] +name = "cssselect" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/91/d51202cc41fbfca7fa332f43a5adac4b253962588c7cc5a54824b019081c/cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc", size = 41423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/a9/2da08717a6862c48f1d61ef957a7bba171e7eefa6c0aa0ceb96a140c2a6b/cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e", size = 18687 }, +] + [[package]] name = "debugpy" version = "1.8.11" @@ -586,15 +595,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, ] -[[package]] -name = "fqdn" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, -] - [[package]] name = "fiona" version = "1.10.1" @@ -626,6 +626,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/14/5ef47002ef19bd5cfbc7a74b21c30ef83f22beb80609314ce0328989ceda/fiona-1.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:15751c90e29cee1e01fcfedf42ab85987e32f0b593cf98d88ed52199ef5ca623", size = 24461486 }, ] +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, +] + [[package]] name = "geographiclib" version = "2.0" @@ -644,27 +653,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/1b/4f57660aa148d3e3043d048b7e1ab87dfeb85204d0fdb5b4e19c08202162/geojson-3.1.0-py3-none-any.whl", hash = "sha256:68a9771827237adb8c0c71f8527509c8f5bef61733aa434cefc9c9d4f0ebe8f3", size = 15044 }, ] -[[package]] -name = "ghp-import" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, -] - -[[package]] -name = "h11" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, -] - [[package]] name = "geopandas" version = "1.0.1" @@ -694,6 +682,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/15/cf2a69ade4b194aa524ac75112d5caac37414b20a3a03e6865dfe0bd1539/geopy-2.4.1-py3-none-any.whl", hash = "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7", size = 125437 }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + [[package]] name = "html5lib" version = "1.1" @@ -767,7 +776,7 @@ name = "ipykernel" version = "6.29.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "platform_system == 'Darwin'" }, + { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -1126,6 +1135,88 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/93/858e87edc634d628e5d752ba944c2833133a28fa87bb093e6832ced36a3e/jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54", size = 214392 }, ] +[[package]] +name = "lxml" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ce/2789e39eddf2b13fac29878bfa465f0910eb6b0096e29090e5176bc8cf43/lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656", size = 8124570 }, + { url = "https://files.pythonhosted.org/packages/24/a8/f4010166a25d41715527129af2675981a50d3bbf7df09c5d9ab8ca24fbf9/lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d", size = 4413042 }, + { url = "https://files.pythonhosted.org/packages/41/a4/7e45756cecdd7577ddf67a68b69c1db0f5ddbf0c9f65021ee769165ffc5a/lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a", size = 5139213 }, + { url = "https://files.pythonhosted.org/packages/02/e2/ecf845b12323c92748077e1818b64e8b4dba509a4cb12920b3762ebe7552/lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8", size = 4838814 }, + { url = "https://files.pythonhosted.org/packages/12/91/619f9fb72cf75e9ceb8700706f7276f23995f6ad757e6d400fbe35ca4990/lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330", size = 5425084 }, + { url = "https://files.pythonhosted.org/packages/25/3b/162a85a8f0fd2a3032ec3f936636911c6e9523a8e263fffcfd581ce98b54/lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965", size = 4875993 }, + { url = "https://files.pythonhosted.org/packages/43/af/dd3f58cc7d946da6ae42909629a2b1d5dd2d1b583334d4af9396697d6863/lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22", size = 5012462 }, + { url = "https://files.pythonhosted.org/packages/69/c1/5ea46b2d4c98f5bf5c83fffab8a0ad293c9bc74df9ecfbafef10f77f7201/lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b", size = 4815288 }, + { url = "https://files.pythonhosted.org/packages/1d/51/a0acca077ad35da458f4d3f729ef98effd2b90f003440d35fc36323f8ae6/lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7", size = 5472435 }, + { url = "https://files.pythonhosted.org/packages/4d/6b/0989c9368986961a6b0f55b46c80404c4b758417acdb6d87bfc3bd5f4967/lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8", size = 4976354 }, + { url = "https://files.pythonhosted.org/packages/05/9e/87492d03ff604fbf656ed2bf3e2e8d28f5d58ea1f00ff27ac27b06509079/lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32", size = 5029973 }, + { url = "https://files.pythonhosted.org/packages/f9/cc/9ae1baf5472af88e19e2c454b3710c1be9ecafb20eb474eeabcd88a055d2/lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86", size = 4888837 }, + { url = "https://files.pythonhosted.org/packages/d2/10/5594ffaec8c120d75b17e3ad23439b740a51549a9b5fd7484b2179adfe8f/lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5", size = 5530555 }, + { url = "https://files.pythonhosted.org/packages/ea/9b/de17f05377c8833343b629905571fb06cff2028f15a6f58ae2267662e341/lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03", size = 5405314 }, + { url = "https://files.pythonhosted.org/packages/8a/b4/227be0f1f3cca8255925985164c3838b8b36e441ff0cc10c1d3c6bdba031/lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7", size = 5079303 }, + { url = "https://files.pythonhosted.org/packages/5c/ee/19abcebb7fc40319bb71cd6adefa1ad94d09b5660228715854d6cc420713/lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80", size = 3475126 }, + { url = "https://files.pythonhosted.org/packages/a1/35/183d32551447e280032b2331738cd850da435a42f850b71ebeaab42c1313/lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3", size = 3805065 }, + { url = "https://files.pythonhosted.org/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056 }, + { url = "https://files.pythonhosted.org/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238 }, + { url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 }, + { url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 }, + { url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 }, + { url = "https://files.pythonhosted.org/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", size = 4866657 }, + { url = "https://files.pythonhosted.org/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", size = 4967017 }, + { url = "https://files.pythonhosted.org/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", size = 4810730 }, + { url = "https://files.pythonhosted.org/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", size = 5455154 }, + { url = "https://files.pythonhosted.org/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", size = 4969416 }, + { url = "https://files.pythonhosted.org/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", size = 5013672 }, + { url = "https://files.pythonhosted.org/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", size = 4878644 }, + { url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 }, + { url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 }, + { url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 }, + { url = "https://files.pythonhosted.org/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226 }, + { url = "https://files.pythonhosted.org/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753 }, + { url = "https://files.pythonhosted.org/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955 }, + { url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 }, + { url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 }, + { url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 }, + { url = "https://files.pythonhosted.org/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", size = 4813963 }, + { url = "https://files.pythonhosted.org/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", size = 4923353 }, + { url = "https://files.pythonhosted.org/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", size = 4740541 }, + { url = "https://files.pythonhosted.org/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", size = 5346504 }, + { url = "https://files.pythonhosted.org/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", size = 4898077 }, + { url = "https://files.pythonhosted.org/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", size = 4946543 }, + { url = "https://files.pythonhosted.org/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", size = 4816841 }, + { url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 }, + { url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 }, + { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 }, + { url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 }, + { url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 }, + { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 }, + { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 }, + { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, + { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, + { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 }, + { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 }, + { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 }, + { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 }, + { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 }, + { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 }, + { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, + { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, + { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, + { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 }, + { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 }, + { url = "https://files.pythonhosted.org/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", size = 3925431 }, + { url = "https://files.pythonhosted.org/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", size = 4216683 }, + { url = "https://files.pythonhosted.org/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", size = 4326732 }, + { url = "https://files.pythonhosted.org/packages/68/de/96cb6d3269bc994b4f5ede8ca7bf0840f5de0a278bc6e50cb317ff71cafa/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce", size = 4218377 }, + { url = "https://files.pythonhosted.org/packages/a5/43/19b1ef6cbffa4244a217f95cc5f41a6cb4720fed33510a49670b03c5f1a0/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83", size = 4351237 }, + { url = "https://files.pythonhosted.org/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", size = 3487557 }, +] + [[package]] name = "markdown" version = "3.7" @@ -1245,7 +1336,7 @@ version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "ghp-import" }, { name = "jinja2" }, { name = "markdown" }, @@ -1925,15 +2016,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, ] -[[package]] -name = "pyparsing" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, -] - [[package]] name = "pyogrio" version = "0.10.0" @@ -1971,6 +2053,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/5d/0deb16d228362a097ee3258d0a887c9c0add4b9678bb4847b08a241e124d/pyogrio-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:02e54bcfb305af75f829044b0045f74de31b77c2d6546f7aaf96822066147848", size = 16158260 }, ] +[[package]] +name = "pyparsing" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, +] + [[package]] name = "pyproj" version = "3.7.0" @@ -2024,12 +2115,14 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "geojson" }, + { name = "geopandas" }, + { name = "lxml" }, { name = "markdownify" }, { name = "pandas" }, { name = "pystac" }, { name = "python-dateutil" }, { name = "pytz" }, - { name = "shapely" }, + { name = "types-lxml" }, ] [package.dev-dependencies] @@ -2067,12 +2160,14 @@ dev = [ [package.metadata] requires-dist = [ { name = "geojson", specifier = ">=2.5.0" }, + { name = "geopandas", specifier = ">=1.0.1" }, + { name = "lxml", specifier = ">=5.3.0" }, { name = "markdownify", specifier = ">=0.14.1" }, { name = "pandas", specifier = ">=2.2.0" }, { name = "pystac", specifier = ">=1.11.0" }, { name = "python-dateutil", specifier = ">=2.7.0" }, { name = "pytz", specifier = ">=2021.1" }, - { name = "shapely", specifier = ">=2.0.0" }, + { name = "types-lxml", specifier = ">=2024.12.13" }, ] [package.metadata.requires-dev] @@ -2787,6 +2882,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, ] +[[package]] +name = "types-beautifulsoup4" +version = "4.12.0.20241020" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-html5lib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/ae/5a7571c649cdd9f3c07d16790467a4fe1191f12a3ad7eecd1097cb8b1d9f/types-beautifulsoup4-4.12.0.20241020.tar.gz", hash = "sha256:158370d08d0cd448bd11b132a50ff5279237a5d4b5837beba074de152a513059", size = 11682 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/43/0f96cdf27d7da7dea729af3476b7be997205765209651a42a4e1895bab72/types_beautifulsoup4-4.12.0.20241020-py3-none-any.whl", hash = "sha256:c95e66ce15a4f5f0835f7fbc5cd886321ae8294f977c495424eaf4225307fd30", size = 12170 }, +] + [[package]] name = "types-html5lib" version = "1.1.11.20241018" @@ -2808,6 +2915,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/6f/8acab5c921096a52bd26888d5155a36cd2ff7f820b4fbb4e36c662e9a32e/types_jsonschema-4.23.0.20240813-py3-none-any.whl", hash = "sha256:be283e23f0b87547316c2ee6b0fd36d95ea30e921db06478029e10b5b6aa6ac3", size = 10314 }, ] +[[package]] +name = "types-lxml" +version = "2024.12.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cssselect" }, + { name = "types-beautifulsoup4" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/ed/1a5bf2be373e9c4a27c8e97ba8b6dc22c3bccdcf7af9851a058c5234fdd0/types_lxml-2024.12.13.tar.gz", hash = "sha256:e2dadb92c7f730cd369daf1efe93ebc2ebfa8b692d4415cfc91b727419152e37", size = 130209 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/dc/eaa78d32ac5520383180ee9f4133969d80f2c3841b05662d913bd3033891/types_lxml-2024.12.13-py3-none-any.whl", hash = "sha256:d4830c99ef6f7b9eae176297a2b8dc840b3a75986bf4449592ca09a9a449b27e", size = 86008 }, +] + [[package]] name = "types-orjson" version = "3.6.2"