From 8a57bc8c4074e86fe8e714db5c07dceeed4894e9 Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:09:36 +0530 Subject: [PATCH 01/27] V0.1.1 (#58) --- .../local_data_platform/__init__.py | 2 +- .../catalog/local/iceberg/__init__.py | 4 +- .../format/csv/__init__.py | 5 +- .../format/iceberg/__init__.py | 28 +++-- .../format/parquet/__init__.py | 6 +- .../local_data_platform/logger.py | 5 +- .../local_data_platform/pipeline/__init__.py | 2 - .../egression/iceberg_to_csv/__init__.py | 2 +- .../pipeline/ingestion/__init__.py | 11 +- .../ingestion/csv_to_iceberg/__init__.py | 3 +- .../ingestion/parquet_to_iceberg/__init__.py | 2 +- .../store/source/gcp/__init__.py | 2 +- .../near_data_lake/reports/get_data.py | 60 ++++++++++ .../put_data.py} | 55 +-------- .../config/egression.json | 6 +- .../config/ingestion.json | 6 +- .../monthly_reporting.md | 23 ++++ .../monthly_reporting.py | 110 ------------------ .../reports/get_data.py | 51 ++++++++ .../reports/put_data.py | 51 ++++++++ 20 files changed, 236 insertions(+), 198 deletions(-) create mode 100644 local-data-platform/real_world_use_cases/near_data_lake/reports/get_data.py rename local-data-platform/real_world_use_cases/near_data_lake/{monthly_reporting.py => reports/put_data.py} (53%) create mode 100644 local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.md delete mode 100644 local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.py create mode 100644 local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/get_data.py create mode 100644 local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/put_data.py diff --git a/local-data-platform/local_data_platform/__init__.py b/local-data-platform/local_data_platform/__init__.py index a2e81dd..a47e6b1 100644 --- a/local-data-platform/local_data_platform/__init__.py +++ b/local-data-platform/local_data_platform/__init__.py @@ -39,7 +39,7 @@ class Table(Base): def __init__(self, name: str, path: Path = os.getcwd()): self.name = name - self.path = path + self.path = os.getcwd()+path def get(self): raise TableNotFound( diff --git a/local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py b/local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py index 2bdd147..caf0e96 100644 --- a/local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py +++ b/local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py @@ -30,10 +30,10 @@ class LocalIcebergCatalog(SqlCatalog): def __init__(self, name: str, path: str, *args, **kwargs): self.name = name - self.uri = f"sqlite:///{path}/{name}.db" + self.uri = f"sqlite:///{path}/{name}_catalog.db" self.warehouse = f"file://{path}" try: - logger.error(f"Initializing LocalIcebergCatalog with {self.uri}") + logger.info(f"Initializing LocalIcebergCatalog with {self.uri}") super().__init__(*args, **kwargs, **self.__dict__) except Exception as e: logger.error(f"Failed to initialize LocalIcebergCatalog {e}") diff --git a/local-data-platform/local_data_platform/format/csv/__init__.py b/local-data-platform/local_data_platform/format/csv/__init__.py index 51ad4d0..af809bf 100644 --- a/local-data-platform/local_data_platform/format/csv/__init__.py +++ b/local-data-platform/local_data_platform/format/csv/__init__.py @@ -17,6 +17,7 @@ def __init__(self, *args, **kwargs): def get(self) -> Table: if not os.path.isfile(self.path): + logger.error(f"This path {self.path} is invalid") raise FileNotFoundError logger.info( @@ -24,10 +25,10 @@ def get(self) -> Table: reading CSV from {self.path} """ ) - df = csv.read_table(self.path) + df = csv.read_csv(self.path) logger.info( f""" - df type {type(df)} + df type {type(df)} len {len(df)} """ ) if df is not None: diff --git a/local-data-platform/local_data_platform/format/iceberg/__init__.py b/local-data-platform/local_data_platform/format/iceberg/__init__.py index 5b81d08..b8f5be2 100644 --- a/local-data-platform/local_data_platform/format/iceberg/__init__.py +++ b/local-data-platform/local_data_platform/format/iceberg/__init__.py @@ -4,6 +4,10 @@ from pyiceberg.typedef import Identifier from pyarrow import Table from local_data_platform.logger import log +import os + +os.environ['PYICEBERG_DOWNCAST_NS_TIMESTAMP_TO_US_ON_WRITE'] = 'true' + logger = log() @@ -21,20 +25,21 @@ class Iceberg(Format): Methods: __init__(catalog: str, *args, **kwargs): Initializes the Iceberg instance with the given catalog and metadata. - + put(df: Table) -> Table: Writes the given data frame to the Iceberg table. - + get(): Fetches data from the Iceberg table and returns it as an Arrow table. """ - def __init__(self, catalog: str, *args, **kwargs): - logger.info(f"Iceberg catalog : {catalog}") - self.catalog_identifier = catalog["identifier"] + def __init__(self, config: str, *args, **kwargs): + logger.info(f"Iceberg catalog : {config}") + self.catalog_identifier = config["identifier"] self.catalog = LocalIcebergCatalog( - self.catalog_identifier, path=catalog["warehouse_path"] + self.catalog_identifier, path=config["warehouse_path"] ) - self.catalog.create_namespace(self.catalog_identifier) + if not self.catalog._namespace_exists(self.catalog_identifier): + self.catalog.create_namespace(self.catalog_identifier) self.identifier = f"{self.catalog_identifier}.{kwargs['name']}" self.metadata = kwargs logger.info(f"Iceberg created with catalog namespace {self.catalog_identifier}") @@ -42,14 +47,19 @@ def __init__(self, catalog: str, *args, **kwargs): super().__init__(*args, **kwargs) def put(self, df: Table) -> Table: + if not df: + logger.error(f"While doing put in Iceberg Format we got df as None") + raise Exception(f" Got Table as non") logger.info(f"self.identifier {self.identifier}") logger.info( f""" - Writing {len(df)} to Iceberg Table {self.identifier} + Writing type {type(df)} of length {len(df)} to Iceberg Table {self.identifier} """ ) table = self.catalog.create_table_if_not_exists( - identifier=self.identifier, schema=df.schema + identifier=self.identifier, schema=df.schema, properties={ + "downcast-ns-timestamp-to-us-on-write": True # Set property for downcasting + } ) table.append(df) return table diff --git a/local-data-platform/local_data_platform/format/parquet/__init__.py b/local-data-platform/local_data_platform/format/parquet/__init__.py index e54c093..18c3beb 100644 --- a/local-data-platform/local_data_platform/format/parquet/__init__.py +++ b/local-data-platform/local_data_platform/format/parquet/__init__.py @@ -16,14 +16,14 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def get(self) -> Table: - if not os.path.isfile(self.path): - raise FileNotFoundError - logger.info( f""" reading parquet from {self.path} """ ) + if not os.path.isfile(self.path): + raise FileNotFoundError + df = parquet.read_table(self.path) logger.info( f""" diff --git a/local-data-platform/local_data_platform/logger.py b/local-data-platform/local_data_platform/logger.py index 5a00313..ef690b6 100644 --- a/local-data-platform/local_data_platform/logger.py +++ b/local-data-platform/local_data_platform/logger.py @@ -8,10 +8,7 @@ def log(): basicConfig(level=INFO, format= """ - %(filename)s - %(funcName)s - - %(asctime)s - %(name)s - - %(levelname)s - - message : %(message)s + %(message)s """ ) diff --git a/local-data-platform/local_data_platform/pipeline/__init__.py b/local-data-platform/local_data_platform/pipeline/__init__.py index e231632..9850c64 100644 --- a/local-data-platform/local_data_platform/pipeline/__init__.py +++ b/local-data-platform/local_data_platform/pipeline/__init__.py @@ -11,7 +11,5 @@ class Pipeline(Flow): def __init__(self, config: Config, *args, **kwargs): self.config = config - # self.source = Source(**config.metadata['source']) - # self.target = Target(**config.metadata['target']) super().__init__(*args, **kwargs) diff --git a/local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py b/local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py index 39d7b2c..abdd157 100644 --- a/local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py +++ b/local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py @@ -36,7 +36,7 @@ def __init__(self, config: Config, *args, **kwargs): self.source = config.metadata["source"] self.target = config.metadata["target"] self.target = CSV(name=self.target["name"], path=self.target["path"]) - self.source = Iceberg(name=self.source["name"], catalog=self.source["catalog"]) + self.source = Iceberg(name=self.source["name"], config=self.source["catalog"]) logger.info( f""" IcebergToCSV initialised with diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/__init__.py b/local-data-platform/local_data_platform/pipeline/ingestion/__init__.py index 0f39f3d..c738363 100644 --- a/local-data-platform/local_data_platform/pipeline/ingestion/__init__.py +++ b/local-data-platform/local_data_platform/pipeline/ingestion/__init__.py @@ -1,11 +1,16 @@ from local_data_platform.pipeline import Pipeline +from local_data_platform.logger import log +logger = log() -class Ingestion(Pipeline): +class Ingestion(Pipeline): def extract(self): - self.source.get() + logger.info("Extracting Source in ingestion pipeline") + return self.source.get() def load(self): - self.target.put(self.extract()) + df = self.extract() + logger.info(f"Loading Source {len(df)} in ingestion pipeline") + self.target.put(df) diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py b/local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py index 9965f7f..843a5ba 100644 --- a/local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py +++ b/local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py @@ -5,6 +5,7 @@ logger = log() + class CSVToIceberg(Ingestion): """ CSVToIceberg is a class responsible for ingesting data from a CSV source and @@ -39,7 +40,7 @@ def __init__(self, config, *args, **kwargs): ) self.target = Iceberg( name=self.target['name'], - catalog=self.target['catalog'] + config=self.target['catalog'] ) logger.info( f""" diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py b/local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py index ce626df..2e6cbe6 100644 --- a/local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py +++ b/local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py @@ -34,7 +34,7 @@ def __init__(self, config: Config, *args, **kwargs): ) self.target = Iceberg( name=self.target['name'], - catalog=self.target['catalog'] + config=self.target['catalog'] ) logger.info( f""" diff --git a/local-data-platform/local_data_platform/store/source/gcp/__init__.py b/local-data-platform/local_data_platform/store/source/gcp/__init__.py index 6e6f731..8c0992a 100644 --- a/local-data-platform/local_data_platform/store/source/gcp/__init__.py +++ b/local-data-platform/local_data_platform/store/source/gcp/__init__.py @@ -2,7 +2,7 @@ from pathlib import Path import json from local_data_platform import Credentials -from local_data_platform import logger +from local_data_platform.logger import log logger = log() diff --git a/local-data-platform/real_world_use_cases/near_data_lake/reports/get_data.py b/local-data-platform/real_world_use_cases/near_data_lake/reports/get_data.py new file mode 100644 index 0000000..250d392 --- /dev/null +++ b/local-data-platform/real_world_use_cases/near_data_lake/reports/get_data.py @@ -0,0 +1,60 @@ +from local_data_platform.pipeline.ingestion.bigquery_to_csv import BigQueryToCSV +from local_data_platform import Config, SupportedFormat, SupportedEngine +from local_data_platform.store.source.json import Json +from local_data_platform.exceptions import PipelineNotFound +import os +from local_data_platform.logger import log + + +logger = log() + + +def get_near_transaction_dataset( + dataset="near_transactions", + config_path="/real_world_use_cases/near_data_lake/config/ingestion.json", +): + """ + Retrieves and processes the near transaction dataset based on the provided configuration. + + Args: + dataset (str): The name of the dataset to be processed. Defaults to "near_transactions". + config_path (str): The path to the configuration file. Defaults to "/real_world_use_cases/near_data_lake/config/ingestion.json". + + Raises: + PipelineNotFound: If the source and target formats specified in the configuration are not supported. + + Returns: + None + """ + + config = Config( + **Json( + name=dataset, + path=config_path, + ).get() + ) + print(config) + logger.info( + f""" + We are using the following dictionary as the configuration to generate a monthly trust metric + {config} + """ + ) + if ( + config.metadata["source"]["format"] == SupportedFormat.JSON.value + and config.metadata["target"]["format"] == SupportedFormat.CSV.value + and config.metadata["source"]["engine"] == SupportedEngine.BIGQUERY.value + ): + data_loader = BigQueryToCSV(config=config) + data_loader.load() + else: + raise PipelineNotFound( + f""" + source {config.metadata['source']['format']} + to target {config.metadata['target']['format']} + pipeline is not supported yet + """ + ) + + +get_near_transaction_dataset() diff --git a/local-data-platform/real_world_use_cases/near_data_lake/monthly_reporting.py b/local-data-platform/real_world_use_cases/near_data_lake/reports/put_data.py similarity index 53% rename from local-data-platform/real_world_use_cases/near_data_lake/monthly_reporting.py rename to local-data-platform/real_world_use_cases/near_data_lake/reports/put_data.py index 00bac1e..e0c2d1b 100644 --- a/local-data-platform/real_world_use_cases/near_data_lake/monthly_reporting.py +++ b/local-data-platform/real_world_use_cases/near_data_lake/reports/put_data.py @@ -1,4 +1,4 @@ -from local_data_platform.pipeline.egression.csv_to_iceberg import CSVToIceberg +from local_data_platform.pipeline.ingestion.csv_to_iceberg import CSVToIceberg from local_data_platform.pipeline.ingestion.bigquery_to_csv import BigQueryToCSV from local_data_platform import Config, SupportedFormat, SupportedEngine from local_data_platform.store.source.json import Json @@ -10,54 +10,6 @@ logger = log() -def get_near_trasaction_dataset( - dataset="near_transactions", - config_path="/real_world_use_cases/near_data_lake/config/ingestion.json", -): - """ - Retrieves and processes the near transaction dataset based on the provided configuration. - - Args: - dataset (str): The name of the dataset to be processed. Defaults to "near_transactions". - config_path (str): The path to the configuration file. Defaults to "/real_world_use_cases/near_data_lake/config/ingestion.json". - - Raises: - PipelineNotFound: If the source and target formats specified in the configuration are not supported. - - Returns: - None - """ - - config = Config( - **Json( - name=dataset, - path=os.getcwd() + config_path, - ).get() - ) - print(config) - logger.info( - f""" - We are using the following dictionary as the configuration to generate a monthly trust metric - {config} - """ - ) - if ( - config.metadata["source"]["format"] == SupportedFormat.JSON.value - and config.metadata["target"]["format"] == SupportedFormat.CSV.value - and config.metadata["source"]["engine"] == SupportedEngine.BIGQUERY.value - ): - data_loader = BigQueryToCSV(config=config) - data_loader.load() - else: - raise PipelineNotFound( - f""" - source {config.metadata['source']['format']} - to target {config.metadata['target']['format']} - pipeline is not supported yet - """ - ) - - def put_near_trasaction_dataset( dataset="near_transactions", config_path="/real_world_use_cases/near_data_lake/config/egression.json", @@ -75,11 +27,11 @@ def put_near_trasaction_dataset( Raises: PipelineNotFound: If the source and target formats specified in the configuration are not supported. - """ + """ config = Config( **Json( name=dataset, - path=os.getcwd() + config_path, + path=config_path, ).get() ) @@ -105,5 +57,4 @@ def put_near_trasaction_dataset( ) -# get_near_trasaction_dataset(); put_near_trasaction_dataset() diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json index abc3f3a..c1bb509 100644 --- a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json +++ b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json @@ -11,15 +11,15 @@ "target": { "name": "rides", "format": "CSV", - "path": "/Users/tushar/Documents/GitHub/local-data-platform/local-data-platform/yellow_tripdata_2023-01.csv" + "path": "/real_world_use_cases/nyc_yellow_taxi_dataset/data/nyc_yellow_taxi_rides.csv" }, "source": { "name": "rides", "format": "ICEBERG", "catalog": { "type": "LocalIceberg", - "identifier": "pyiceberg_catalog_db", - "warehouse_path": "./tmp/warehouse" + "identifier": "nyc_yellow_taxi_dataset", + "warehouse_path": "real_world_use_cases/nyc_yellow_taxi_dataset/data" } } } diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json index b961760..dffa0c3 100644 --- a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json +++ b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json @@ -11,7 +11,7 @@ "source": { "name": "rides", "format": "PARQUET", - "path": "/Users/tushar/Documents/GitHub/local-data-platform/local-data-platform/yellow_tripdata_2023-01.parquet" + "path": "/yellow_tripdata_2023-01.parquet" }, "target": { "name": "rides", @@ -19,8 +19,8 @@ "path": "yellow_tripdata_2023-01.parquet", "catalog": { "type": "LocalIceberg", - "identifier": "pyiceberg_catalog_db", - "warehouse_path": "./tmp/warehouse" + "identifier": "nyc_yellow_taxi_dataset", + "warehouse_path": "real_world_use_cases/nyc_yellow_taxi_dataset/data" } } } diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.md b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.md new file mode 100644 index 0000000..5632dfc --- /dev/null +++ b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.md @@ -0,0 +1,23 @@ +''' +New York Taxi and Limousine Commission +TLC Trip Record Data + +Yellow and green taxi trip records include fields capturing pick-up and drop-off dates/times, +pick-up and drop-off locations, trip distances, itemized fares, rate types, payment types, +and driver-reported passenger counts. + +For-Hire Vehicle (“FHV”) trip records include fields capturing the dispatching base license number and the pick-up date, +time, and taxi zone location ID (shape file below). + +All files will be stored in the PARQUET format. +Trip data will be published monthly (with two months delay) instead of bi-annually. +HVFHV files will now include 17 more columns (please see High Volume FHV Trips Dictionary for details). +Additional columns will be added to the old files as well. +''' + + + + + + + diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.py b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.py deleted file mode 100644 index 5951c77..0000000 --- a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/monthly_reporting.py +++ /dev/null @@ -1,110 +0,0 @@ -''' -New York Taxi and Limousine Commission -TLC Trip Record Data - -Yellow and green taxi trip records include fields capturing pick-up and drop-off dates/times, -pick-up and drop-off locations, trip distances, itemized fares, rate types, payment types, -and driver-reported passenger counts. - -For-Hire Vehicle (“FHV”) trip records include fields capturing the dispatching base license number and the pick-up date, -time, and taxi zone location ID (shape file below). - -All files will be stored in the PARQUET format. -Trip data will be published monthly (with two months delay) instead of bi-annually. -HVFHV files will now include 17 more columns (please see High Volume FHV Trips Dictionary for details). -Additional columns will be added to the old files as well. -''' -from local_data_platform.pipeline.ingestion.parquet_to_iceberg import ParquetToIceberg -from local_data_platform.pipeline.egression.iceberg_to_csv import IcebergToCSV -from local_data_platform import Config, SupportedFormat -from local_data_platform.store.source.json import Json -from local_data_platform.exceptions import PipelineNotFound -import os -from local_data_platform.logger import log - - -logger = log() - - -def get_nyc_yellow_taxi_dataset( - dataset='nyc_taxi', - config_path='/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json' -): - logger.info( - """ - We will try to read a ICEBERG table from local catalog - """ - ) - - config = Config( - **Json( - name=dataset, - path=os.getcwd()+config_path, - ).get() - ) - - logger.info( - f""" - We are using the following dictionary as the configuration to generate a monthly trust metric - {config} - """ - ) - if ( - config.metadata['source']['format'] == SupportedFormat.ICEBERG.value and - config.metadata['target']['format'] == SupportedFormat.CSV.value - ): - data_loader = IcebergToCSV(config=config) - data_loader.load() - else: - raise PipelineNotFound( - f""" - source {config.metadata['source']['format']} - to target {config.metadata['target']['format']} - pipeline is not supported yet - """ - ) - - - -def put_nyc_yellow_taxi_dataset( - dataset='nyc_taxi', - config_path='/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json' -): - logger.info( - """ - We will try to read a PARQUET file downloaded from : - https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page - """ - ) - - config = Config( - **Json( - name=dataset, - path=os.getcwd()+config_path, - ).get() - ) - - logger.info( - f""" - We are using the following dictionary as the configuration to generate a monthly trust metric - {config} - """ - ) - if ( - config.metadata['source']['format'] == SupportedFormat.PARQUET.value and - config.metadata['target']['format'] == SupportedFormat.ICEBERG.value - ): - data_loader = ParquetToIceberg(config=config) - data_loader.load() - else: - raise PipelineNotFound( - f""" - source {config.metadata['source']['format']} - to target {config.metadata['target']['format']} - pipeline is not supported yet - """ - ) - - -put_nyc_yellow_taxi_dataset() -# get_nyc_yellow_taxi_dataset() \ No newline at end of file diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/get_data.py b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/get_data.py new file mode 100644 index 0000000..3674ed5 --- /dev/null +++ b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/get_data.py @@ -0,0 +1,51 @@ +from local_data_platform import Config, SupportedFormat +from local_data_platform.store.source.json import Json +from local_data_platform.exceptions import PipelineNotFound +import os +from local_data_platform.logger import log +from local_data_platform.pipeline.egression.iceberg_to_csv import IcebergToCSV + + +logger = log() + + +def get_nyc_yellow_taxi_dataset( + dataset='nyc_taxi', + config_path='/real_world_use_cases/nyc_yellow_taxi_dataset/config/egression.json' +): + logger.info( + """ + We will try to read a ICEBERG table from local catalog + """ + ) + + config = Config( + **Json( + name=dataset, + path=config_path, + ).get() + ) + + logger.info( + f""" + We are using the following dictionary as the configuration to generate a monthly trust metric + {config} + """ + ) + if ( + config.metadata['source']['format'] == SupportedFormat.ICEBERG.value and + config.metadata['target']['format'] == SupportedFormat.CSV.value + ): + data_loader = IcebergToCSV(config=config) + data_loader.load() + else: + raise PipelineNotFound( + f""" + source {config.metadata['source']['format']} + to target {config.metadata['target']['format']} + pipeline is not supported yet + """ + ) + + +get_nyc_yellow_taxi_dataset() \ No newline at end of file diff --git a/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/put_data.py b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/put_data.py new file mode 100644 index 0000000..387e56d --- /dev/null +++ b/local-data-platform/real_world_use_cases/nyc_yellow_taxi_dataset/reports/put_data.py @@ -0,0 +1,51 @@ +from local_data_platform import Config, SupportedFormat +from local_data_platform.store.source.json import Json +from local_data_platform.exceptions import PipelineNotFound +import os +from local_data_platform.logger import log +from local_data_platform.pipeline.ingestion.parquet_to_iceberg import ParquetToIceberg + +logger = log() + + +def put_nyc_yellow_taxi_dataset( + dataset='nyc_taxi', + config_path='/real_world_use_cases/nyc_yellow_taxi_dataset/config/ingestion.json' +): + logger.info( + """ + We will try to read a PARQUET file downloaded from : + https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page + """ + ) + + config = Config( + **Json( + name=dataset, + path=config_path, + ).get() + ) + + logger.info( + f""" + We are using the following dictionary as the configuration to generate a monthly trust metric + {config} + """ + ) + if ( + config.metadata['source']['format'] == SupportedFormat.PARQUET.value and + config.metadata['target']['format'] == SupportedFormat.ICEBERG.value + ): + data_loader = ParquetToIceberg(config=config) + data_loader.load() + else: + raise PipelineNotFound( + f""" + source {config.metadata['source']['format']} + to target {config.metadata['target']['format']} + pipeline is not supported yet + """ + ) + + +put_nyc_yellow_taxi_dataset() From d1c0894908b23be7ca4eddc09eca12cd532e621e Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:28:18 +0530 Subject: [PATCH 02/27] 0.1.1 BigQuery Release (#46) * Create publish.yml * Update publish.yml --- .github/workflows/publish.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..920305d --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,33 @@ +name: Upload Python Package to PyPI when a Release is Created + +on: + release: + types: [created] + +jobs: + pypi-publish: + name: Publish release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/local-data-platform + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel + - name: Build package + run: | + python setup.py sdist bdist_wheel # Could also be python -m build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + - name: pypi-publish + uses: pypa/gh-action-pypi-publish@v1.11.0 + From 72ab669fb44f89b6a499b6ed8c517ea52548702e Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:52:12 +0530 Subject: [PATCH 03/27] pypi release through github actions (#60) * Create publish.yml * Update publish.yml * Update publish.yml --- .github/workflows/publish.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 920305d..05e6e3d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,10 +22,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel + pip install setuptools wheel poetry==1.8 + poetry install - name: Build package run: | - python setup.py sdist bdist_wheel # Could also be python -m build + poetry publish - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish From 0757da8d77eee598a5f0af4f2601e92ba0191bdf Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:57:14 +0530 Subject: [PATCH 04/27] Create manual.yml --- .github/workflows/manual.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/manual.yml diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml new file mode 100644 index 0000000..11b2e35 --- /dev/null +++ b/.github/workflows/manual.yml @@ -0,0 +1,32 @@ +# This is a basic workflow that is manually triggered + +name: Manual workflow + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + name: + # Friendly description to be shown in the UI instead of 'name' + description: 'Person to greet' + # Default value if no value is explicitly provided + default: 'World' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + greet: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Runs a single command using the runners shell + - name: Send greeting + run: echo "Hello ${{ inputs.name }}" From cd2cbbe31c39a32a3d89a3104892813c16e9cfff Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:05:55 +0530 Subject: [PATCH 05/27] Update publish.yml (#62) --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 05e6e3d..a0231fe 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,7 @@ name: Upload Python Package to PyPI when a Release is Created on: release: types: [created] + workflow_dispatch: jobs: pypi-publish: From 80c82866cd3893ad9ccba23a64fb22553c8758da Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:13:58 +0530 Subject: [PATCH 06/27] Update publish.yml (#63) --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a0231fe..76f9b04 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,6 +24,7 @@ jobs: run: | python -m pip install --upgrade pip pip install setuptools wheel poetry==1.8 + cd local-data-platform poetry install - name: Build package run: | From 48efe67316df527e1b6c0050f992455e83d84611 Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:18:24 +0530 Subject: [PATCH 07/27] Update pyproject.toml (#64) --- local-data-platform/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-data-platform/pyproject.toml b/local-data-platform/pyproject.toml index 800afe2..e042fa3 100644 --- a/local-data-platform/pyproject.toml +++ b/local-data-platform/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "local-data-platform" -version = "0.1.0" +version = "0.1.1" description = "" authors = ["Tushar Choudhary <151359025+tusharchou@users.noreply.github.com>"] readme = "README.md" From d1dbf6254ca8904db3b2ebdb5f768e9da8b7a2c9 Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:34:23 +0530 Subject: [PATCH 08/27] Update publish.yml (#65) * Update publish.yml * Update publish.yml * Update publish.yml --- .github/workflows/publish.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 76f9b04..5ed41b9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,7 +28,9 @@ jobs: poetry install - name: Build package run: | - poetry publish + pwd + cd local-data-platform + poetry build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish From 746449026ac92659fef64613438fcf2c86551a06 Mon Sep 17 00:00:00 2001 From: Anubhav Kumar Date: Wed, 30 Oct 2024 21:29:02 +0530 Subject: [PATCH 09/27] Brmhastra patch 1 (#67) * Update publish.yml * Update publish.yml * Update publish.yml * Update publish.yml updated yaml file to copy distribution o/p from build to root directory * Update publish.yml added detailed copy from /ldf/dist to gihub/wo../dist --------- Co-authored-by: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> --- .github/workflows/publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5ed41b9..bd49d56 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,6 +31,9 @@ jobs: pwd cd local-data-platform poetry build + - name: Copy distribution files to root + run: | + cp local-data-platform/dist/* ./github/workspace/dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish From 773058f7484b20354f7bf8933398feaf236161b1 Mon Sep 17 00:00:00 2001 From: Anubhav Kumar Date: Wed, 30 Oct 2024 21:35:27 +0530 Subject: [PATCH 10/27] Brmhastra patch 1 (#68) * Update publish.yml * Update publish.yml * Update publish.yml * Update publish.yml updated yaml file to copy distribution o/p from build to root directory * Update publish.yml added detailed copy from /ldf/dist to gihub/wo../dist * Update publish.yml added a code for creating dist directory --------- Co-authored-by: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bd49d56..fe5fdb2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,6 +33,7 @@ jobs: poetry build - name: Copy distribution files to root run: | + mkdir ./github/workspace/dist cp local-data-platform/dist/* ./github/workspace/dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 From d3e26b76d5efd91ed02a99ea317af8a65119a716 Mon Sep 17 00:00:00 2001 From: Anubhav Kumar Date: Wed, 30 Oct 2024 21:49:00 +0530 Subject: [PATCH 11/27] Brmhastra patch 1 (#69) * Update publish.yml * Update publish.yml * Update publish.yml * Update publish.yml updated yaml file to copy distribution o/p from build to root directory * Update publish.yml added detailed copy from /ldf/dist to gihub/wo../dist * Update publish.yml added a code for creating dist directory * Update publish.yml relocating dots from ./github to /.github --------- Co-authored-by: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> --- .github/workflows/publish.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fe5fdb2..9040e1f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,8 +33,7 @@ jobs: poetry build - name: Copy distribution files to root run: | - mkdir ./github/workspace/dist - cp local-data-platform/dist/* ./github/workspace/dist + cp local-data-platform/dist/* /.github/workspace/dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish From 840223ef375ac7b4ef4f1ce3d17fcf56d43b1c39 Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Wed, 30 Oct 2024 22:58:01 +0530 Subject: [PATCH 12/27] GitHub workflow for pypi release (#70) * Update publish.yml * Update pyproject.toml --- .github/workflows/publish.yml | 3 ++- local-data-platform/pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9040e1f..86842a9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,6 +31,8 @@ jobs: pwd cd local-data-platform poetry build + - name: Create dist directory + run: mkdir -p /.github/workspace/dist - name: Copy distribution files to root run: | cp local-data-platform/dist/* /.github/workspace/dist @@ -38,4 +40,3 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish uses: pypa/gh-action-pypi-publish@v1.11.0 - diff --git a/local-data-platform/pyproject.toml b/local-data-platform/pyproject.toml index e042fa3..7dc440d 100644 --- a/local-data-platform/pyproject.toml +++ b/local-data-platform/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "local-data-platform" version = "0.1.1" -description = "" +description = "Python library for iceberg lake house on your local" authors = ["Tushar Choudhary <151359025+tusharchou@users.noreply.github.com>"] readme = "README.md" From e39db41c7118bc72bd0b78cbf84fcc7b8a8f37ac Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Wed, 30 Oct 2024 23:11:01 +0530 Subject: [PATCH 13/27] GitHub workflow for pypi release (#71) * Update publish.yml * Update pyproject.toml * Update publish.yml --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 86842a9..560df4f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,7 +32,7 @@ jobs: cd local-data-platform poetry build - name: Create dist directory - run: mkdir -p /.github/workspace/dist + run: mkdir -p $GITHUB_WORKSPACE/.github/workspace/dist - name: Copy distribution files to root run: | cp local-data-platform/dist/* /.github/workspace/dist From 8ab9414d13da31b441cf693e7f0a23f14c6679cd Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Wed, 30 Oct 2024 23:25:55 +0530 Subject: [PATCH 14/27] Update publish.yml (#72) --- .github/workflows/publish.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 560df4f..96428f1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,8 +34,7 @@ jobs: - name: Create dist directory run: mkdir -p $GITHUB_WORKSPACE/.github/workspace/dist - name: Copy distribution files to root - run: | - cp local-data-platform/dist/* /.github/workspace/dist + run: cp local-data-platform/dist/* $GITHUB_WORKSPACE/.github/workspace/dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: pypi-publish From 11143b4db7f68e69db65663b4e48a9bf0b262832 Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Thu, 31 Oct 2024 00:08:06 +0530 Subject: [PATCH 15/27] Update publish.yml (#73) --- .github/workflows/publish.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 96428f1..d1148a7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,28 +14,32 @@ jobs: url: https://pypi.org/p/local-data-platform permissions: id-token: write + steps: - uses: actions/checkout@v4 + - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.x" + - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel poetry==1.8 cd local-data-platform poetry install + - name: Build package run: | - pwd cd local-data-platform poetry build + - name: Create dist directory run: mkdir -p $GITHUB_WORKSPACE/.github/workspace/dist + - name: Copy distribution files to root run: cp local-data-platform/dist/* $GITHUB_WORKSPACE/.github/workspace/dist - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - - name: pypi-publish + + - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.11.0 From c9d110151670ba25c23418f2ee19de40ce67363e Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Thu, 31 Oct 2024 13:56:00 +0530 Subject: [PATCH 16/27] Release v1.1 changes (#74) --- .github/workflows/publish.yml | 3 +++ .../local_data_platform-0.1.1-py3-none-any.whl | Bin 0 -> 19091 bytes .../dist/local_data_platform-0.1.1.tar.gz | Bin 0 -> 8536 bytes 3 files changed, 3 insertions(+) create mode 100644 local-data-platform/dist/local_data_platform-0.1.1-py3-none-any.whl create mode 100644 local-data-platform/dist/local_data_platform-0.1.1.tar.gz diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d1148a7..4eb15c5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -35,6 +35,9 @@ jobs: cd local-data-platform poetry build + - name: List contents of dist directory + run: ls -la local-data-platform/dist + - name: Create dist directory run: mkdir -p $GITHUB_WORKSPACE/.github/workspace/dist diff --git a/local-data-platform/dist/local_data_platform-0.1.1-py3-none-any.whl b/local-data-platform/dist/local_data_platform-0.1.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d5d56df034fb8c4fc0b486a6ed746ba6877bcdfe GIT binary patch literal 19091 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 literal 0 HcmV?d00001 From c3f2aa1e5cde76b37d67464ce08e781c5c816175 Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Thu, 31 Oct 2024 15:37:18 +0530 Subject: [PATCH 17/27] Github action setup for release v1.1 (#76) * Release v1.1 dist changes * Release v1.1 publish.yml changes * Release v1.1 publish.yml changes --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4eb15c5..168e449 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,7 @@ jobs: name: Publish release to PyPI runs-on: ubuntu-latest environment: - name: pypi + name: production url: https://pypi.org/p/local-data-platform permissions: id-token: write From bf783b4bd41fba4f9c0e39f5450df5584ed9bb42 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 1 Nov 2024 12:24:33 +0530 Subject: [PATCH 18/27] Update publish.yml --- .github/workflows/publish.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 168e449..1210215 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,11 +38,17 @@ jobs: - name: List contents of dist directory run: ls -la local-data-platform/dist - - name: Create dist directory - run: mkdir -p $GITHUB_WORKSPACE/.github/workspace/dist + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: local-data-platform/dist - - name: Copy distribution files to root - run: cp local-data-platform/dist/* $GITHUB_WORKSPACE/.github/workspace/dist + - name: Download all the dists + uses: actions/download-artifact@v1.11.0 + with: + name: python-package-distributions + path: local-data-platform/dist/ - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.11.0 From 00f50093bc507e717c6f6d5554a137641dfcad0b Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 2 Nov 2024 19:08:18 +0530 Subject: [PATCH 19/27] Update publish.yml (#78) --- .github/workflows/publish.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1210215..c3c1296 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,11 +44,8 @@ jobs: name: python-package-distributions path: local-data-platform/dist - - name: Download all the dists - uses: actions/download-artifact@v1.11.0 - with: - name: python-package-distributions - path: local-data-platform/dist/ - - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.11.0 + with: + path: local-data-platform/ + From 28d5787d6ff72fbdca46ddbac51bf374480d717a Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 2 Nov 2024 19:47:43 +0530 Subject: [PATCH 20/27] Update publish.yml --- .github/workflows/publish.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c3c1296..3a0c77f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -35,15 +35,6 @@ jobs: cd local-data-platform poetry build - - name: List contents of dist directory - run: ls -la local-data-platform/dist - - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: python-package-distributions - path: local-data-platform/dist - - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.11.0 with: From 66b31b02dde3bd19a6e4f8d61fcc61c268312a10 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sun, 3 Nov 2024 11:21:37 +0530 Subject: [PATCH 21/27] Update publish.yml (#80) --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3a0c77f..4a6e7d9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,5 +38,5 @@ jobs: - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@v1.11.0 with: - path: local-data-platform/ + packages-dir: local-data-platform/dist/ From 6716cfd14ab2cbd8c6b7c0c4d9f770296753f14c Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Mon, 4 Nov 2024 14:08:29 +0530 Subject: [PATCH 22/27] Update pyproject.toml (#81) --- local-data-platform/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/local-data-platform/pyproject.toml b/local-data-platform/pyproject.toml index 7dc440d..61671f2 100644 --- a/local-data-platform/pyproject.toml +++ b/local-data-platform/pyproject.toml @@ -4,6 +4,7 @@ version = "0.1.1" description = "Python library for iceberg lake house on your local" authors = ["Tushar Choudhary <151359025+tusharchou@users.noreply.github.com>"] readme = "README.md" +readme_content_type = "text/markdown" [tool.poetry.dependencies] python = ">=3.9,<4.0" From f8c994a23095541cc6cfaaa1726efede7b79e925 Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Tue, 5 Nov 2024 18:00:08 +0530 Subject: [PATCH 23/27] Release v1.1 fix (#84) * Release v1.1 dist changes * Release v1.1 publish.yml changes * Release v1.1 publish.yml changes * Release v1.1 --- local-data-platform/README.md | 3 +++ local-data-platform/pyproject.toml | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/local-data-platform/README.md b/local-data-platform/README.md index e69de29..88daa7d 100644 --- a/local-data-platform/README.md +++ b/local-data-platform/README.md @@ -0,0 +1,3 @@ +# Local Data Platform +Local Data Platform is a python library that uses open source tools to orchestrate a data platform operations locally for development and testing.
+This library provides solutions for all stages ranging from ingestion to reporting all of which one can build data pipeline locally, test and easily scale up to cloud. diff --git a/local-data-platform/pyproject.toml b/local-data-platform/pyproject.toml index 61671f2..7dc440d 100644 --- a/local-data-platform/pyproject.toml +++ b/local-data-platform/pyproject.toml @@ -4,7 +4,6 @@ version = "0.1.1" description = "Python library for iceberg lake house on your local" authors = ["Tushar Choudhary <151359025+tusharchou@users.noreply.github.com>"] readme = "README.md" -readme_content_type = "text/markdown" [tool.poetry.dependencies] python = ">=3.9,<4.0" From 49800334f89e7ec67bb05c0fd230745c64a9b072 Mon Sep 17 00:00:00 2001 From: Mrutunjay Kinagi Date: Sat, 9 Nov 2024 17:28:56 +0530 Subject: [PATCH 24/27] 0.1.1 Pytest Added for BigQuery Source (#86) * fixed bug warehouse uri Thu Oct 24 9:25 PM IST * added logging to CSV.get() * supported big query ts format * refactored parameter to config from catalog to reduce confusion * Fixed bug of logger in GCP * replaced local path with dynamic path * replaced local path with dynamic path * replaced local path with dynamic path * demo * demo * Release v1.1 dist changes * Release v1.1 publish.yml changes * Release v1.1 publish.yml changes * added a class implementation for github issue hoping it is useful for guiding users to resolution ETAs eventually * added a exception for PlanNotFound to ask users to raise issues on the repository for resolution * Updated overview and milestones. Added directory structure under technical specifications. * Updated components in technical speciifcations * added testing for BigQueryToCSV.extract() * added testing for BigQueryToCSV.extract() * added testing for Iceberg.get() * Release v1.1 * Release v1.1 bug fix * Release v1.1 bug fix * Release v1.1 bug fix * Pytest Added for BigQuery Source --------- Co-authored-by: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> --- .github/workflows/publish.yml | 5 + README.md | 316 +++++++++++------- local-data-platform/README.md | 222 +++++++++++- ...local_data_platform-0.1.0-py3-none-any.whl | Bin 5546 -> 0 bytes .../dist/local_data_platform-0.1.0.tar.gz | Bin 2285 -> 0 bytes ...local_data_platform-0.1.1-py3-none-any.whl | Bin 19091 -> 0 bytes .../dist/local_data_platform-0.1.1.tar.gz | Bin 8536 -> 0 bytes .../local_data_platform/exceptions.py | 3 + .../local_data_platform/issue/__init__.py | 92 +++++ .../tests/test_extract_big_query_to_csv.py | 53 +++ .../tests/test_format_iceberg_extract.py | 3 + .../tests/test_gcp_connection.py | 94 ++---- 12 files changed, 594 insertions(+), 194 deletions(-) delete mode 100644 local-data-platform/dist/local_data_platform-0.1.0-py3-none-any.whl delete mode 100644 local-data-platform/dist/local_data_platform-0.1.0.tar.gz delete mode 100644 local-data-platform/dist/local_data_platform-0.1.1-py3-none-any.whl delete mode 100644 local-data-platform/dist/local_data_platform-0.1.1.tar.gz create mode 100644 local-data-platform/local_data_platform/issue/__init__.py create mode 100644 local-data-platform/tests/test_extract_big_query_to_csv.py create mode 100644 local-data-platform/tests/test_format_iceberg_extract.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4a6e7d9..3b5db81 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,6 +30,11 @@ jobs: cd local-data-platform poetry install + - name: Clean up dist directory + run: | + cd local-data-platform + rm -rf dist/* + - name: Build package run: | cd local-data-platform diff --git a/README.md b/README.md index c0aab52..cbadcd2 100644 --- a/README.md +++ b/README.md @@ -1,143 +1,203 @@ +Dear User # Local Data Platform -Local Data Platform is a python library that uses open source tools to orchestrate a data platform operations locally for development and testing.
-This library provides solutions for all stages ranging from ingestion to reporting all of which one can build data pipeline locally, test and easily scale up to cloud. +### Explain this to me like I am five +Imagine you have a toy box where you keep all your favorite toys. +A local data platform is like that toy box, but for storing and +organizing important information instead of toys. +Just like how your toy box, +**a local data platform keeps all your data** +(like pictures, documents, and other info) **in one place +so you can easily find, use and manage it.** + +It's really handy for keeping everything organized and in one spot! 🌟📦 + +Got it? What else are you curious about? + +> **Vision:** Local Data Platform is used as a python library to learn +> and operate data lake house locally.
+> **Mission:** Develop a python package which provides solutions for all stages +> of data organisation, ranging from ingestion to reporting. +> The goal is that one can build data pipeline locally, test and +> easily scale up to cloud.
+>
+> **By 2025,** local-data-platform is a python package that uses open source +> tools to orchestrate a data platform operation, locally, for development +> and testing.
## Problem Statement -| Question | Answer | -|----------|---------------------------------------------------------------------------------------------------| -| What? | a local data platform that can scale up to cloud | -| Why? | save costs on cloud infra and developement time | -| When? | start of product development life cycle | -| Where? | local first | -| Who? | Business who want a product data platform that will run locally and scale up when the time comes. | - -## Components - -It uses below tools: -1. Ingestion using [Apache Arrow](https://arrow.apache.org/) in [Parquet](https://parquet.apache.org/) file format. -2. Data Catalog using [Iceberg](https://iceberg.apache.org/) -3. [DuckDB](https://duckdb.org/) as Datawarehouse -4. [DBT](https://www.getdbt.com/) for transformation operations. -5. [Apache Airflow](https://airflow.apache.org/) for orchestration -### Source - -Our local data platform supports `Parquet Files` for now and new formats and sources will be added in subsequent releases.
- -`Parquet Files` : High-performance columnar storage format, optimized for efficient reading and querying of large datasets.
- - - -### Data Catalog with Apache Iceberg on SQLite - -Our platform uses Apache Iceberg to manage large-scale datasets efficiently while ensuring ACID compliance, schema evolution, and performant queries.
- -Our platform leverages Apache Iceberg as the data catalog on top of SQLite for storing and transforming raw data.
- -Initially, raw data in form of Parquet files are ingested into SQLite. SQLite, being a lightweight, serverless database, serves as an intermediary layer where the data can be stored, processed, and transformed as needed. Apache Iceberg acts as the data catalog throughout the process. It manages metadata for all datasets, including raw data in SQLite. - - - -### Transformations - -Once raw data is ingested into SQLite, we use `DBT` (Data Build Tool) for transforming and modeling the data. - - - -### Target - -Once the transformations are complete, the processed and clean data is stored in `DuckDB` using Apache Iceberg's table format.
- -Apache Iceberg acts as a unified metadata layer across both the raw and processed data. The platform can handle complex data versioning, schema evolution, and partition pruning, ensuring optimal performance during querying. With DuckDB’s in-memory analytical capabilities and Iceberg’s efficient data layout, querying the processed data becomes highly performant and scalable. - - - -## Example -#### Sample Data - -Data can be available as single file in the source format. For example New York Yellow taxi data is available to be - -pulled from [here](https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page) +| Question | Answer | +|----------|----------------------------------------------------------------------------------------------------| +| What? | a local data platform that can scale up to cloud | +| Why? | save costs on cloud infra and development time | +| When? | start of product development life cycle | +| Where? | local first | +| Who? | Business who wants a product data platform that will run locally and scale up when the time comes. | + +# Technical Specifications +This will help you understand how to read the repository. \ +**Note:** The users install the package and the developer import the library. + +## Directory Structure + +### local-data-platform/ `repository` +- **.github/** `hidden folder` + - ISSUE-TEMPLATE/ `samples` + - bug_report.md `Report bugs here` + - custom.md `Report ad hoc issues here` + - feature_request.md `Request a new feature here` + - pull_request_template.md `Raise a pull request on the repo` + +- **docs/** `Documentation for Read the Docs` + +- **local-data-platform** `package` + - local_data_platform `library` + - hello_world.py `module` + - hello_world `function` + - prints 'Hello, world!' `output` + +- **samples/** `tutorials` + - bigQueryTutorial.py `Demo bigQuery compatibility here` +- .gitignore `Mention files to ignore in your PR` +- .readthedocs.yaml `Configuration for Read the Docs` +- LICENSE `for legal purposes` +- lumache.py `Template used in Sphinx projects for Read the Docs` +- pyproject.toml `template configuration` +- README.md `How to understand the repo` +- README.rst `Configuration for Read the Docs` + +## How to test Pre-release as a User + +1. Check the directory structure \ +`ls` +2. Change directory to local-data-platform +`cd local-data-platform` +2. Install the dependencies listed in your pyproject.toml file \ +`$poetry install` +2. Execute your test suite to ensure everything is working as expected \ +`poetry run pytest` +3. Run hello world command \ +`poetry run python hello_world.py` + + + +## Package structure +- **local-data-platform** `package` + - **dist** `Package distribution files` + - **docs** `Documentation` + - **local_data_platform** `library` + - **catalog** `Catalog your data` + - **local** `Catalog your data locally` + - **iceberg** `Catalog your data in iceberg SQL lite db` + - export.py `Export your catalog data to csv` + - **cloud** `Interact with cloud service providers` + - **gcp** `Interact with Google Cloud Platform` + - **login** `Login to GCP to get API credentials` + - **engine** `Underlying processing Tech` + - **format** `Supported formats for storage` + - **csv** `Supports Google sheets and Excel sheets` + - **iceberg** `Supports Apache Iceberg` + - **parquet** `Supports Apache Parquet` + - **issue** `Github Issues` + - **pipeline** `Data Pipeline` + - **egression** `Downstream pipelines` + - **csv_to_iceberg** `Raw to Silver Layer` + - **iceberg_to_csv** `Silver Layer to Gold Layer` + - **ingestion** `Upstream pipelines` + - **bigquery_to_csv** `Source to Raw` + - **csv_to_iceberg** `Raw to Silver Layer` + - **paraquet_to_iceberg** `Raw to Silver Layer` + - **scraper** `HTML to CSV` + - **store** `Data store` + - **source** `Source data class` + - **gcp** `GCP Storage` + - **bigquery** `GCP service` + - **json** `Local JSON file` + - **near** `NEAR Data Lake` + - **parquet** `Local Parquet file` + - **target** `Target data class` + - **iceberg** `Local Data Lake house` + - etl.py `Sample pipeline` + - exceptions.py `Known limitations` + - hello_world.py `Test Feature` + - is_function.py `Query Library Functions` + - logger.py `Library logger` + - **real_world_use_cases** `User Test Cases` + - **near_data_lake** `NEAR Coin Transactions` + - **config** `Pipeline configurations` + - **sample_queries** `NEAR Data Lake Transaction Table` + - near_transaction.json `Query List` + - egression.json `Loading data in local data lake house` + - ingestion.json `Extracting data from NEAR data lake house` + - **data** `target path` + - **near_transactions.db** `Local data lake house` + - **transactions** `iceberg table` + - **data** `table records` + - **metadata** `iceberg table metadata` + - near_transactions_catalog.db `iceberg local data catalog` + - **reports** `Production analysis` + - get_data.py `Get insights` + - put_data.py `Refresh Gold Layer` + - near_transactions.csv `Output` + - **nyc_yello_taxi_dataset** `NYC Yello Taxis Rides` + - **config** `Pipeline configurations` + - egression.json `Loading data in local data lake house` + - egression_payments.json `Loading payments report in Gold Layer` + - ingestion.json `Extracting data from local parquet file` + - **data** `target path` + - **nyc_yello_taxi_dataset.db** `Local data lake house` + - **rides** `iceberg table` + - **data** `table records` + - **metadata** `iceberg table metadata` + - nyc_yellow_taxi_dataset_catalog.db `iceberg local data catalog` + - nyc_yellow_taxi_rides.csv `Ouput` + - **reports** `Production analysis` + - export_catalog.py `Saves local iceberg catalog in CSV` + - get_data.py `Create Gold Layer` + - get_report.py `Updates Gold Layer` + - put_data.py `Refreshes Gold Layer` + - monthly_reporting.md `Report in MD` + - **tests** `PyTest Unit testing` + - test_gcp_connection.py `Testing GCP Login` + + +## Plan + +| Milestone | Epic | Target Date | Delivery Date | Comment | +|-----------|-------------------------|-------------|---------------|-----------------------------------------| +| 0.1.0 | HelloWorld | 1st Oct 24 | 1st Oct 24 | Good Start | +| 0.1.1 | Ingestion | 31st Oct 24 | 5th Nov 24 | First Release: _Completed in 2 Sprints_ | +| 0.1.2 | Warehousing | 15th Nov 24 | TBD | Coming Soon | +| 0.1.3 | Orchestration | 29th Nov 24 | TBD | Coming Soon | +| 0.1.4 | Self Serving Gold Layer | 29th Nov 24 | TBD | Coming Soon | +| 0.1.5 | Monitoring | 29th Nov 24 | TBD | Coming Soon | +| 0.1.6 | BI Reporting Dashboard | 31st Dec 24 | TBD | Coming Soon | +| 0.1.7 | Data Science Insights | 31st Dec 24 | TBD | Coming Soon | +| 0.1.8 | LLM | 31st Dec 24 | TBD | Coming Soon | +| 0.1.9 | Launch Documentation | 30th Nov 24 | TBD | Coming Soon | +| 1.0.0 | Ready for Production | 1st Nov 24 | TBD | End Game | -``` - -curl https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet -o /tmp/yellow_tripdata_2023-01.parquet - -``` -#### Ingestion Layer -Please refer given ingestion layer [python script](https://github.com/tusharchou/local-data-platform/blob/main/local-data-platform/nyc_yellow_taxi.py) - -#### Subsequent Layers -[yet to be released](null) . Please check the Plan and milestone below. - - ## Plan - - +### Releases - -| Milestone | Epic | Target Date | Delivery Date | Release Owner | Comment | -|-----------|----------------------|-------------|---------------|-----------------|--------------| -| 0.1.0 | HelloWorld | 1st Oct 24 | 1st Oct 24 | @tusharchou | Good Start | -| 0.1.1 | Ingestion | 3rd Oct 24 | 9th Oct 24 | @tusharchou | First Sprint | -| 0.1.2 | Warehousing | 18th Oct 24 | TBD | @tusharchou | Coming Soon | -| 1.0.0 | Ready for Production | 1st Nov 24 | TBD | TBD | End Game | - - - -### Milestone - - - -- [x] 0.1.0 : Done+ Published Library on [PyPI](https://pypi.org/project/local-data-platform/) - - +- [x] 0.1.0 : Done- Published Library on [PyPI](https://pypi.org/project/local-data-platform/) - [ ] 0.1.1 : In Progress- [Demo BigQuery compatibility](https://github.com/tusharchou/local-data-platform/milestone/2) -- [x] 0.1.1 : Done+ [Documentation: Updated README to explain clearly problem and plan of excecution](https://github.com/tusharchou/local-data-platform/issues/6) - -- [ ] PR : In Progress- [Feature: Simply query NEAR Coin GCP Data Lake through BiqQuery](https://github.com/tusharchou/local-data-platform/pull/25) - -- [ ] PR : In Progress- [Feature: Privately store NYC Yellow Taxi Rides Data in Local Data Platform](https://github.com/tusharchou/local-data-platform/pull/26) - -- [ ] FR : In Progress- [Change: Easily solve for User's Local Data Need](https://github.com/tusharchou/local-data-platform/pull/28) - -- [ ] IS : In Progress- [Documentation: Align on Product Framework](https://github.com/tusharchou/local-data-platform/issues/29) - -- [ ] IS : In Progress- [Request: Source Parquet Table](https://github.com/tusharchou/local-data-platform/issues/24) - -- [ ] IS : In Progress- [Request: Source Iceberg Table](https://github.com/tusharchou/local-data-platform/issues/21) - -- [ ] IS : In Progress- [Request: Target Iceberg Table](https://github.com/tusharchou/local-data-platform/issues/22) - -- [ ] IS : In Progress- [Request: Target.put() Iceberg Table](https://github.com/tusharchou/local-data-platform/issues/20) - -- [ ] IS : In Progress- [Request: NYCYellowTaxi.rides.put()](https://github.com/tusharchou/local-data-platform/issues/8) - -- [ ] IS : In Progress- [Request: NYCYellowTaxi.rides.get()](https://github.com/tusharchou/local-data-platform/issues/3) - -- [ ] IS : In Progress- [Request: test.iceberg.exception()](https://github.com/tusharchou/local-data-platform/issues/1) - -- [ ] IS : In Progress- [Documentation: NEAR Trader-How to use NEAR Data Lake](https://github.com/tusharchou/local-data-platform/issues/12) - -- [ ] IS : In Progress- [Request: Source.get() BigQuery](https://github.com/tusharchou/local-data-platform/issues/19) - -- [ ] IS : To-do- [Request: Iceberg Partitioning and Version Control](https://github.com/tusharchou/local-data-platform/issues/29) - -- [ ] IS : To-do- [Request: Align on Product Framework](https://github.com/tusharchou/local-data-platform/issues/29) - -- [ ] IS : In Progress- [Align on Product Framework](https://github.com/tusharchou/local-data-platform/issues/29) - -- [ ] 0.1.2 : To-do Continuous Integration - -- [ ] 0.1.9 : To-do[Launch Documentation](https://github.com/tusharchou/local-data-platform/milestone/2) - -- [ ] 0.2.0 : To-do [Cloud Integration](https://github.com/tusharchou/local-data-platform/milestone/3) - -- [ ] 1.0.0 : To-do [Demo BigQuery compatibility](https://github.com/tusharchou/local-data-platform/milestone/2) +- [x] 0.1.1 : Done- [Documentation: Updated README to explain clearly problem and plan of excecution](https://github.com/tusharchou/local-data-platform/issues/6) + +- [ ] 0.1.2 : To-do- [Warehousing: DuckDB, Iceberg, DBT](https://github.com/tusharchou/local-data-platform/milestone/5) +- [ ] 0.1.3 : To-do- [Orchestration](https://github.com/tusharchou/local-data-platform/milestone/6) +- [ ] 0.1.4 : To-do- [Self Serving Gold Layer](https://github.com/tusharchou/local-data-platform/milestone/11) +- [ ] 0.1.5 : To-do- [Monitoring](https://github.com/tusharchou/local-data-platform/milestone/10) +- [ ] 0.1.6 : To-do- [Business Intelligence Reporting Dashboard](https://github.com/tusharchou/local-data-platform/milestone/9) +- [ ] 0.1.7 : To-do- [Data Science Insights](https://github.com/tusharchou/local-data-platform/milestone/8) +- [ ] 0.1.8 : To-do- [LLM](https://github.com/tusharchou/local-data-platform/milestone/7) +- [ ] 0.1.9 : To-do- [Launch Documentation](https://github.com/tusharchou/local-data-platform/milestone/2) +- [ ] 0.2.0 : To-do- [Cloud Integration](https://github.com/tusharchou/local-data-platform/milestone/3) +- [ ] 1.0.0 : To-do- Product ### References diff --git a/local-data-platform/README.md b/local-data-platform/README.md index 88daa7d..cbadcd2 100644 --- a/local-data-platform/README.md +++ b/local-data-platform/README.md @@ -1,3 +1,221 @@ +Dear User # Local Data Platform -Local Data Platform is a python library that uses open source tools to orchestrate a data platform operations locally for development and testing.
-This library provides solutions for all stages ranging from ingestion to reporting all of which one can build data pipeline locally, test and easily scale up to cloud. +### Explain this to me like I am five +Imagine you have a toy box where you keep all your favorite toys. +A local data platform is like that toy box, but for storing and +organizing important information instead of toys. +Just like how your toy box, +**a local data platform keeps all your data** +(like pictures, documents, and other info) **in one place +so you can easily find, use and manage it.** + +It's really handy for keeping everything organized and in one spot! 🌟📦 + +Got it? What else are you curious about? + +> **Vision:** Local Data Platform is used as a python library to learn +> and operate data lake house locally.
+> **Mission:** Develop a python package which provides solutions for all stages +> of data organisation, ranging from ingestion to reporting. +> The goal is that one can build data pipeline locally, test and +> easily scale up to cloud.
+>
+> **By 2025,** local-data-platform is a python package that uses open source +> tools to orchestrate a data platform operation, locally, for development +> and testing.
+ +## Problem Statement + + +| Question | Answer | +|----------|----------------------------------------------------------------------------------------------------| +| What? | a local data platform that can scale up to cloud | +| Why? | save costs on cloud infra and development time | +| When? | start of product development life cycle | +| Where? | local first | +| Who? | Business who wants a product data platform that will run locally and scale up when the time comes. | + +# Technical Specifications +This will help you understand how to read the repository. \ +**Note:** The users install the package and the developer import the library. + +## Directory Structure + +### local-data-platform/ `repository` +- **.github/** `hidden folder` + - ISSUE-TEMPLATE/ `samples` + - bug_report.md `Report bugs here` + - custom.md `Report ad hoc issues here` + - feature_request.md `Request a new feature here` + - pull_request_template.md `Raise a pull request on the repo` + +- **docs/** `Documentation for Read the Docs` + +- **local-data-platform** `package` + - local_data_platform `library` + - hello_world.py `module` + - hello_world `function` + - prints 'Hello, world!' `output` + +- **samples/** `tutorials` + - bigQueryTutorial.py `Demo bigQuery compatibility here` +- .gitignore `Mention files to ignore in your PR` +- .readthedocs.yaml `Configuration for Read the Docs` +- LICENSE `for legal purposes` +- lumache.py `Template used in Sphinx projects for Read the Docs` +- pyproject.toml `template configuration` +- README.md `How to understand the repo` +- README.rst `Configuration for Read the Docs` + +## How to test Pre-release as a User + +1. Check the directory structure \ +`ls` +2. Change directory to local-data-platform +`cd local-data-platform` +2. Install the dependencies listed in your pyproject.toml file \ +`$poetry install` +2. Execute your test suite to ensure everything is working as expected \ +`poetry run pytest` +3. Run hello world command \ +`poetry run python hello_world.py` + + + +## Package structure +- **local-data-platform** `package` + - **dist** `Package distribution files` + - **docs** `Documentation` + - **local_data_platform** `library` + - **catalog** `Catalog your data` + - **local** `Catalog your data locally` + - **iceberg** `Catalog your data in iceberg SQL lite db` + - export.py `Export your catalog data to csv` + - **cloud** `Interact with cloud service providers` + - **gcp** `Interact with Google Cloud Platform` + - **login** `Login to GCP to get API credentials` + - **engine** `Underlying processing Tech` + - **format** `Supported formats for storage` + - **csv** `Supports Google sheets and Excel sheets` + - **iceberg** `Supports Apache Iceberg` + - **parquet** `Supports Apache Parquet` + - **issue** `Github Issues` + - **pipeline** `Data Pipeline` + - **egression** `Downstream pipelines` + - **csv_to_iceberg** `Raw to Silver Layer` + - **iceberg_to_csv** `Silver Layer to Gold Layer` + - **ingestion** `Upstream pipelines` + - **bigquery_to_csv** `Source to Raw` + - **csv_to_iceberg** `Raw to Silver Layer` + - **paraquet_to_iceberg** `Raw to Silver Layer` + - **scraper** `HTML to CSV` + - **store** `Data store` + - **source** `Source data class` + - **gcp** `GCP Storage` + - **bigquery** `GCP service` + - **json** `Local JSON file` + - **near** `NEAR Data Lake` + - **parquet** `Local Parquet file` + - **target** `Target data class` + - **iceberg** `Local Data Lake house` + - etl.py `Sample pipeline` + - exceptions.py `Known limitations` + - hello_world.py `Test Feature` + - is_function.py `Query Library Functions` + - logger.py `Library logger` + - **real_world_use_cases** `User Test Cases` + - **near_data_lake** `NEAR Coin Transactions` + - **config** `Pipeline configurations` + - **sample_queries** `NEAR Data Lake Transaction Table` + - near_transaction.json `Query List` + - egression.json `Loading data in local data lake house` + - ingestion.json `Extracting data from NEAR data lake house` + - **data** `target path` + - **near_transactions.db** `Local data lake house` + - **transactions** `iceberg table` + - **data** `table records` + - **metadata** `iceberg table metadata` + - near_transactions_catalog.db `iceberg local data catalog` + - **reports** `Production analysis` + - get_data.py `Get insights` + - put_data.py `Refresh Gold Layer` + - near_transactions.csv `Output` + - **nyc_yello_taxi_dataset** `NYC Yello Taxis Rides` + - **config** `Pipeline configurations` + - egression.json `Loading data in local data lake house` + - egression_payments.json `Loading payments report in Gold Layer` + - ingestion.json `Extracting data from local parquet file` + - **data** `target path` + - **nyc_yello_taxi_dataset.db** `Local data lake house` + - **rides** `iceberg table` + - **data** `table records` + - **metadata** `iceberg table metadata` + - nyc_yellow_taxi_dataset_catalog.db `iceberg local data catalog` + - nyc_yellow_taxi_rides.csv `Ouput` + - **reports** `Production analysis` + - export_catalog.py `Saves local iceberg catalog in CSV` + - get_data.py `Create Gold Layer` + - get_report.py `Updates Gold Layer` + - put_data.py `Refreshes Gold Layer` + - monthly_reporting.md `Report in MD` + - **tests** `PyTest Unit testing` + - test_gcp_connection.py `Testing GCP Login` + + +## Plan + +| Milestone | Epic | Target Date | Delivery Date | Comment | +|-----------|-------------------------|-------------|---------------|-----------------------------------------| +| 0.1.0 | HelloWorld | 1st Oct 24 | 1st Oct 24 | Good Start | +| 0.1.1 | Ingestion | 31st Oct 24 | 5th Nov 24 | First Release: _Completed in 2 Sprints_ | +| 0.1.2 | Warehousing | 15th Nov 24 | TBD | Coming Soon | +| 0.1.3 | Orchestration | 29th Nov 24 | TBD | Coming Soon | +| 0.1.4 | Self Serving Gold Layer | 29th Nov 24 | TBD | Coming Soon | +| 0.1.5 | Monitoring | 29th Nov 24 | TBD | Coming Soon | +| 0.1.6 | BI Reporting Dashboard | 31st Dec 24 | TBD | Coming Soon | +| 0.1.7 | Data Science Insights | 31st Dec 24 | TBD | Coming Soon | +| 0.1.8 | LLM | 31st Dec 24 | TBD | Coming Soon | +| 0.1.9 | Launch Documentation | 30th Nov 24 | TBD | Coming Soon | +| 1.0.0 | Ready for Production | 1st Nov 24 | TBD | End Game | + + + +### Releases + +- [x] 0.1.0 : Done- Published Library on [PyPI](https://pypi.org/project/local-data-platform/) + +- [ ] 0.1.1 : In Progress- [Demo BigQuery compatibility](https://github.com/tusharchou/local-data-platform/milestone/2) + +- [x] 0.1.1 : Done- [Documentation: Updated README to explain clearly problem and plan of excecution](https://github.com/tusharchou/local-data-platform/issues/6) + +- [ ] 0.1.2 : To-do- [Warehousing: DuckDB, Iceberg, DBT](https://github.com/tusharchou/local-data-platform/milestone/5) +- [ ] 0.1.3 : To-do- [Orchestration](https://github.com/tusharchou/local-data-platform/milestone/6) +- [ ] 0.1.4 : To-do- [Self Serving Gold Layer](https://github.com/tusharchou/local-data-platform/milestone/11) +- [ ] 0.1.5 : To-do- [Monitoring](https://github.com/tusharchou/local-data-platform/milestone/10) +- [ ] 0.1.6 : To-do- [Business Intelligence Reporting Dashboard](https://github.com/tusharchou/local-data-platform/milestone/9) +- [ ] 0.1.7 : To-do- [Data Science Insights](https://github.com/tusharchou/local-data-platform/milestone/8) +- [ ] 0.1.8 : To-do- [LLM](https://github.com/tusharchou/local-data-platform/milestone/7) +- [ ] 0.1.9 : To-do- [Launch Documentation](https://github.com/tusharchou/local-data-platform/milestone/2) +- [ ] 0.2.0 : To-do- [Cloud Integration](https://github.com/tusharchou/local-data-platform/milestone/3) +- [ ] 1.0.0 : To-do- Product + + +### References + + + +[iceberg-python](https://py.iceberg.apache.org) + +[near-data-lake](https://docs.near.org/concepts/advanced/near-lake-framework) + +[duckdb](https://duckdb.org/docs/extensions/iceberg.html) + + + +#### Self Promotion + + + +[Reliable Change Data Capture using Iceberg](https://medium.com/@tushar.choudhary.de/reliable-cdc-apache-spark-ingestion-pipeline-using-iceberg-5d8f0fee6fd6) + +[Introduction to pyiceberg](https://medium.com/@tushar.choudhary.de/internals-of-apache-pyiceberg-10c2302a5c8b) \ No newline at end of file diff --git a/local-data-platform/dist/local_data_platform-0.1.0-py3-none-any.whl b/local-data-platform/dist/local_data_platform-0.1.0-py3-none-any.whl deleted file mode 100644 index c9da76c9ca631c5c7fffcdfaafb54ab5238dfc23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5546 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 diff --git a/local-data-platform/dist/local_data_platform-0.1.0.tar.gz b/local-data-platform/dist/local_data_platform-0.1.0.tar.gz deleted file mode 100644 index 55576276fadfa560aafb67cdd3167a56085eac3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2285 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 diff --git a/local-data-platform/local_data_platform/exceptions.py b/local-data-platform/local_data_platform/exceptions.py index 879a1a5..666bb50 100644 --- a/local-data-platform/local_data_platform/exceptions.py +++ b/local-data-platform/local_data_platform/exceptions.py @@ -8,3 +8,6 @@ class PipelineNotFound(Exception): class EngineNotFound(Exception): """Raised when engine is not supported""" + +class PlanNotFound(Exception): + """Raised when issue doesn't have resolution estimate""" \ No newline at end of file diff --git a/local-data-platform/local_data_platform/issue/__init__.py b/local-data-platform/local_data_platform/issue/__init__.py new file mode 100644 index 0000000..ca1fd47 --- /dev/null +++ b/local-data-platform/local_data_platform/issue/__init__.py @@ -0,0 +1,92 @@ +from .. import Repository +from ..logger import log +import requests +from bs4 import BeautifulSoup +from textwrap import fill +import re + +logger = log() + +""" +class issue is of type Repository +""" + + +class Issue(Repository): + def __init__( + self, + number: int=1, + repo_name='local-data-platform', + owner='tusharchou' + ) -> str: + logger.debug( + f""" + Loading... Issue Object from {owner} {repo_name} {number} + """ + ) + self.num = number + self.project = repo_name + self.owner = owner + self.name, self.desc = self._get_github_issue() + + def get(self) -> str: + return (self.name ,self.desc) + + def _get_github_issue(self): + url = f"https://github.com/{self.owner}/{self.project}/issues/{self.num}" + logger.debug( + f""" + Pulling Issue Object from {url} + """ + ) + + try: + response = requests.get(url) + logger.debug( + f""" + Extracting Issue Object from {response} + """ + ) + response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx) + + soup = BeautifulSoup(response.text, 'html.parser') + + """ + PyIceberg Near-Term Roadmap + """ + element = soup.select_one('.js-issue-title').get_text(strip=True) + # element = soup.select_one('.js-issue-title markdown-title') + + logger.debug( + f""" + """ + ) + # Extract the issue body + text = soup.select_one('.js-comment-body').get_text() + # pattern = re.compile(r'(? None: + # assert table_v2.schema() == Schema( + # NestedField(field_id=1, name="x", field_type=LongType(), required=True), + # NestedField(field_id=2, name="y", field_type=LongType(), required=True, doc="comment"), + # NestedField(field_id=3, name="z", field_type=LongType(), required=True), + # identifier_field_ids=[1, 2], + # ) + # assert table_v2.schema().schema_id == 1 + # + # assert False diff --git a/local-data-platform/tests/test_format_iceberg_extract.py b/local-data-platform/tests/test_format_iceberg_extract.py new file mode 100644 index 0000000..9db6fda --- /dev/null +++ b/local-data-platform/tests/test_format_iceberg_extract.py @@ -0,0 +1,3 @@ +class TestIceberg: + def test_put(self): + assert False diff --git a/local-data-platform/tests/test_gcp_connection.py b/local-data-platform/tests/test_gcp_connection.py index 976706b..d306570 100644 --- a/local-data-platform/tests/test_gcp_connection.py +++ b/local-data-platform/tests/test_gcp_connection.py @@ -1,100 +1,66 @@ import pytest +from unittest.mock import patch, MagicMock +from local_data_platform.store.source.gcp.bigquery import BigQuery, GCPCredentials +from local_data_platform.store.source.json import Json +from pathlib import Path +from pyarrow import Table import tempfile import json -from unittest.mock import patch, MagicMock -from local_data_platform.store.source.gcp.bigquery import GCPBigQueryConnection - @pytest.fixture def mock_bigquery_client(): - with patch( - "local_data_platform.store.source.gcp.bigquery.bigquery.Client" - ) as mock_client: + with patch("local_data_platform.store.source.gcp.bigquery.bigquery.Client") as mock_client: yield mock_client - @pytest.fixture def mock_service_account_credentials(): - with patch( - "local_data_platform.store.source.gcp.bigquery.service_account.Credentials" - ) as mock_credentials: + with patch("local_data_platform.store.source.gcp.bigquery.service_account.Credentials") as mock_credentials: yield mock_credentials - @pytest.fixture def temp_credentials_file(): - """ - Fixture that creates a temporary JSON file containing Google Cloud Platform (GCP) service account credentials. - - This fixture generates a temporary file with mock GCP service account credentials and yields the file path. - The file is not deleted automatically after the test, allowing for inspection if needed. - - Returns: - str: The file path to the temporary credentials JSON file. - """ credentials_data = { "type": "service_account", "project_id": "your-project-id", "private_key_id": "some-private-key-id", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASC...\n-----END PRIVATE KEY-----\n", + "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASC...\\n-----END PRIVATE KEY-----\\n", "client_email": "your-service-account-email@your-project-id.iam.gserviceaccount.com", "client_id": "some-client-id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-service-account-email%40your-project-id.iam.gserviceaccount.com", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-project-id.iam.gserviceaccount.com", } - with tempfile.NamedTemporaryFile( - delete=False, mode="w", suffix=".json" - ) as temp_file: + with tempfile.NamedTemporaryFile(delete=False, mode="w") as temp_file: json.dump(credentials_data, temp_file) temp_file_path = temp_file.name yield temp_file_path +@pytest.fixture +def temp_json_file(): + json_data = { + "query": "SELECT * FROM test_table" + } + with tempfile.NamedTemporaryFile(delete=False, mode="w") as temp_file: + json.dump(json_data, temp_file) + temp_file_path = temp_file.name + yield temp_file_path -def test_query( - mock_service_account_credentials, mock_bigquery_client, temp_credentials_file -): - """ - Test the `query` method of the `GCPBigQueryConnection` class. - - This test verifies that the `query` method correctly executes a SQL query and returns the expected results. - It mocks the service account credentials and BigQuery client to simulate the interaction with Google Cloud Platform. - - Args: - mock_service_account_credentials (MagicMock): Mocked service account credentials. - mock_bigquery_client (MagicMock): Mocked BigQuery client. - temp_credentials_file (str): Path to a temporary credentials file. - - Steps: - 1. Mock the service account credentials and BigQuery client. - 2. Initialize the `GCPBigQueryConnection` with the temporary credentials file and project ID. - 3. Execute the `query` method with a sample SQL query. - 4. Verify that the results match the expected output. - 5. Ensure that the `query` method was called with the correct query string. - - Running the Tests: - pytest tests/test_gcp_connection.py - """ - # Mock the credentials and client - mock_service_account_credentials.from_service_account_file.return_value = ( - MagicMock() - ) +def test_get_method(mock_service_account_credentials, mock_bigquery_client, temp_credentials_file, temp_json_file): + mock_service_account_credentials.from_service_account_file.return_value = MagicMock() mock_client_instance = mock_bigquery_client.return_value mock_query_job = MagicMock() - mock_query_job.result.return_value = [{"column1": "value1", "column2": "value2"}] + mock_query_job.to_dataframe.return_value = MagicMock() mock_client_instance.query.return_value = mock_query_job - # Initialize the GCPBigQueryConnection - connection = GCPBigQueryConnection(temp_credentials_file, "your-project-id") + credentials = GCPCredentials(path=temp_credentials_file, kwargs={}) + bigquery_instance = BigQuery(name="test", credentials=credentials, path=Path("/tmp")) - # Execute the query method - result = connection.query("SELECT * FROM dataset.table") + json_instance = Json(path=Path(temp_json_file),name="test") + query = json_instance.get()["query"] - # Verify the results - assert len(result) == 1 - assert result[0]["column1"] == "value1" - assert result[0]["column2"] == "value2" + # Convert Path to string before passing to BigQuery + result = bigquery_instance.get(query=str(query)) - # Verify that the query method was called with the correct query string - mock_client_instance.query.assert_called_once_with("SELECT * FROM dataset.table") + assert isinstance(result, Table) + mock_client_instance.query.assert_called_with(query) \ No newline at end of file From 0e7e61da070457da982fe4d51b394ebd5011c0cd Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:36:21 +0530 Subject: [PATCH 25/27] fix(deps): Stabilize build dependencies and configuration --- .gitignore | 56 +- .readthedocs.yaml | 36 +- mkdocs.yml | 68 +++ poetry.lock | 1420 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 33 +- requirements.txt | 6 + 6 files changed, 1582 insertions(+), 37 deletions(-) create mode 100644 mkdocs.yml create mode 100644 poetry.lock create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index eee2dcb..6c44f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,50 @@ -.idea/local-data-platform.iml -.idea/misc.xml -.idea/modules.xml -.idea/vcs.xml -.idea/workspace.xml -**/__pycache__/ \ No newline at end of file +# Virtual Environment +.venv/ + +# Python cache +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +build/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# MkDocs +site/ +docs/_build/ + +# Pytest +.pytest_cache/ +.mypy_cache/ +.coverage + +# OS / Editor specific +.DS_Store +.idea/ +*.swp + +# Temporary files +*.bak + +# Temporary directories +src/tmp/ +src/local_data_platform/tmp/ + +# Old config files +mkdocs.yaml \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 381f813..0d0f869 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,32 +1,18 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required +# .readthedocs.yml version: 2 -# Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: - python: "3.9" - # You can also specify other tool versions: - # nodejs: "19" - # rust: "1.64" - # golang: "1.19" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: docs/conf.py + python: "3.12" -# Optionally build your docs in additional formats such as PDF and ePub -# formats: -# - pdf -# - epub +# Configuration for MkDocs +mkdocs: + configuration: mkdocs.yml -# Optional but recommended, declare the Python requirements required -# to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -# python: -# install: -# - requirements: docs/requirements.txt +# Python dependencies +python: + install: + - requirements: requirements.txt + - method: pip + path: . diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..ee7bf64 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,68 @@ +site_name: local-data-platform +site_author: Local Data Platform core team +copyright: 2024, Local Data Platform core team + +repo_url: https://github.com/tusharchou/local-data-platform +repo_name: local-data-platform + +nav: + - Home: index.md + - User Guide: + - Recipes: recipes.md + - API Reference: api.md + - Contributing: + - User Issues: user_issues.md + - Feature Requests: developer_feature_requests.md + +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - navigation.top + - search.suggest + - search.highlight + - content.tabs.link + - content.code.annotation + - content.code.copy + language: en + palette: + - scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + +docs_dir: docs + +# Extensions +markdown_extensions: + - attr_list + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - toc: + permalink: true + toc_depth: 3 + +plugins: + - search + - autorefs + - mkdocstrings: + handlers: + python: + options: + paths: [src] + docstring_style: google + members_order: source + show_source: true + filters: + - "!^local_data_platform\\.real_world_use_cases" \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..c2cb796 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1420 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "babel" +version = "2.17.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, +] + +[package.extras] +dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] + +[[package]] +name = "cachetools" +version = "5.5.2" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, +] + +[[package]] +name = "certifi" +version = "2025.7.14" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +files = [ + {file = "certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2"}, + {file = "certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +files = [ + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, +] + +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "flake8" +version = "7.3.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.9" +files = [ + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" + +[[package]] +name = "fsspec" +version = "2025.7.0" +description = "File-system specification" +optional = false +python-versions = ">=3.9" +files = [ + {file = "fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21"}, + {file = "fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff (>=0.5)"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffe" +version = "1.7.3" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.9" +files = [ + {file = "griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75"}, + {file = "griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.8.2" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.9" +files = [ + {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, + {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, +] + +[package.extras] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.2" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13"}, + {file = "mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[package.dependencies] +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mkdocs-material" +version = "9.5.21" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material-9.5.21-py3-none-any.whl", hash = "sha256:210e1f179682cd4be17d5c641b2f4559574b9dea2f589c3f0e7c17c5bd1959bc"}, + {file = "mkdocs_material-9.5.21.tar.gz", hash = "sha256:049f82770f40559d3c2aa2259c562ea7257dbb4aaa9624323b5ef27b2d95a450"}, +] + +[package.dependencies] +babel = ">=2.10,<3.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.0,<4.0" +markdown = ">=3.2,<4.0" +mkdocs = ">=1.6,<2.0" +mkdocs-material-extensions = ">=1.3,<2.0" +paginate = ">=0.5,<1.0" +pygments = ">=2.16,<3.0" +pymdown-extensions = ">=10.2,<11.0" +regex = ">=2022.4" +requests = ">=2.26,<3.0" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.25.2" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"}, + {file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"}, +] + +[package.dependencies] +click = ">=7.0" +Jinja2 = ">=2.11.1" +Markdown = ">=3.3" +MarkupSafe = ">=1.1" +mkdocs = ">=1.4" +mkdocs-autorefs = ">=0.3.1" +mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""} +platformdirs = ">=2.2.0" +pymdown-extensions = ">=6.3" + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=0.5.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "1.10.9" +description = "A Python handler for mkdocstrings." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings_python-1.10.9-py3-none-any.whl", hash = "sha256:cbe98710a6757dfd4dff79bf36cb9731908fb4c69dd2736b15270ae7a488243d"}, + {file = "mkdocstrings_python-1.10.9.tar.gz", hash = "sha256:f344aaa47e727d8a2dc911e063025e58e2b7fb31a41110ccc3902aa6be7ca196"}, +] + +[package.dependencies] +griffe = ">=0.49" +mkdocs-autorefs = ">=1.0" +mkdocstrings = ">=0.25" + +[[package]] +name = "mmh3" +version = "5.1.0" +description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions." +optional = false +python-versions = ">=3.9" +files = [ + {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eaf4ac5c6ee18ca9232238364d7f2a213278ae5ca97897cafaa123fcc7bb8bec"}, + {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48f9aa8ccb9ad1d577a16104834ac44ff640d8de8c0caed09a2300df7ce8460a"}, + {file = "mmh3-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4ba8cac21e1f2d4e436ce03a82a7f87cda80378691f760e9ea55045ec480a3d"}, + {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d69281c281cb01994f054d862a6bb02a2e7acfe64917795c58934b0872b9ece4"}, + {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d05ed3962312fbda2a1589b97359d2467f677166952f6bd410d8c916a55febf"}, + {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78ae6a03f4cff4aa92ddd690611168856f8c33a141bd3e5a1e0a85521dc21ea0"}, + {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95f983535b39795d9fb7336438faae117424c6798f763d67c6624f6caf2c4c01"}, + {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d46fdd80d4c7ecadd9faa6181e92ccc6fe91c50991c9af0e371fdf8b8a7a6150"}, + {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16e976af7365ea3b5c425124b2a7f0147eed97fdbb36d99857f173c8d8e096"}, + {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6fa97f7d1e1f74ad1565127229d510f3fd65d931fdedd707c1e15100bc9e5ebb"}, + {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4052fa4a8561bd62648e9eb993c8f3af3bdedadf3d9687aa4770d10e3709a80c"}, + {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3f0e8ae9f961037f812afe3cce7da57abf734285961fffbeff9a4c011b737732"}, + {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99297f207db967814f1f02135bb7fe7628b9eacb046134a34e1015b26b06edce"}, + {file = "mmh3-5.1.0-cp310-cp310-win32.whl", hash = "sha256:2e6c8dc3631a5e22007fbdb55e993b2dbce7985c14b25b572dd78403c2e79182"}, + {file = "mmh3-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:e4e8c7ad5a4dddcfde35fd28ef96744c1ee0f9d9570108aa5f7e77cf9cfdf0bf"}, + {file = "mmh3-5.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:45da549269883208912868a07d0364e1418d8292c4259ca11699ba1b2475bd26"}, + {file = "mmh3-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b529dcda3f951ff363a51d5866bc6d63cf57f1e73e8961f864ae5010647079d"}, + {file = "mmh3-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db1079b3ace965e562cdfc95847312f9273eb2ad3ebea983435c8423e06acd7"}, + {file = "mmh3-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:22d31e3a0ff89b8eb3b826d6fc8e19532998b2aa6b9143698043a1268da413e1"}, + {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2139bfbd354cd6cb0afed51c4b504f29bcd687a3b1460b7e89498329cc28a894"}, + {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c8105c6a435bc2cd6ea2ef59558ab1a2976fd4a4437026f562856d08996673a"}, + {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57730067174a7f36fcd6ce012fe359bd5510fdaa5fe067bc94ed03e65dafb769"}, + {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde80eb196d7fdc765a318604ded74a4378f02c5b46c17aa48a27d742edaded2"}, + {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9c8eddcb441abddeb419c16c56fd74b3e2df9e57f7aa2903221996718435c7a"}, + {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:99e07e4acafbccc7a28c076a847fb060ffc1406036bc2005acb1b2af620e53c3"}, + {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e25ba5b530e9a7d65f41a08d48f4b3fedc1e89c26486361166a5544aa4cad33"}, + {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bb9bf7475b4d99156ce2f0cf277c061a17560c8c10199c910a680869a278ddc7"}, + {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a1b0878dd281ea3003368ab53ff6f568e175f1b39f281df1da319e58a19c23a"}, + {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:25f565093ac8b8aefe0f61f8f95c9a9d11dd69e6a9e9832ff0d293511bc36258"}, + {file = "mmh3-5.1.0-cp311-cp311-win32.whl", hash = "sha256:1e3554d8792387eac73c99c6eaea0b3f884e7130eb67986e11c403e4f9b6d372"}, + {file = "mmh3-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8ad777a48197882492af50bf3098085424993ce850bdda406a358b6ab74be759"}, + {file = "mmh3-5.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f29dc4efd99bdd29fe85ed6c81915b17b2ef2cf853abf7213a48ac6fb3eaabe1"}, + {file = "mmh3-5.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45712987367cb9235026e3cbf4334670522a97751abfd00b5bc8bfa022c3311d"}, + {file = "mmh3-5.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b1020735eb35086ab24affbea59bb9082f7f6a0ad517cb89f0fc14f16cea4dae"}, + {file = "mmh3-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:babf2a78ce5513d120c358722a2e3aa7762d6071cd10cede026f8b32452be322"}, + {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4f47f58cd5cbef968c84a7c1ddc192fef0a36b48b0b8a3cb67354531aa33b00"}, + {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2044a601c113c981f2c1e14fa33adc9b826c9017034fe193e9eb49a6882dbb06"}, + {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94d999c9f2eb2da44d7c2826d3fbffdbbbbcde8488d353fee7c848ecc42b968"}, + {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a015dcb24fa0c7a78f88e9419ac74f5001c1ed6a92e70fd1803f74afb26a4c83"}, + {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457da019c491a2d20e2022c7d4ce723675e4c081d9efc3b4d8b9f28a5ea789bd"}, + {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71408579a570193a4ac9c77344d68ddefa440b00468a0b566dcc2ba282a9c559"}, + {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8b3a04bc214a6e16c81f02f855e285c6df274a2084787eeafaa45f2fbdef1b63"}, + {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:832dae26a35514f6d3c1e267fa48e8de3c7b978afdafa0529c808ad72e13ada3"}, + {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf658a61fc92ef8a48945ebb1076ef4ad74269e353fffcb642dfa0890b13673b"}, + {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3313577453582b03383731b66447cdcdd28a68f78df28f10d275d7d19010c1df"}, + {file = "mmh3-5.1.0-cp312-cp312-win32.whl", hash = "sha256:1d6508504c531ab86c4424b5a5ff07c1132d063863339cf92f6657ff7a580f76"}, + {file = "mmh3-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:aa75981fcdf3f21759d94f2c81b6a6e04a49dfbcdad88b152ba49b8e20544776"}, + {file = "mmh3-5.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4c1a76808dfea47f7407a0b07aaff9087447ef6280716fd0783409b3088bb3c"}, + {file = "mmh3-5.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a523899ca29cfb8a5239618474a435f3d892b22004b91779fcb83504c0d5b8c"}, + {file = "mmh3-5.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:17cef2c3a6ca2391ca7171a35ed574b5dab8398163129a3e3a4c05ab85a4ff40"}, + {file = "mmh3-5.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52e12895b30110f3d89dae59a888683cc886ed0472dd2eca77497edef6161997"}, + {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d6719045cda75c3f40397fc24ab67b18e0cb8f69d3429ab4c39763c4c608dd"}, + {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d19fa07d303a91f8858982c37e6939834cb11893cb3ff20e6ee6fa2a7563826a"}, + {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31b47a620d622fbde8ca1ca0435c5d25de0ac57ab507209245e918128e38e676"}, + {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f810647c22c179b6821079f7aa306d51953ac893587ee09cf1afb35adf87cb"}, + {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6128b610b577eed1e89ac7177ab0c33d06ade2aba93f5c89306032306b5f1c6"}, + {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1e550a45d2ff87a1c11b42015107f1778c93f4c6f8e731bf1b8fa770321b8cc4"}, + {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:785ae09276342f79fd8092633e2d52c0f7c44d56e8cfda8274ccc9b76612dba2"}, + {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0f4be3703a867ef976434afd3661a33884abe73ceb4ee436cac49d3b4c2aaa7b"}, + {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e513983830c4ff1f205ab97152a0050cf7164f1b4783d702256d39c637b9d107"}, + {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9135c300535c828c0bae311b659f33a31c941572eae278568d1a953c4a57b59"}, + {file = "mmh3-5.1.0-cp313-cp313-win32.whl", hash = "sha256:c65dbd12885a5598b70140d24de5839551af5a99b29f9804bb2484b29ef07692"}, + {file = "mmh3-5.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:10db7765201fc65003fa998faa067417ef6283eb5f9bba8f323c48fd9c33e91f"}, + {file = "mmh3-5.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:b22fe2e54be81f6c07dcb36b96fa250fb72effe08aa52fbb83eade6e1e2d5fd7"}, + {file = "mmh3-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:166b67749a1d8c93b06f5e90576f1ba838a65c8e79f28ffd9dfafba7c7d0a084"}, + {file = "mmh3-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adba83c7ba5cc8ea201ee1e235f8413a68e7f7b8a657d582cc6c6c9d73f2830e"}, + {file = "mmh3-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a61f434736106804eb0b1612d503c4e6eb22ba31b16e6a2f987473de4226fa55"}, + {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba9ce59816b30866093f048b3312c2204ff59806d3a02adee71ff7bd22b87554"}, + {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd51597bef1e503363b05cb579db09269e6e6c39d419486626b255048daf545b"}, + {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d51a1ed642d3fb37b8f4cab966811c52eb246c3e1740985f701ef5ad4cdd2145"}, + {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:709bfe81c53bf8a3609efcbd65c72305ade60944f66138f697eefc1a86b6e356"}, + {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e01a9b0092b6f82e861137c8e9bb9899375125b24012eb5219e61708be320032"}, + {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:27e46a2c13c9a805e03c9ec7de0ca8e096794688ab2125bdce4229daf60c4a56"}, + {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5766299c1d26f6bfd0a638e070bd17dbd98d4ccb067d64db3745bf178e700ef0"}, + {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7785205e3e4443fdcbb73766798c7647f94c2f538b90f666688f3e757546069e"}, + {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8e574fbd39afb433b3ab95683b1b4bf18313dc46456fc9daaddc2693c19ca565"}, + {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1b6727a5a20e32cbf605743749f3862abe5f5e097cbf2afc7be5aafd32a549ae"}, + {file = "mmh3-5.1.0-cp39-cp39-win32.whl", hash = "sha256:d6eaa711d4b9220fe5252032a44bf68e5dcfb7b21745a96efc9e769b0dd57ec2"}, + {file = "mmh3-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:49d444913f6c02980e5241a53fe9af2338f2043d6ce5b6f5ea7d302c52c604ac"}, + {file = "mmh3-5.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:0daaeaedd78773b70378f2413c7d6b10239a75d955d30d54f460fb25d599942d"}, + {file = "mmh3-5.1.0.tar.gz", hash = "sha256:136e1e670500f177f49ec106a4ebf0adf20d18d96990cc36ea492c651d2b406c"}, +] + +[package.extras] +benchmark = ["pymmh3 (==0.0.5)", "pyperf (==2.8.1)", "xxhash (==3.5.0)"] +docs = ["myst-parser (==4.0.0)", "shibuya (==2024.12.21)", "sphinx (==8.1.3)", "sphinx-copybutton (==0.5.2)"] +lint = ["black (==24.10.0)", "clang-format (==19.1.7)", "isort (==5.13.2)", "pylint (==3.3.3)"] +plot = ["matplotlib (==3.10.0)", "pandas (==2.2.3)"] +test = ["pytest (==8.3.4)", "pytest-sugar (==1.0.0)"] +type = ["mypy (==1.14.1)"] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.9" +files = [ + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pycodestyle" +version = "2.14.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, + {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.33.2" +typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyflakes" +version = "3.4.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, +] + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyiceberg" +version = "0.9.1" +description = "Apache Iceberg is an open table format for huge analytic datasets" +optional = false +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,!=3.8.*,>=3.9" +files = [ + {file = "pyiceberg-0.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a183d9217eb82159c01b23c683057f96c8b2375f592b921721d1c157895e2df"}, + {file = "pyiceberg-0.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57030bb15c397b0379242907c5611f5b4338fb799e972353fd0edafde6cfd2ef"}, + {file = "pyiceberg-0.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ba4cd9a8f6a04cfbc68e0c83f2db3ffd14244da8601a142cc05965d4b343645"}, + {file = "pyiceberg-0.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5a48c6a2016d0dcde8c9079cc5e6b6d2e2ac663eddfe4697e7ea03a0edc40b7"}, + {file = "pyiceberg-0.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:8bebfa5a804a95a9f3d98d88cbeb37430b09add04592238bba2a2b2e0466d60d"}, + {file = "pyiceberg-0.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e75c502dd56ac3d77036ce8a3b2566348da5ff4367c7c671981616ef6dcc883"}, + {file = "pyiceberg-0.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0a8189c9b3ba81dd12493d6bb874a656a4d4909904552b97a629d1d43b3a0e90"}, + {file = "pyiceberg-0.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c03065d5c5b704444ab8fb18cdd232ec43994db95b9e53444008ebc2cf9dc2c"}, + {file = "pyiceberg-0.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:93f2586a5da737de6e4643bf096a01772f068d1eedb7ffde6b36c60b6b9e6bd3"}, + {file = "pyiceberg-0.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:94e45c10051110ba7a43b85a1f0a680b4a31d1d6cee593c8e62e14d22d18c47d"}, + {file = "pyiceberg-0.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b8a958e3bbe919026533cee1f0fb6b7040928fce8d42c2ecea228de7c17578fa"}, + {file = "pyiceberg-0.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7e956b35c6822600c45fd8f3ea8cfea328cc406fefa534afeb6fdb325d05406"}, + {file = "pyiceberg-0.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e4e585164d7d86f5c9a609a1bc2abeae2f0ea0680a11a2064d3a945866b5311"}, + {file = "pyiceberg-0.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fee08dac30e8524526f7d18468f9670f8606905b850b261314c597c6633f3b4"}, + {file = "pyiceberg-0.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:124793c54a0c2fb5ac4ab19c38da116c068e277c85cbaa7e4064e635a70b595e"}, + {file = "pyiceberg-0.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6e29eb5ce63e8a14738f3efeb54022093456e02b681f0b8c815f7ef9e20ddcb"}, + {file = "pyiceberg-0.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ebd4f74da8a3f7b78ad746c1d91d8cd9aa9cf97f4d36da164e3550f6a06b00e"}, + {file = "pyiceberg-0.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b357638a58d9b0a5d7018fbe88fa84469c980c80d86441b7b9cd99871512447d"}, + {file = "pyiceberg-0.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f8a93c1e4ab35195018ce8fbbb6d973e099194ffe06d859bdf069d7b846da7aa"}, + {file = "pyiceberg-0.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:5c1b3598d521476ffce13949ae762a3dec49287198b26de445caa0daf2e395fa"}, + {file = "pyiceberg-0.9.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:77aec1c77d675603e0c5358e74adcae8d13b323753d702011be3f309d26af355"}, + {file = "pyiceberg-0.9.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cf567438bf6267bbb67fdfdfc72ac500d523725fca9a6a38f93e8acd4146190e"}, + {file = "pyiceberg-0.9.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5992db7c00d789a33ff117700d453126803e769507a5edeb79bb6510ff72fc00"}, + {file = "pyiceberg-0.9.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e460fca26162a3822c0e8d50b49c80928a0e35cb41698748d7a26f8c016215"}, + {file = "pyiceberg-0.9.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:037aa7a8bfaf7f1482e6a3532217b5f4281bc81db6698c3ea87771d0453a8232"}, + {file = "pyiceberg-0.9.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5150464428a0568c4f46405884bc777dde37935580fb72b0030dfa28805d82e7"}, + {file = "pyiceberg-0.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af2a6c273cfaf2b21b319fcf79489f87604220a0497942303b2a715a9d0f29e9"}, + {file = "pyiceberg-0.9.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:023c3fcee36a441b7e20418b6e9cdc6f904141bfda09f8580dfe022d7faa7a53"}, + {file = "pyiceberg-0.9.1.tar.gz", hash = "sha256:3634134ce33859a441768b39df179b2c6f3de2bbbf506622884f553b013ee799"}, +] + +[package.dependencies] +cachetools = ">=5.5.0,<6.0.0" +click = ">=7.1.1,<9.0.0" +fsspec = ">=2023.1.0" +mmh3 = ">=4.0.0,<6.0.0" +pydantic = ">=2.0,<2.4.0 || >2.4.0,<2.4.1 || >2.4.1,<3.0" +pyparsing = ">=3.1.0,<4.0.0" +requests = ">=2.20.0,<3.0.0" +rich = ">=10.11.0,<14.0.0" +sortedcontainers = "2.4.0" +strictyaml = ">=1.7.0,<2.0.0" +tenacity = ">=8.2.3,<10.0.0" + +[package.extras] +adlfs = ["adlfs (>=2023.1.0)"] +daft = ["getdaft (>=0.2.12)"] +duckdb = ["duckdb (>=0.5.0,<2.0.0)", "pyarrow (>=17.0.0,<20.0.0)"] +dynamodb = ["boto3 (>=1.24.59)"] +gcsfs = ["gcsfs (>=2023.1.0)"] +glue = ["boto3 (>=1.24.59)", "mypy-boto3-glue (>=1.28.18)"] +hive = ["thrift (>=0.13.0,<1.0.0)"] +hive-kerberos = ["kerberos (>=1.3.1,<2.0.0)", "thrift (>=0.13.0,<1.0.0)", "thrift-sasl (>=0.4.3)"] +pandas = ["pandas (>=1.0.0,<3.0.0)", "pyarrow (>=17.0.0,<20.0.0)"] +polars = ["polars (>=1.21.0,<2.0.0)"] +pyarrow = ["pyarrow (>=17.0.0,<20.0.0)"] +pyiceberg-core = ["pyiceberg-core (>=0.4.0,<0.5.0)"] +ray = ["pandas (>=1.0.0,<3.0.0)", "pyarrow (>=17.0.0,<20.0.0)", "ray (==2.10.0)", "ray (>=2.10.0,<3.0.0)"] +rest-sigv4 = ["boto3 (>=1.24.59)"] +s3fs = ["s3fs (>=2023.1.0)"] +snappy = ["python-snappy (>=0.6.0,<1.0.0)"] +sql-postgres = ["psycopg2-binary (>=2.9.6)", "sqlalchemy (>=2.0.18,<3.0.0)"] +sql-sqlite = ["sqlalchemy (>=2.0.18,<3.0.0)"] +zstandard = ["zstandard (>=0.13.0,<1.0.0)"] + +[[package]] +name = "pymdown-extensions" +version = "10.16" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pymdown_extensions-10.16-py3-none-any.whl", hash = "sha256:f5dd064a4db588cb2d95229fc4ee63a1b16cc8b4d0e6145c0899ed8723da1df2"}, + {file = "pymdown_extensions-10.16.tar.gz", hash = "sha256:71dac4fca63fabeffd3eb9038b756161a33ec6e8d230853d3cecf562155ab3de"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.19.1)"] + +[[package]] +name = "pyparsing" +version = "3.2.3" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, + {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "8.4.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + +[[package]] +name = "requests" +version = "2.32.4" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "strictyaml" +version = "1.7.3" +description = "Strict, typed YAML parser" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "strictyaml-1.7.3-py3-none-any.whl", hash = "sha256:fb5c8a4edb43bebb765959e420f9b3978d7f1af88c80606c03fb420888f5d1c7"}, + {file = "strictyaml-1.7.3.tar.gz", hash = "sha256:22f854a5fcab42b5ddba8030a0e4be51ca89af0267961c8d6cfa86395586c407"}, +] + +[package.dependencies] +python-dateutil = ">=2.6.0" + +[[package]] +name = "tenacity" +version = "9.1.2" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, + {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +files = [ + {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, + {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +files = [ + {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, + {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "watchdog" +version = "6.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.9" +files = [ + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "2d19db6500226f2c392eccfde08c2876784901c005764bca5867d017ee0a6f43" diff --git a/pyproject.toml b/pyproject.toml index 14a2dda..d4a1af8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,29 @@ [build-system] -requires = ["flit_core >=3.2,<4"] -build-backend = "flit_core.buildapi" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" -[project] -name = "lumache" -authors = [{name = "Graziella", email = "graziella@lumache"}] -dynamic = ["version", "description"] +[tool.poetry] +name = "local-data-platform" +version = "0.1.1" +description = "A Python library to build, test, and run a complete data platform on your local machine." +authors = ["Local Data Platform core team "] +license = "Apache-2.0" +readme = "README.md" +packages = [{include = "local_data_platform", from = "src"}] + +[tool.poetry.dependencies] +python = "^3.12" +pyiceberg = ">=0.5.0" + +[tool.poetry.group.docs] +optional = true +[tool.poetry.group.docs.dependencies] +mkdocs = "^1.6.0" +mkdocs-material = "9.5.21" +mkdocstrings = {extras = ["python"], version = "^0.25.0"} +pymdown-extensions = "^10.8.1" +[tool.poetry.group.dev] +optional = true +[tool.poetry.group.dev.dependencies] +pytest = ">=7.0.0" +flake8 = ">=5.0.0" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a1b0c76 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +mkdocs>=1.6.0 +mkdocs-material==9.5.21 +mkdocstrings[python]>=0.25.0 +pymdown-extensions>=10.8.1 +pyyaml>=6.0 +requests \ No newline at end of file From 0c4c40f68c7f052393e0db0635c986b756a6c7c0 Mon Sep 17 00:00:00 2001 From: Sankalp Modi Date: Sun, 20 Jul 2025 14:00:45 +0530 Subject: [PATCH 26/27] Add restaurant_data_mart_PRD.md for managing restaurant data with LDP (#93) --- resturant_data_mart_PRD.md | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 resturant_data_mart_PRD.md diff --git a/resturant_data_mart_PRD.md b/resturant_data_mart_PRD.md new file mode 100644 index 0000000..50d9032 --- /dev/null +++ b/resturant_data_mart_PRD.md @@ -0,0 +1,84 @@ +# Restaurant Data Mart - Product Requirements Document (PRD) + +## Objective +To build a data mart for managing restaurant data, enabling seamless data collection, storage in Iceberg, and reporting to Slack through the LDP (Lakehouse Data Platform). + +--- + +## Key Features + +### 1. Data Collection +- **Source**: Collect data from restaurant management systems, APIs, and manual uploads. +- **Data Types**: + - Restaurant details (name, location, cuisine, etc.) + - Menu items and pricing + - Sales and revenue data + - Customer reviews and ratings +- **Validation**: Ensure data integrity with schema validation and deduplication. + +### 2. Data Storage +- **Storage Format**: Apache Iceberg for efficient querying and versioning. +- **Partitioning**: Partition data by restaurant ID and date for optimized performance. +- **Metadata Management**: Maintain metadata for schema evolution and auditing. + +### 3. Reporting and Notifications +- **Slack Integration**: + - Send daily/weekly reports on key metrics (e.g., revenue, top-performing restaurants). + - Alert on anomalies (e.g., sudden drop in sales). +- **Dashboards**: Provide visual insights into restaurant performance using BI tools. + +### 4. Automation via LDP +- **ETL Pipelines**: + - Extract data from sources. + - Transform data into a unified schema. + - Load data into Iceberg tables. +- **Scheduling**: Automate pipelines using LDP's orchestration capabilities. +- **Monitoring**: Track pipeline health and data quality metrics. + +--- + +## Technical Requirements + +### 1. Data Schema +- **Restaurant Table**: + - `restaurant_id` (string, primary key) + - `name` (string) + - `location` (string) + - `cuisine` (string) + - `created_at` (timestamp) +- **Sales Table**: + - `sale_id` (string, primary key) + - `restaurant_id` (string, foreign key) + - `amount` (decimal) + - `sale_date` (date) + - `created_at` (timestamp) + +### 2. Iceberg Configuration +- Enable time travel and schema evolution. +- Optimize for large-scale queries. + +### 3. Slack Integration +- Use Slack Webhooks for notifications. +- Configure channels for different types of reports. + +--- + +## Success Metrics +- Reduced manual effort in managing restaurant data. +- Improved data accuracy and reporting timeliness. +- Increased adoption of insights by stakeholders. + +--- + +## Timeline +- **Week 1-2**: Requirements gathering and schema design. +- **Week 3-4**: Build ETL pipelines and Iceberg integration. +- **Week 5**: Implement Slack reporting and dashboards. +- **Week 6**: Testing and deployment. + +--- + +## Stakeholders +- **Product Owner**: [Your Name] +- **Engineering Team**: Data Engineers, Backend Developers +- **Business Team**: Restaurant Managers, Analysts \ No newline at end of file From 09755aacac7560252daa0e2f8e7dde97ca533bb0 Mon Sep 17 00:00:00 2001 From: Tushar Choudhary <151359025+tusharchou@users.noreply.github.com> Date: Sun, 27 Jul 2025 15:57:27 +0530 Subject: [PATCH 27/27] feat(dx): Add Makefile and setup guide (#96) * feat(dx): Add Makefile and setup guide * feat: Stabilize build and enhance developer experience - Fixes dependency issues by correcting pyproject.toml and standardizing on a root requirements.txt for Read the Docs. - Adds a Makefile to streamline common development commands. - Introduces Dockerfiles for reproducible development and documentation hosting. - Implements a script to dynamically generate a list of open GitHub issues for the documentation. - Cleans up the project structure by removing obsolete Sphinx files, duplicate Makefiles, and misplaced source files. * Add docs improvements, recipes, test, and CI workflow * Remove site/ assets from version control and add to .gitignore * docs: add wiki to docs and update mkdocs navigation * Refactor: move package to root, update docs, fix navigation and developer tooling * cleaning * fix: update readthedocs.yml and docs improvements * deleted readthedocs.yml * added more docs * added more docs * added letter * feat(library): introduce github module and src layout * reset * issues * issues fix * homepage * business solutions * marketing data analysis * marketing data analysis fix * marketing data analysis final --- .github/workflows/append_pr_history.yml | 43 ++++ .github/workflows/ci.yml | 56 +++++ .github/workflows/mkdocs_link_check.yml | 37 +++ .gitignore | 56 +---- Makefile | 84 +++++++ README.md | 187 +++----------- docs/Makefile | 90 +++++-- docs/PR_HISTORY.md | 5 + docs/api.md | 5 + docs/closed_items.md | 25 ++ docs/conf.py | 63 ----- docs/contributing.md | 28 +++ docs/developer_tooling_update.md | 27 ++ docs/index.md | 30 +++ .../__init__.py => docs/pr_description.md | 0 docs/pr_reviews.md | 31 +++ docs/recipes.md | 27 ++ docs/requirements.txt | 58 ----- docs/scripts/generate_issue_list.py | 0 docs/user_issues.md | 230 ++++++++++++++++++ docs/wiki/ACTIVE_DOCS_URLS.md | 69 ++++++ docs/wiki/BRANCHES.md | 22 ++ docs/wiki/CONTENTS.md | 17 ++ docs/wiki/CONTRIBUTING.md | 5 + docs/wiki/DEVELOPMENT.md | 31 +++ docs/wiki/GITHUB.md | 64 +++++ docs/wiki/PROBLEM_STATEMENT.md | 18 ++ docs/wiki/PRODUCT_DEVELOPMENT.md | 28 +++ docs/wiki/PROJECT_OVERVIEW.md | 10 + docs/wiki/PROJECT_STRUCTURE.md | 32 +++ docs/wiki/RECIPES.md | 5 + docs/wiki/TECHNICAL_SPECIFICATIONS.md | 3 + docs/wiki/VISION.md | 29 +++ docs/wiki/business/MARKETING_DATA_ANALYSIS.md | 69 ++++++ docs/wiki/business/PROPOSAL.md | 31 +++ docs/wiki/library/BASE.md | 24 ++ docs/wiki/library/EXCEPTION.md | 164 +++++++++++++ docs/wiki/library/TEST.md | 156 ++++++++++++ docs/wiki/plan/ISSUES.md | 17 ++ docs/wiki/usecases/PHOTO_MANAGEMENT.md | 19 ++ docs/wiki/workflow/PHOTO_MANAGEMENT.md | 86 +++++++ how_to_setup.md | 70 ++++++ .../__init__.py | 0 .../catalog/__init__.py | 0 .../catalog/local/__init__.py | 0 .../catalog/local/iceberg/__init__.py | 0 .../engine/__init__.py | 0 .../etl.py | 0 .../exceptions.py | 0 .../format/__init__.py | 0 .../format/csv/__init__.py | 0 .../format/iceberg/__init__.py | 0 .../format/parquet/__init__.py | 0 .../hello_world.py | 0 .../issue/__init__.py | 0 .../logger.py | 0 .../pipeline/__init__.py | 0 .../pipeline/egression/__init__.py | 0 .../egression/csv_to_iceberg/__init__.py | 0 .../egression/iceberg_to_csv/__init__.py | 0 .../pipeline/ingestion/__init__.py | 0 .../ingestion/bigquery_to_csv/__init__.py | 0 .../ingestion/csv_to_iceberg/__init__.py | 0 .../ingestion/parquet_to_iceberg/__init__.py | 0 .../pipeline/ingestion/pyarrow/__init__.py | 0 .../store/__init__.py | 0 .../store/source/__init__.py | 0 .../store/source/gcp/__init__.py | 0 .../store/source/gcp/bigquery/__init__.py | 0 .../store/source/json/__init__.py | 0 .../store/source/near/__init__.py | 0 .../store/source/parquet/__init__.py | 0 .../store/target/__init__.py | 0 .../store/target/iceberg/__init__.py | 0 .../tmp/warehouse/pyiceberg_catalog.db | Bin mkdocs.yml | 31 ++- poetry.lock | 35 ++- pyproject.toml | 24 +- scripts/__init__.py | 0 scripts/append_pr_history.py | 48 ++++ scripts/fetch_closed_items.py | 54 ++++ scripts/fetch_rtd_urls.py | 58 +++++ scripts/generate_issue_list.py | 71 ++++++ scripts/github_api.py | 0 src/local_data_platform/__init__.py | 0 src/local_data_platform/github/__init__.py | 101 ++++++++ src/tmp/warehouse/pyiceberg_catalog.db | Bin 0 -> 20480 bytes tests/__init__.py | 0 tests/test_github.py | 65 +++++ tests/test_json_source.py | 0 tests/test_placeholder.py | 3 + 91 files changed, 2190 insertions(+), 351 deletions(-) create mode 100644 .github/workflows/append_pr_history.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/mkdocs_link_check.yml create mode 100644 Makefile create mode 100644 docs/PR_HISTORY.md create mode 100644 docs/api.md create mode 100644 docs/closed_items.md create mode 100644 docs/contributing.md create mode 100644 docs/developer_tooling_update.md create mode 100644 docs/index.md rename local-data-platform/local_data_platform/store/source/near/__init__.py => docs/pr_description.md (100%) create mode 100644 docs/pr_reviews.md create mode 100644 docs/recipes.md delete mode 100644 docs/requirements.txt create mode 100644 docs/scripts/generate_issue_list.py create mode 100644 docs/user_issues.md create mode 100644 docs/wiki/ACTIVE_DOCS_URLS.md create mode 100644 docs/wiki/BRANCHES.md create mode 100644 docs/wiki/CONTENTS.md create mode 100644 docs/wiki/CONTRIBUTING.md create mode 100644 docs/wiki/DEVELOPMENT.md create mode 100644 docs/wiki/GITHUB.md create mode 100644 docs/wiki/PROBLEM_STATEMENT.md create mode 100644 docs/wiki/PRODUCT_DEVELOPMENT.md create mode 100644 docs/wiki/PROJECT_OVERVIEW.md create mode 100644 docs/wiki/PROJECT_STRUCTURE.md create mode 100644 docs/wiki/RECIPES.md create mode 100644 docs/wiki/TECHNICAL_SPECIFICATIONS.md create mode 100644 docs/wiki/VISION.md create mode 100644 docs/wiki/business/MARKETING_DATA_ANALYSIS.md create mode 100644 docs/wiki/business/PROPOSAL.md create mode 100644 docs/wiki/library/BASE.md create mode 100644 docs/wiki/library/EXCEPTION.md create mode 100644 docs/wiki/library/TEST.md create mode 100644 docs/wiki/plan/ISSUES.md create mode 100644 docs/wiki/usecases/PHOTO_MANAGEMENT.md create mode 100644 docs/wiki/workflow/PHOTO_MANAGEMENT.md create mode 100644 how_to_setup.md rename {local-data-platform/local_data_platform => local_data_platform}/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/catalog/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/catalog/local/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/catalog/local/iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/engine/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/etl.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/exceptions.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/format/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/format/csv/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/format/iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/format/parquet/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/hello_world.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/issue/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/logger.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/egression/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/egression/csv_to_iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/egression/iceberg_to_csv/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/ingestion/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/ingestion/bigquery_to_csv/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/ingestion/csv_to_iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/ingestion/parquet_to_iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/pipeline/ingestion/pyarrow/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/source/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/source/gcp/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/source/gcp/bigquery/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/source/json/__init__.py (100%) create mode 100644 local_data_platform/store/source/near/__init__.py rename {local-data-platform/local_data_platform => local_data_platform}/store/source/parquet/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/target/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/store/target/iceberg/__init__.py (100%) rename {local-data-platform/local_data_platform => local_data_platform}/tmp/warehouse/pyiceberg_catalog.db (100%) create mode 100644 scripts/__init__.py create mode 100644 scripts/append_pr_history.py create mode 100644 scripts/fetch_closed_items.py create mode 100644 scripts/fetch_rtd_urls.py create mode 100644 scripts/generate_issue_list.py create mode 100644 scripts/github_api.py create mode 100644 src/local_data_platform/__init__.py create mode 100644 src/local_data_platform/github/__init__.py create mode 100644 src/tmp/warehouse/pyiceberg_catalog.db create mode 100644 tests/__init__.py create mode 100644 tests/test_github.py create mode 100644 tests/test_json_source.py create mode 100644 tests/test_placeholder.py diff --git a/.github/workflows/append_pr_history.yml b/.github/workflows/append_pr_history.yml new file mode 100644 index 0000000..dda7a64 --- /dev/null +++ b/.github/workflows/append_pr_history.yml @@ -0,0 +1,43 @@ +# This GitHub Actions workflow appends PR history to docs/PR_HISTORY.md after a PR is merged into main or a release branch. + +name: Append PR History + +on: + pull_request: + types: [closed] + branches: + - main + - 'release/**' + +jobs: + append-pr-history: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write # Required to push a commit + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Ensure full git history for diff + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Append PR history + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_MERGER: ${{ github.event.pull_request.merged_by.login }} + PR_DESCRIPTION: ${{ github.event.pull_request.body }} + run: python scripts/append_pr_history.py + + - name: Commit and push PR_HISTORY.md + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add docs/PR_HISTORY.md + git commit -m "docs: update PR history for PR #${{ github.event.pull_request.number }}" || echo "No changes to commit" + git push \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..089cccf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI - Lint, Test, and Docs + +on: + pull_request: + branches: [ main, fix-readthedocs, docs-sidebar-recipes-from-fix-readthedocs ] + push: + branches: [ main, fix-readthedocs, docs-sidebar-recipes-from-fix-readthedocs ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install --with dev,docs + - name: Lint with flake8 + run: poetry run flake8 src/ tests/ + + test: + runs-on: ubuntu-latest + needs: lint + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install --with dev,docs + - name: Run tests + run: poetry run pytest tests/ + + docs: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install --with docs + - name: Build documentation + run: poetry run mkdocs build --strict + +# Only allow merge if all jobs succeed (enforced by branch protection rules in GitHub settings) diff --git a/.github/workflows/mkdocs_link_check.yml b/.github/workflows/mkdocs_link_check.yml new file mode 100644 index 0000000..367dbe0 --- /dev/null +++ b/.github/workflows/mkdocs_link_check.yml @@ -0,0 +1,37 @@ +# This workflow checks for broken links in the built MkDocs site using mkdocs-htmlproofer-plugin. +# It runs on every push and pull request to main and release branches. + +name: MkDocs Broken Link Check + +on: + push: + branches: + - main + - 'release/**' + pull_request: + branches: + - main + - 'release/**' + +jobs: + link-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + pip install -r requirements.txt || true + pip install mkdocs-htmlproofer-plugin + + - name: Build docs and check links + run: | + mkdocs build diff --git a/.gitignore b/.gitignore index 6c44f1e..59814a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,50 +1,8 @@ -# Virtual Environment -.venv/ - -# Python cache -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -build/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# MkDocs -site/ +.idea/local-data-platform.iml +.idea/misc.xml +.idea/modules.xml +.idea/vcs.xml +.idea/workspace.xml +**/__pycache__/ docs/_build/ - -# Pytest -.pytest_cache/ -.mypy_cache/ -.coverage - -# OS / Editor specific -.DS_Store -.idea/ -*.swp - -# Temporary files -*.bak - -# Temporary directories -src/tmp/ -src/local_data_platform/tmp/ - -# Old config files -mkdocs.yaml \ No newline at end of file +site/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7741bba --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +# Makefile for the local-data-platform project + +.PHONY: help all install reinstall lint test docs serve-docs clean generate-docs verify-pymdownx + +# Default target to show help. +help: + @echo "Usage: make [target]" + @echo "" + @echo "Targets:" + @echo " all Run all quality checks (lint and test)." + @echo " help Show this help message." + @echo " install Install project dependencies using Poetry." + @echo " reinstall Force a clean re-installation of all dependencies." + @echo " lint Run flake8 linter on the project." + @echo " test Run pytest tests." + @echo " generate-docs Generate dynamic documentation content (e.g., issue lists)." + @echo " docs Build the MkDocs documentation." + @echo " serve-docs Build and serve the documentation locally on http://localhost:8000." + @echo " clean Remove temporary build files and caches." + @echo " verify-pymdownx Verify a specific documentation dependency." + +POETRY_RUN := poetry run + +# ============================================================================== +# Development Setup +# ============================================================================== + +install: + @echo "--> Installing dependencies with Poetry..." + poetry install --with dev,docs + +reinstall: + @echo "--> Removing existing virtual environment to ensure a clean state..." + @poetry env remove $$(poetry env info --path) || echo "No virtualenv found to remove, continuing..." + @echo "--> Reinstalling all dependencies from scratch..." + @$(MAKE) install + +# ============================================================================== +# Quality & Testing +# ============================================================================== + +all: lint test + @echo "--> All quality checks passed successfully." + +lint: + @echo "--> Linting with flake8..." + $(POETRY_RUN) flake8 src/ tests/ + +test: + @echo "--> Running tests with pytest..." + $(POETRY_RUN) pytest tests/ + +# ============================================================================== +# Documentation +# ============================================================================== + +generate-docs: + @echo "--> Generating dynamic documentation content..." + $(POETRY_RUN) python3 docs/scripts/generate_issue_list.py + +docs: + @$(MAKE) generate-docs + @echo "--> Building documentation..." + $(POETRY_RUN) mkdocs build --strict + +serve-docs: + @$(MAKE) generate-docs + @echo "--> Serving documentation..." + $(POETRY_RUN) mkdocs serve + +# ============================================================================== +# Cleaning +# ============================================================================== + +clean: + @echo "--> Cleaning up build artifacts and caches..." + @find . -type f -name "*.py[co]" -delete + @find . -type d -name "__pycache__" -exec rm -rf {} + + @rm -rf .pytest_cache .mypy_cache build dist *.egg-info site + +# Verify if pymdownx.toc is importable within the Poetry environment +verify-pymdownx: + @echo "--> Verifying pymdownx.toc installation..." + $(POETRY_RUN) python3 -c "import pymdownx.toc" || echo "pymdownx.toc not found. Please run 'make install'." \ No newline at end of file diff --git a/README.md b/README.md index cbadcd2..6db1df9 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,13 @@ -Dear User # Local Data Platform -### Explain this to me like I am five -Imagine you have a toy box where you keep all your favorite toys. -A local data platform is like that toy box, but for storing and -organizing important information instead of toys. -Just like how your toy box, -**a local data platform keeps all your data** -(like pictures, documents, and other info) **in one place -so you can easily find, use and manage it.** -It's really handy for keeping everything organized and in one spot! 🌟📦 +**local-data-platform** is a Python library to build, test, and run a complete data platform on your local machine. The core idea is to provide a "toy box for data"—a local environment where you can manage the entire data lifecycle, from ingestion to reporting, before needing to scale up to a cloud environment. -Got it? What else are you curious about? +This approach allows developers and businesses to save on cloud infrastructure costs during the initial development and testing phases, with a clear path for future scaling. > **Vision:** Local Data Platform is used as a python library to learn > and operate data lake house locally.
> **Mission:** Develop a python package which provides solutions for all stages -> of data organisation, ranging from ingestion to reporting. -> The goal is that one can build data pipeline locally, test and -> easily scale up to cloud.
->
-> **By 2025,** local-data-platform is a python package that uses open source -> tools to orchestrate a data platform operation, locally, for development -> and testing.
+> of data organisation, ranging from ingestion to reporting. The goal is that one can build data pipelines locally, test them, and easily scale up to the cloud. ## Problem Statement @@ -41,127 +26,38 @@ This will help you understand how to read the repository. \ ## Directory Structure -### local-data-platform/ `repository` -- **.github/** `hidden folder` - - ISSUE-TEMPLATE/ `samples` - - bug_report.md `Report bugs here` - - custom.md `Report ad hoc issues here` - - feature_request.md `Request a new feature here` - - pull_request_template.md `Raise a pull request on the repo` - -- **docs/** `Documentation for Read the Docs` - -- **local-data-platform** `package` - - local_data_platform `library` - - hello_world.py `module` - - hello_world `function` - - prints 'Hello, world!' `output` - -- **samples/** `tutorials` - - bigQueryTutorial.py `Demo bigQuery compatibility here` -- .gitignore `Mention files to ignore in your PR` -- .readthedocs.yaml `Configuration for Read the Docs` -- LICENSE `for legal purposes` -- lumache.py `Template used in Sphinx projects for Read the Docs` -- pyproject.toml `template configuration` -- README.md `How to understand the repo` -- README.rst `Configuration for Read the Docs` +The project follows a standard `src` layout for Python packages. Key directories include: +- **src/local_data_platform/**: The main source code for the library. +- **docs/**: MkDocs documentation sources. +- **tests/**: The Pytest test suite. ## How to test Pre-release as a User -1. Check the directory structure \ -`ls` -2. Change directory to local-data-platform -`cd local-data-platform` -2. Install the dependencies listed in your pyproject.toml file \ -`$poetry install` -2. Execute your test suite to ensure everything is working as expected \ -`poetry run pytest` -3. Run hello world command \ -`poetry run python hello_world.py` - - - -## Package structure -- **local-data-platform** `package` - - **dist** `Package distribution files` - - **docs** `Documentation` - - **local_data_platform** `library` - - **catalog** `Catalog your data` - - **local** `Catalog your data locally` - - **iceberg** `Catalog your data in iceberg SQL lite db` - - export.py `Export your catalog data to csv` - - **cloud** `Interact with cloud service providers` - - **gcp** `Interact with Google Cloud Platform` - - **login** `Login to GCP to get API credentials` - - **engine** `Underlying processing Tech` - - **format** `Supported formats for storage` - - **csv** `Supports Google sheets and Excel sheets` - - **iceberg** `Supports Apache Iceberg` - - **parquet** `Supports Apache Parquet` - - **issue** `Github Issues` - - **pipeline** `Data Pipeline` - - **egression** `Downstream pipelines` - - **csv_to_iceberg** `Raw to Silver Layer` - - **iceberg_to_csv** `Silver Layer to Gold Layer` - - **ingestion** `Upstream pipelines` - - **bigquery_to_csv** `Source to Raw` - - **csv_to_iceberg** `Raw to Silver Layer` - - **paraquet_to_iceberg** `Raw to Silver Layer` - - **scraper** `HTML to CSV` - - **store** `Data store` - - **source** `Source data class` - - **gcp** `GCP Storage` - - **bigquery** `GCP service` - - **json** `Local JSON file` - - **near** `NEAR Data Lake` - - **parquet** `Local Parquet file` - - **target** `Target data class` - - **iceberg** `Local Data Lake house` - - etl.py `Sample pipeline` - - exceptions.py `Known limitations` - - hello_world.py `Test Feature` - - is_function.py `Query Library Functions` - - logger.py `Library logger` - - **real_world_use_cases** `User Test Cases` - - **near_data_lake** `NEAR Coin Transactions` - - **config** `Pipeline configurations` - - **sample_queries** `NEAR Data Lake Transaction Table` - - near_transaction.json `Query List` - - egression.json `Loading data in local data lake house` - - ingestion.json `Extracting data from NEAR data lake house` - - **data** `target path` - - **near_transactions.db** `Local data lake house` - - **transactions** `iceberg table` - - **data** `table records` - - **metadata** `iceberg table metadata` - - near_transactions_catalog.db `iceberg local data catalog` - - **reports** `Production analysis` - - get_data.py `Get insights` - - put_data.py `Refresh Gold Layer` - - near_transactions.csv `Output` - - **nyc_yello_taxi_dataset** `NYC Yello Taxis Rides` - - **config** `Pipeline configurations` - - egression.json `Loading data in local data lake house` - - egression_payments.json `Loading payments report in Gold Layer` - - ingestion.json `Extracting data from local parquet file` - - **data** `target path` - - **nyc_yello_taxi_dataset.db** `Local data lake house` - - **rides** `iceberg table` - - **data** `table records` - - **metadata** `iceberg table metadata` - - nyc_yellow_taxi_dataset_catalog.db `iceberg local data catalog` - - nyc_yellow_taxi_rides.csv `Ouput` - - **reports** `Production analysis` - - export_catalog.py `Saves local iceberg catalog in CSV` - - get_data.py `Create Gold Layer` - - get_report.py `Updates Gold Layer` - - put_data.py `Refreshes Gold Layer` - - monthly_reporting.md `Report in MD` - - **tests** `PyTest Unit testing` - - test_gcp_connection.py `Testing GCP Login` +1. **Clone the repository:** + ```bash + git clone https://github.com/tusharchou/local-data-platform.git + cd local-data-platform + ``` +2. **Install dependencies:** + This project uses Poetry for dependency management. Use the Makefile for convenience. + ```bash + make install + ``` +3. **Run the tests:** + ```bash + make test + ``` + +## Package Modules + +The library's main modules are located in `src/local_data_platform`. Key modules include: + +* **`store`**: Handles data storage and interaction with sources. +* **`pipeline`**: Provides tools for building ETL pipelines. +* **`catalog`**: Manages data cataloging with Apache Iceberg. +* **`cloud`**: Contains components for interacting with cloud services. ## Plan | Milestone | Epic | Target Date | Delivery Date | Comment | @@ -182,22 +78,17 @@ This will help you understand how to read the repository. \ ### Releases -- [x] 0.1.0 : Done- Published Library on [PyPI](https://pypi.org/project/local-data-platform/) - -- [ ] 0.1.1 : In Progress- [Demo BigQuery compatibility](https://github.com/tusharchou/local-data-platform/milestone/2) +#### Completed +- **v0.1.0**: Initial release on [PyPI](https://pypi.org/project/local-data-platform/). +- **v0.1.1**: Implemented data ingestion and improved documentation. -- [x] 0.1.1 : Done- [Documentation: Updated README to explain clearly problem and plan of excecution](https://github.com/tusharchou/local-data-platform/issues/6) +#### Upcoming -- [ ] 0.1.2 : To-do- [Warehousing: DuckDB, Iceberg, DBT](https://github.com/tusharchou/local-data-platform/milestone/5) -- [ ] 0.1.3 : To-do- [Orchestration](https://github.com/tusharchou/local-data-platform/milestone/6) -- [ ] 0.1.4 : To-do- [Self Serving Gold Layer](https://github.com/tusharchou/local-data-platform/milestone/11) -- [ ] 0.1.5 : To-do- [Monitoring](https://github.com/tusharchou/local-data-platform/milestone/10) -- [ ] 0.1.6 : To-do- [Business Intelligence Reporting Dashboard](https://github.com/tusharchou/local-data-platform/milestone/9) -- [ ] 0.1.7 : To-do- [Data Science Insights](https://github.com/tusharchou/local-data-platform/milestone/8) -- [ ] 0.1.8 : To-do- [LLM](https://github.com/tusharchou/local-data-platform/milestone/7) -- [ ] 0.1.9 : To-do- [Launch Documentation](https://github.com/tusharchou/local-data-platform/milestone/2) -- [ ] 0.2.0 : To-do- [Cloud Integration](https://github.com/tusharchou/local-data-platform/milestone/3) -- [ ] 1.0.0 : To-do- Product +- **v0.1.2**: Warehousing with DuckDB, Iceberg, and dbt. +- **v0.1.3**: Pipeline orchestration. +- **v0.1.9**: Full documentation launch. +- **v0.2.0**: Cloud integration features. +- **v1.0.0**: Production-ready release. ### References diff --git a/docs/Makefile b/docs/Makefile index d4bb2cb..c84ded3 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,20 +1,80 @@ -# Minimal makefile for Sphinx documentation -# +# Makefile for the local-data-platform project -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build +.PHONY: help all install reinstall lint test docs serve-docs clean generate-docs verify-pymdownx -# Put it first so that "make" without argument is like "make help". +# Default target to show help. help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @echo "Usage: make [target]" + @echo "" + @echo "Targets:" + @echo " all Run all quality checks (lint and test)." + @echo " help Show this help message." + @echo " install Install project dependencies using Poetry." + @echo " reinstall Force a clean re-installation of all dependencies." + @echo " lint Run flake8 linter on the project." + @echo " test Run pytest tests." + @echo " generate-docs Generate dynamic documentation content (e.g., issue lists)." + @echo " docs Build the MkDocs documentation." + @echo " serve-docs Build and serve the documentation locally on http://localhost:8000." + @echo " clean Remove temporary build files and caches." + @echo " verify-pymdownx Verify a specific documentation dependency." -.PHONY: help Makefile +POETRY_RUN := poetry run -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) +# ============================================================================== +# Development Setup +# ============================================================================== + +install: + @echo "--> Installing dependencies with Poetry..." + poetry install --with dev,docs + +reinstall: + @echo "--> Removing existing virtual environment to ensure a clean state..." + @poetry env remove $$(poetry env info --path) || echo "No virtualenv found to remove, continuing..." + @echo "--> Reinstalling all dependencies from scratch..." + @$(MAKE) install + +# ============================================================================== +# Quality & Testing +# ============================================================================== + +all: lint test + @echo "--> All quality checks passed successfully." + +lint: + @echo "--> Linting with flake8..." + $(POETRY_RUN) flake8 src/ tests/ + +test: + @echo "--> Running tests with pytest..." + $(POETRY_RUN) pytest tests/ + +# ============================================================================== +# Documentation +# ============================================================================== + +docs: + @$(MAKE) clean + @echo "--> Building documentation..." + $(POETRY_RUN) mkdocs build --strict + +serve-docs: + @$(MAKE) clean + @echo "--> Serving documentation..." + $(POETRY_RUN) mkdocs serve + +# ============================================================================== +# Cleaning +# ============================================================================== + +clean: + @echo "--> Cleaning up build artifacts and caches..." + @find . -type f -name "*.py[co]" -delete + @find . -type d -name "__pycache__" -exec rm -rf {} + + @rm -rf .pytest_cache .mypy_cache build dist *.egg-info site + +# Verify if pymdownx.toc is importable within the Poetry environment +verify-pymdownx: + @echo "--> Verifying pymdownx.toc installation..." + $(POETRY_RUN) python3 -c "import pymdownx.toc" || echo "pymdownx.toc not found. Please run 'make install'." \ No newline at end of file diff --git a/docs/PR_HISTORY.md b/docs/PR_HISTORY.md new file mode 100644 index 0000000..e5af010 --- /dev/null +++ b/docs/PR_HISTORY.md @@ -0,0 +1,5 @@ +# Pull Request History + +This page provides a chronological history of all pull requests merged into the main or release branches. It is automatically updated by a GitHub Action after each merge. + +--- \ No newline at end of file diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..7637dc3 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,5 @@ +# API Docs + +This section provides a detailed reference for the `local-data-platform` public API. + +::: local_data_platform \ No newline at end of file diff --git a/docs/closed_items.md b/docs/closed_items.md new file mode 100644 index 0000000..a1b586d --- /dev/null +++ b/docs/closed_items.md @@ -0,0 +1,25 @@ +# Recently Closed Items + +This page lists the most recently closed Pull Requests and Issues. + +## Recently Closed Pull Requests + +- **[PR #95](https://github.com/tusharchou/local-data-platform/pull/95)**: 0.1.1 fix(deps): introducing mkdocs (by @tusharchou) +- **[PR #92](https://github.com/tusharchou/local-data-platform/pull/92)**: Add Recipes Page, Sidebar Navigation, and JSON Reading Test to Documentation (by @tusharchou) +- **[PR #2](https://github.com/tusharchou/local-data-platform/pull/2)**: 0.1.1 Iceberg Python Lake House: Testing pyiceberg 0.7.1 (by @tusharchou) +- **[PR #86](https://github.com/tusharchou/local-data-platform/pull/86)**: 0.1.1 Pytest Added for BigQuery Source (by @mrutunjay-kinagi) +- **[PR #25](https://github.com/tusharchou/local-data-platform/pull/25)**: 0.1.1 19 local_data_platform.source.near.bigquery.get(query) (by @mrutunjay-kinagi) +- **[PR #85](https://github.com/tusharchou/local-data-platform/pull/85)**: Release v1.1 bug fix (by @mrutunjay-kinagi) +- **[PR #73](https://github.com/tusharchou/local-data-platform/pull/73)**: Github actions setup for release v1.1 (by @mrutunjay-kinagi) +- **[PR #84](https://github.com/tusharchou/local-data-platform/pull/84)**: Release v1.1 fix (by @mrutunjay-kinagi) +- **[PR #81](https://github.com/tusharchou/local-data-platform/pull/81)**: Update pyproject.toml (by @redpheonixx) +- **[PR #80](https://github.com/tusharchou/local-data-platform/pull/80)**: Update publish.yml (by @redpheonixx) +- **[PR #79](https://github.com/tusharchou/local-data-platform/pull/79)**: Update publish.yml (by @redpheonixx) +- **[PR #4](https://github.com/tusharchou/local-data-platform/pull/4)**: 0.1.1 Real World Use case: Fact table (by @tusharchou) +- **[PR #78](https://github.com/tusharchou/local-data-platform/pull/78)**: Update publish.yml (by @redpheonixx) +- **[PR #7](https://github.com/tusharchou/local-data-platform/pull/7)**: 0.1.1 Draft README.md (by @tusharchou) +- **[PR #61](https://github.com/tusharchou/local-data-platform/pull/61)**: Create manual.yml (by @tusharchou) + +## Recently Closed Issues + +- **[Issue #1](https://github.com/tusharchou/local-data-platform/issues/1)**: 0.1.2 Testing pyiceberg 0.8.1 feature requests (by @tusharchou) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 89d6367..e69de29 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,63 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = "My Personal Data Project" -copyright = "2024, Local Data Platform core team" -author = "Local Data Platform core team" - - -# -- General configuration --------------------------------------------------- -# -- General configuration - -extensions = [ - "sphinx.ext.duration", - "sphinx.ext.doctest", - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.intersphinx", -] - -intersphinx_mapping = { - "rtd": ("https://docs.readthedocs.io/en/stable/", None), - "python": ("https://docs.python.org/3/", None), - "sphinx": ("https://www.sphinx-doc.org/en/master/", None), -} -intersphinx_disabled_domains = ["std"] - -templates_path = ["_templates"] - -# -- Options for EPUB output -epub_show_urls = "footnote" - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..b5c789e --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,28 @@ +# Contributing to Local Data Platform + +We're thrilled that you're interested in contributing to the Local Data Platform! Your help is essential for keeping it great. + +This section provides guidelines for contributing to the project. Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. + +## How to Get Started + +If you're new to the project, the best place to start is the `how_to_setup.md` guide located in the root of the repository. This will walk you through cloning the project and setting up your local development environment. + +Once you're set up, you can explore the other pages in this section to learn how to report issues or request features. + +## Submitting Pull Requests + +We follow a standard "fork and pull" model for contributions. To submit a change, please follow these steps: + +1. **Create a Fork**: Fork the repository to your own GitHub account. +2. **Create a Branch**: Create a new branch from `main` in your fork for your changes. Please use a descriptive branch name (e.g., `feat/add-new-ingestion-source` or `fix/docs-build-error`). +3. **Make Your Changes**: Make your changes, ensuring you follow the project's coding style. +4. **Run Quality Checks**: Before committing, run all the local quality checks to ensure your changes don't introduce any issues. + ```sh + make all + ``` +5. **Commit Your Changes**: Commit your changes with a clear and descriptive commit message. We follow the Conventional Commits specification. +6. **Push to Your Fork**: Push your branch to your fork on GitHub. +7. **Open a Pull Request**: From your fork on GitHub, open a pull request to the `main` branch of the `tusharchou/local-data-platform` repository. + +Your PR will be reviewed by the maintainers, and once approved, it will be merged. Thank you for your contribution! \ No newline at end of file diff --git a/docs/developer_tooling_update.md b/docs/developer_tooling_update.md new file mode 100644 index 0000000..78ca898 --- /dev/null +++ b/docs/developer_tooling_update.md @@ -0,0 +1,27 @@ +# Update on Developer Experience Improvements + +**To:** Product Designer, Product Manager + +**From:** The Development Team + +**Subject:** Upcoming PR: `feat(dx): Add Makefile and setup guide #96` + +--- + +Hello Team, + +This is a quick update on an upcoming Pull Request that significantly improves our development workflow. This PR introduces a `Makefile` and a comprehensive `how_to_setup.md` guide. + +### For the Product Designer + +This change will make it much faster and more consistent for you to set up a local development environment. If you ever need to run the project locally to test a new design or component, the process will be simplified to just a couple of straightforward commands, reducing friction and getting you up and running in minutes. + +### For the Product Manager + +This initiative is focused on improving our overall Developer Experience (DX). A better DX directly translates to faster onboarding for new team members and more efficient development cycles for the entire team. By standardizing our setup and common commands, we reduce time spent on environment-related issues and can focus more on delivering features, ultimately leading to quicker and more predictable progress. + +### Next Steps + +No action is required from you on this PR. We wanted to keep you informed about this foundational improvement that will help streamline our development process for everyone involved. + +Please feel free to reach out if you have any questions! \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..1910660 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,30 @@ +# Local Data Platform + +A modern, modular, and developer-friendly platform for local data engineering, analytics, and experimentation. + +> Want to contribute? Check out the [**Contributing Guide**](contributing.md)! + +[Explore Recipes & Examples](recipes.md){ .md-button .md-button--primary } + +[View Open Issues](user_issues.md){ .md-button } + +--- + +## 🏆 Top Issues to Contribute On + + + + +| Title | Theme | Status | Comments | Votes | +|-------|-------|--------|----------|-------| +| Example: Add BigQuery Ingestion | Ingestion | In Progress | 5 | 12 | +| Example: Improve Error Handling | Core | Under Review | 3 | 8 | +| Example: Add Parquet Export | Egression | Planned | 2 | 6 | + +--- + +## 📋 Top PRs to Review + +For project managers and senior contributors, this section highlights key pull requests that are ready for review. + +[Review Open Pull Requests](pr_reviews.md) diff --git a/local-data-platform/local_data_platform/store/source/near/__init__.py b/docs/pr_description.md similarity index 100% rename from local-data-platform/local_data_platform/store/source/near/__init__.py rename to docs/pr_description.md diff --git a/docs/pr_reviews.md b/docs/pr_reviews.md new file mode 100644 index 0000000..3084852 --- /dev/null +++ b/docs/pr_reviews.md @@ -0,0 +1,31 @@ +# Reviewing Pull Requests + +This page provides an overview of significant pull requests that are ready for review. It helps project managers and senior developers track progress and ensure quality. + +--- + +## Example PR: Documentation and Testing Improvements + +This is an example of a pull request description to guide reviews. + +### Summary +This pull request enhances the documentation and testing for the `local-data-platform` project. The main improvements include: + +- Adding a `recipes.md` page to the documentation, featuring practical usage examples such as reading a JSON file and building a JSON-to-Parquet pipeline. +- Ensuring the recipes page appears in the sidebar/main navigation for easier access. +- Updating Sphinx and Markdown documentation structure for improved navigation and clarity. +- Adding a test (`tests/test_json_source.py`) to verify that the `JsonSource` class can read a JSON file as described in the documentation. +- Maintaining compatibility for documentation builds both locally and on Read the Docs. + +### How to Test +- Build the documentation locally: + ```sh + cd docs + make html + ``` + Verify that the "Recipes" page appears in the sidebar and renders correctly. + +- Run the test suite to ensure the new test passes: + ```sh + pytest tests/test_json_source.py + ``` \ No newline at end of file diff --git a/docs/recipes.md b/docs/recipes.md new file mode 100644 index 0000000..a80616c --- /dev/null +++ b/docs/recipes.md @@ -0,0 +1,27 @@ +# Recipes + +This section contains practical examples and step-by-step guides for using the Local Data Platform. + +--- + +## Reading a Local JSON File + +This recipe demonstrates how to use a `JsonSource` to read a local JSON file into a data structure. + +### Prerequisites + +- A local JSON file named `data.json` in your project directory. + +### Code Example + +```python +from local_data_platform.store import JsonSource + +def read_local_json(file_path: str): + json_source = JsonSource(path=file_path) + data = json_source.read() + print("Successfully read data:", data) + +if __name__ == "__main__": + read_local_json("data.json") +``` \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 10835d2..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,58 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: -# -# pip-compile docs/requirements.in -# -alabaster==0.7.12 - # via sphinx -babel==2.10.3 - # via sphinx -certifi==2024.7.4 - # via requests -charset-normalizer==2.1.0 - # via requests -docutils==0.17.1 - # via - # sphinx - # sphinx-rtd-theme -idna==3.7 - # via requests -imagesize==1.4.1 - # via sphinx -jinja2==3.1.4 - # via sphinx -markupsafe==2.1.1 - # via jinja2 -packaging==21.3 - # via sphinx -pygments==2.12.0 - # via sphinx -pyparsing==3.0.9 - # via packaging -pytz==2022.1 - # via babel -requests==2.32.2 - # via sphinx -snowballstemmer==2.2.0 - # via sphinx -sphinx==5.0.2 - # via - # -r requirements.in - # sphinx-rtd-theme -sphinx-rtd-theme==1.0.0 - # via -r requirements.in -sphinxcontrib-applehelp==1.0.2 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.0 - # via sphinx -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -urllib3==1.26.19 - # via requests diff --git a/docs/scripts/generate_issue_list.py b/docs/scripts/generate_issue_list.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/user_issues.md b/docs/user_issues.md new file mode 100644 index 0000000..aa742fd --- /dev/null +++ b/docs/user_issues.md @@ -0,0 +1,230 @@ +# Open Issues + +This page lists all open issues in the repository. Use the filters below to sort by status or theme. + + +--- +tags: + - Status - Planned + - Theme - General +--- +### #94 - design of readthedocs. +*Status: Planned | Theme: General* + +> Great job on getting the docs started. I have a couple of comments on the design of readthedocs. - Rename API Reference to API Docs - Rename User Guid... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #89 - Event driven User behaviour Analysis +*Status: Planned | Theme: General* + +> **Is your feature request related to a problem? Please describe.** User behaviour analysis can be a difficult problem to solve as the application conv... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #88 - Optimistic Concurrency Iceberg +*Status: Planned | Theme: General* + +> https://github.com/apache/iceberg-python/issues/819 @redpheonixx + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #75 - A python function to pull data from Snowflake +*Status: Planned | Theme: General* + +> Extension Request. Add Snowflake to LDP. Create a python function to fetch data from Snowflake. **Describe the solution you'd like** A clear and conci... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #59 - 0.1.8 Query this catalog through prompt +*Status: Planned | Theme: General* + +> # Feature Request: Catalog Search ## 4W1Hs **Who**: any business with listings need to expose search for user to quickly find the vendor **What**: a e... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #57 - 0.1.2 Warehousing: add duckdb, dbt and iceberg packages +*Status: Planned | Theme: General* + +> **Is your feature request related to a problem? Please describe.** No, its a separate release feature 0.1.2 warehousing which requires duckdb, dbt and... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #48 - 0.1.2 Warehousing: Excel to csv support +*Status: Planned | Theme: General* + +> Hi team Thanks for fixing the last issue raised #31! I am a beginner with Python and working with restaurant data. The data is extracted as an excel f... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #47 - 0.1.2 Reoccurring Customer Churn Analysis +*Status: Planned | Theme: General* + +> # Reporting ## Churn Analyisis This often involves identifying the percentage of customers who have stopped using a product or service over a certain ... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #45 - 0.1.4 DBT Transformation layer +*Status: Planned | Theme: General* + +> - [ ] Set up environment with dbt, DuckDB installed preferably a docker compose file - [ ] Configure dbt_project.yml file to support iceberg - [ ] Con... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #44 - 0.1.3 Orchestration using cron +*Status: Planned | Theme: General* + +> Source - #27 The Problem - In the absence of an automated task scheduler like cron, essential tasks that need to run periodically, such as data pulls ... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #42 - 0.1.3 Orchestration: using airflow +*Status: Planned | Theme: General* + +> writing orchestration code with Apache Airflow, along with documentation to ensure clarity and maintainability. + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #38 - 0.1.1 Demo: Questions by end user +*Status: Planned | Theme: General* + +> Thank you so much for writing this but it will help us understand the use case if you could answer the 5 Ws: 1. What is this? 2. Why is this? 3. Who w... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #33 - 0.1.1 Documentation: Read The Docs +*Status: Planned | Theme: General* + +> Inspiration : https://py.iceberg.apache.org/#installation to-do: - [ ] .README.md project overview - [ ] read the docs tutorial - [ ] pypi release not... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #29 - 0.1.1 Align on Product Framework +*Status: Planned | Theme: General* + +> **Start** It is important to understand **what we are solving and for who.** This library needs to be robust and should adapt based on the use case we... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #27 - 0.1.9 Blog the class diagram +*Status: Planned | Theme: General* + +> Answer to be added for what design principles were used to choose the class structure? + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #23 - 0.1.2 Implement Partitioning and Version Control +*Status: Planned | Theme: General* + +> You can optimize the table for queries by partitioning it based on relevant fields such as block_timestamp or signer_account_id. This will improve que... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #19 - 0.1.1 Create a Google BigQuery Client
 +*Status: Planned | Theme: General* + +> Use Python's BigQuery API to pull the data from the Near Protocol dataset. For example, this code queries the transactions table and retrieves the dat... + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #13 - 0.2.0 DosuBot : Github maintainer +*Status: Planned | Theme: General* + +> We need to check this out. https://github.com/apps/dosubot @tusharchou + +--- + +--- +tags: + - Status - Planned + - Theme - General +--- +### #5 - 0.1.1 Setup Wiki +*Status: Planned | Theme: General* + +> How to contribute to local-data-platform 0.1.2 release diff --git a/docs/wiki/ACTIVE_DOCS_URLS.md b/docs/wiki/ACTIVE_DOCS_URLS.md new file mode 100644 index 0000000..bf88b18 --- /dev/null +++ b/docs/wiki/ACTIVE_DOCS_URLS.md @@ -0,0 +1,69 @@ +# Active Hosted Docs URLs + +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/#getting-started](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/#getting-started) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/#hello-local-data-platform](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/#hello-local-data-platform) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-1](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-1) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-10](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-10) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-12](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-12) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-13](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-13) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-2](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-2) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-4](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-4) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-5](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-5) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-6](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-6) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-7](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-7) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-8](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-8) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-9](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#__codelineno-0-9) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#api-reference](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#api-reference) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#core-modules](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#core-modules) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#hello-world](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#hello-world) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.etl](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.etl) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.EngineNotFound](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.EngineNotFound) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.PipelineNotFound](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.PipelineNotFound) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.PlanNotFound](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.PlanNotFound) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.TableNotFound](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.exceptions.TableNotFound) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.hello_world](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.hello_world) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.hello_world.hello_world](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.hello_world.hello_world) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.logger](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.logger) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.pipeline](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.pipeline) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.store](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#local_data_platform.store) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#pipeline](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#pipeline) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#store](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/api/#store) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/#how-to-suggest-a-feature](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/#how-to-suggest-a-feature) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/#requesting-developer-features](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/developer_feature_requests/#requesting-developer-features) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-1](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-1) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-10](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-10) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-2](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-2) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-3](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-3) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-4](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-4) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-5](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-5) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-6](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-6) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-7](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-7) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-8](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-8) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-9](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-0-9) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-1](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-1) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-10](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-10) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-11](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-11) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-2](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-2) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-3](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-3) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-4](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-4) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-5](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-5) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-6](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-6) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-7](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-7) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-8](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-8) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-9](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-1-9) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-1](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-1) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-2](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-2) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-3](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-3) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-4](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-4) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-5](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#__codelineno-2-5) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#local-data-platform-recipes](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#local-data-platform-recipes) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#recipe-json-to-parquet-data-pipeline](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#recipe-json-to-parquet-data-pipeline) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#recipe-read-a-json-file](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#recipe-read-a-json-file) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#sample-json-file](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/recipes/#sample-json-file) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/user_issues/](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/user_issues/) +- [https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/user_issues/#open-issues](https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/user_issues/#open-issues) diff --git a/docs/wiki/BRANCHES.md b/docs/wiki/BRANCHES.md new file mode 100644 index 0000000..1a65380 --- /dev/null +++ b/docs/wiki/BRANCHES.md @@ -0,0 +1,22 @@ +# Branches in local-data-platform + +| Branch Name | Purpose | +|---------------------------------------------|--------------------------------------------------------------| +| main | Main production branch, stable releases | +| develop | Development integration branch (if used) | +| docs-sidebar-recipes | Docs: Add recipes page/sidebar navigation | +| docs-sidebar-recipes-from-fix-readthedocs | Docs: Recipes/sidebar, branched from fix-readthedocs | +| feat/developer-tooling | Developer tooling, Makefile, setup guide, etc. | +| fix-readthedocs | Fixes for Read the Docs build | +| fix/stabilize-dependencies | Dependency stabilization | +| problem-statement | Problem statement documentation | +| brmhastra-patch-1 | User patch/feature branch | +| redpheonixx-patch-1 ... patch-5 | User patch/feature branches | +| tusharchou-patch-1 ... patch-8 | User patch/feature branches | +| v0.1.1 | Release tag branch | +| 3-012-viewing-data-through-duck-db-from-iceberg | Feature: DuckDB/Iceberg integration | +| 51-011-supported-file-formats-and-io | Feature: Supported file formats and IO | +| dependabot/pip/docs/pip-e49d2f513e | Automated dependency update | +| test-pyiceberg-0-7-1 | Testing pyiceberg version 0.7.1 | + +> This table lists the main local and remote branches and their purposes. For user/feature branches, see the branch name for context. diff --git a/docs/wiki/CONTENTS.md b/docs/wiki/CONTENTS.md new file mode 100644 index 0000000..d9dfec0 --- /dev/null +++ b/docs/wiki/CONTENTS.md @@ -0,0 +1,17 @@ +# Local Data Platform Wiki + +## Contents + +- [Project Overview](/wiki/PROJECT_OVERVIEW) +- [Problem Statement](/wiki/PROBLEM_STATEMENT) +- [Technical Specifications](/wiki/TECHNICAL_SPECIFICATIONS) +- [Development & Documentation](/wiki/DEVELOPMENT) +- [Project Structure](/wiki/PROJECT_STRUCTURE) +- [Recipes & Examples](/wiki/RECIPES) +- [Contributing](/wiki/CONTRIBUTING) +- [Active Docs URLs](/wiki/ACTIVE_DOCS_URLS) +- [License](../LICENSE) + +--- + +See each file above for details on that section. diff --git a/docs/wiki/CONTRIBUTING.md b/docs/wiki/CONTRIBUTING.md new file mode 100644 index 0000000..663a9ad --- /dev/null +++ b/docs/wiki/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +1. Fork the repo and create your branch from `main`. +2. Ensure tests and docs build (`poetry run pytest`, `poetry run mkdocs serve`). +3. Open a pull request! diff --git a/docs/wiki/DEVELOPMENT.md b/docs/wiki/DEVELOPMENT.md new file mode 100644 index 0000000..a34043f --- /dev/null +++ b/docs/wiki/DEVELOPMENT.md @@ -0,0 +1,31 @@ +# Development & Documentation + +## Requirements + +- Python 3.8+ +- [Poetry](https://python-poetry.org/) +- [MkDocs](https://www.mkdocs.org/) (for documentation) + +## Setup + +```sh +# Install dependencies +poetry install + +# (Optional) Activate the poetry shell +poetry shell +``` + +## Running Tests + +```sh +poetry run pytest +``` + +## Building & Serving Documentation + +```sh +# Serve docs locally with MkDocs +poetry run mkdocs serve +# Then open http://127.0.0.1:8000 in your browser +``` diff --git a/docs/wiki/GITHUB.md b/docs/wiki/GITHUB.md new file mode 100644 index 0000000..fbb44bb --- /dev/null +++ b/docs/wiki/GITHUB.md @@ -0,0 +1,64 @@ +# Feature: A class to interact with the github API + +Okay, here's a prompt designed to be given to Gemini Code Assist, building on our previous discussion and leveraging its ability to generate multi-file projects and detailed code. + +--- + +**Gemini Code Assist Prompt:** + +"My project needs an object-oriented Python utility to interact with GitHub repositories, specifically for fetching issues and pull requests. I already have a shared low-level API fetching utility located at `scripts/github_api.py` which contains a `fetch_from_github(owner, repo, endpoint, params)` function that handles pagination and authentication via `GITHUB_TOKEN` environment variable. + +I need you to create a new Python package and populate it with two classes: `Repo` and `Item`. + +**Here are the requirements:** + +1. **Project Structure:** + * Create a new directory `local_data_platform`. + * Inside `local_data_platform`, create an empty `__init__.py` file to make it a package. + * Inside `local_data_platform`, create a new file named `github.py`. + +2. **`local_data_platform/github.py` content:** + + * **Import:** It should import `fetch_from_github` from `scripts.github_api`. + + * **`Repo` Class:** + * Represents a GitHub repository. + * `__init__(self, owner: str, name: str)`: Initializes with the repository owner (username or organization) and name. + * `__repr__(self)`: Provides a helpful string representation. + + * **`Item` Class:** + * Represents a single GitHub issue or pull request. + * `__init__(self, data: dict, repo: 'Repo')`: + * Takes a `data` dictionary (the raw JSON response for an issue/PR from GitHub API) and a `repo` object (an instance of the `Repo` class). + * Determines `self.type` as either `'issue'` or `'pull_request'` based on the presence of the `'pull_request'` key in `data`. + * Initializes common attributes directly from `data`: `number`, `title`, `html_url`, `state`, `created_at`, `updated_at`, `closed_at`, `user_login` (from `user.login`), and `labels`. + * `is_pr(self) -> bool`: Returns `True` if the item is a pull request, `False` otherwise. + * `is_issue(self) -> bool`: Returns `True` if the item is a regular issue, `False` otherwise. + * `__repr__(self)`: Provides a helpful string representation including type, number, title (truncated), state, and repo name. + * `__getattr__(self, name)`: Implement this to allow accessing any key from the underlying `_data` dictionary directly as an attribute (e.g., `item.body` or `item.assignee`). If the attribute doesn't exist in `_data`, raise an `AttributeError`. + + * **`@classmethod fetch_all_items(cls, repo: Repo, status: str = "open") -> list['Item']`**: + * This is the core fetching method. + * It should take a `Repo` object and an optional `status` string (defaulting to "open"). + * It must use the imported `fetch_from_github` function, passing `repo.owner`, `repo.name`, the endpoint `"issues"`, and `params={"state": status}`. (Note: The GitHub `/issues` endpoint returns both issues and PRs, which is suitable here). + * It should then iterate through the raw data returned by `fetch_from_github` and wrap each dictionary in an `Item` object, returning a list of `Item` instances. + * Include basic type validation for `repo` parameter. + +3. **Example Usage Script (`main.py`):** + * Create a `main.py` file in the project root (sibling to `local_data_platform` and `scripts`). + * It should demonstrate how to: + * Import `Repo` and `Item`. + * Define `REPO_OWNER` and `REPO_NAME` constants (e.g., "tusharchou", "local-data-platform"). + * Instantiate a `Repo` object. + * Call `Item.fetch_all_items` to get **open** items. + * Print the total count of open items. + * Separate the fetched items into `open_issues` and `open_prs` lists using the `is_issue()` and `is_pr()` methods. + * Print the counts for open issues and open PRs. + * Loop through and print the `number`, `title`, `type`, `state`, `user_login`, `created_at` (formatted), and `html_url` for a few (e.g., top 5) issues and PRs. + * Demonstrate fetching **closed** items as well. + * Show an example of using the `__getattr__` functionality (e.g., printing `item.body` if available). + * Include a note about setting the `GITHUB_TOKEN` environment variable for authenticated requests. + +Please provide the complete code for `local_data_platform/__init__.py`, `local_data_platform/github.py`, and `main.py`." + +--- diff --git a/docs/wiki/PROBLEM_STATEMENT.md b/docs/wiki/PROBLEM_STATEMENT.md new file mode 100644 index 0000000..6550aff --- /dev/null +++ b/docs/wiki/PROBLEM_STATEMENT.md @@ -0,0 +1,18 @@ +# Problem Statement + +| Question | Answer | +|----------|----------------------------------------------------------------------------------------------------| +| What? | a local data platform that can scale up to cloud | +| Why? | save costs on cloud infra and development time | +| When? | start of product development life cycle | +| Where? | local first | +| Who? | Business who wants a product data platform that will run locally and scale up when the time comes. | + + +# Problem Statement + +In today's data-rich world, individuals often face significant challenges managing, analyzing, and deriving meaningful insights from their personal data. Traditional "big data" solutions are typically complex, resource-intensive, and designed for enterprise-scale problems, rendering them inaccessible or overkill for personal use cases. Furthermore, pervasive privacy concerns frequently prevent individuals from leveraging convenient cloud-based tools for sensitive personal information, leading to data silos, missed opportunities for personal growth, and an inability to fully understand their own digital footprint. + +The **Local Data Platform (LDP)** directly addresses this critical gap. It empowers anyone with a laptop to harness the immense power of familiar Python big data libraries (such as Pandas, Dask, Polars, and more) to solve their unique personal data problems. LDP provides a structured, user-friendly, and private environment designed for individuals to research, process, and maintain their personal datasets entirely locally. This ensures unparalleled data privacy and guarantees that your valuable information remains alive and accessible precisely when you need to research or revisit it. + +By fostering a vibrant, community-driven approach, LDP enables users to collaborate on solutions, share best practices, and collectively evolve the platform to tackle a wide array of personal data challenges. Crucially, this collaborative growth occurs **without ever requiring users to share their sensitive raw data**, upholding the core principle of personal privacy. The ultimate goal is to cultivate a robust, accessible, and community-supported ecosystem for comprehensive personal data mastery, with all documentation and guidance readily available on the ReadTheDocs website. \ No newline at end of file diff --git a/docs/wiki/PRODUCT_DEVELOPMENT.md b/docs/wiki/PRODUCT_DEVELOPMENT.md new file mode 100644 index 0000000..056a81d --- /dev/null +++ b/docs/wiki/PRODUCT_DEVELOPMENT.md @@ -0,0 +1,28 @@ +# Product Developement + +# Problem Statement + +Github issues and PR are the soul of a project, so we should have a utility to pull and review them while working on the project. + +## Data Structure + +### github.Repo + +represents the repo + +### github.Item + +It can either be a PR or an issue. + +#### usage + +```python + +local_data_platfrom.github.item + +``` + +#### Methods + +fetch all items(status=OPEN) + diff --git a/docs/wiki/PROJECT_OVERVIEW.md b/docs/wiki/PROJECT_OVERVIEW.md new file mode 100644 index 0000000..286fd80 --- /dev/null +++ b/docs/wiki/PROJECT_OVERVIEW.md @@ -0,0 +1,10 @@ +# Project Overview + +**local-data-platform** is a Python library to build, test, and run a complete data platform on your local machine. The core idea is to provide a "toy box for data"—a local environment where you can manage the entire data lifecycle, from ingestion to reporting, before needing to scale up to a cloud environment. + +This approach allows developers and businesses to save on cloud infrastructure costs during the initial development and testing phases, with a clear path for future scaling. + +> **Vision:** Local Data Platform is used as a python library to learn +> and operate data lake house locally. +> **Mission:** Develop a python package which provides solutions for all stages +> of data organisation, ranging from ingestion to reporting. The goal is that one can build data pipelines locally, test them, and easily scale up to the cloud. diff --git a/docs/wiki/PROJECT_STRUCTURE.md b/docs/wiki/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..19a5b8f --- /dev/null +++ b/docs/wiki/PROJECT_STRUCTURE.md @@ -0,0 +1,32 @@ +# Project Structure + +``` +tusharchou/local-data-platform/ +├── local_data_platform/ +│ ├── __init__.py +│ ├── storage_base.py +│ └── in_memory_storage.py +├── scripts/ +│ ├── __init__.py +│ └── github_api.py +├── main.py +├── mkdocs.yml <-- NEW FILE (MkDocs Configuration) +├── docs/ <-- DIRECTORY +│ ├── index.md <-- CHANGED (Formerly index.rst) +│ ├── problem_statement.md <-- CHANGED (Formerly problem_statement.rst) +│ ├── installation.md <-- Placeholder for new page +│ ├── usage.md <-- Placeholder for new page +│ ├── api_reference.md <-- Placeholder for new page (for mkdocstrings) +│ ├── contributing.md <-- Placeholder for new page +│ └── community.md <-- Placeholder for new page +├── requirements-dev.txt <-- NEW FILE (For dev/docs dependencies) +└── README.md +``` + +## .gitignore + +The following are ignored to keep the repo clean: +- IDE/project files (`.idea/`) +- Python cache (`__pycache__/`) +- Sphinx build output (`docs/_build/`) +- MkDocs static site output (`site/`) diff --git a/docs/wiki/RECIPES.md b/docs/wiki/RECIPES.md new file mode 100644 index 0000000..08ea838 --- /dev/null +++ b/docs/wiki/RECIPES.md @@ -0,0 +1,5 @@ +# Recipes & Examples + +See [docs/recipes.md](../docs/recipes.md) for practical usage examples, including: +- Reading a JSON file +- Building a JSON-to-Parquet pipeline diff --git a/docs/wiki/TECHNICAL_SPECIFICATIONS.md b/docs/wiki/TECHNICAL_SPECIFICATIONS.md new file mode 100644 index 0000000..4d7b38e --- /dev/null +++ b/docs/wiki/TECHNICAL_SPECIFICATIONS.md @@ -0,0 +1,3 @@ +# Technical Specifications + +[Introduction to pyiceberg](https://medium.com/@tushar.choudhary.de/internals-of-apache-pyiceberg-10c2302a5c8b) diff --git a/docs/wiki/VISION.md b/docs/wiki/VISION.md new file mode 100644 index 0000000..0e50b42 --- /dev/null +++ b/docs/wiki/VISION.md @@ -0,0 +1,29 @@ +# Welcome to the Local Data Platform (LDP)! + +**Empowering Personal Data Mastery with Python.** + +The Local Data Platform (LDP) is a revolutionary open-source initiative designed to put the power of "big data" Python libraries directly into the hands of individuals. Whether you're a data enthusiast, a researcher, or simply someone looking to gain deeper insights from your personal information, LDP provides the tools and framework to do so securely and privately, right on your laptop. + +--- + +## Problem Statement + +_The full problem statement is detailed on the [Problem Statement](./PROBLEM_STATEMENT.md) page._ + +--- + +## Why Local? + +In an era where data privacy is paramount, LDP champions a local-first approach. Your data stays on your machine, under your control. This eliminates the need to upload sensitive information to third-party cloud services, giving you peace of mind while still enabling powerful analysis. + +## Key Features (Coming Soon!) + +* **Offline Capability:** Work with your data anywhere, anytime, without an internet connection. +* **Privacy by Design:** Your personal data never leaves your device unless you explicitly choose to share it. +* **Scalable Personal Analytics:** Leverage libraries like Pandas, Dask, Polars, and more for efficient processing of large datasets. +* **Community-Driven Solutions:** Collaborate with others to develop and share solutions for common personal data challenges. +* **Extensible Architecture:** Easily integrate new data sources, processing modules, and visualization tools. + +## Get Started + +Ready to take control of your personal data? Head over to our [Installation](installation.md) guide to set up LDP on your machine. \ No newline at end of file diff --git a/docs/wiki/business/MARKETING_DATA_ANALYSIS.md b/docs/wiki/business/MARKETING_DATA_ANALYSIS.md new file mode 100644 index 0000000..772b79a --- /dev/null +++ b/docs/wiki/business/MARKETING_DATA_ANALYSIS.md @@ -0,0 +1,69 @@ +# Agentic Marketing Analyser + +## Summary + +This feature proposes the integration of an AI-powered agent that delivers location-specific marketing insights to help tour and activity operators understand what’s working and what needs attention across their local digital channels. Powered by a Local Data Platform (LDP), the agent transforms raw marketing data into clear, actionable recommendations that drive bookings, visibility, and revenue. + +## Problem It Solves + +Most small to mid-sized tourism operators struggle with marketing decisions. Common pain points include: + +- "Which campaigns are working in which locations?" +- "What should I post next to drive more bookings?" +- "Why are bookings slow this week despite good reviews?" +- "Where should I focus marketing budget or effort?" + +They lack in-house marketing teams. What they need is a smart, always-on guide that understands local trends and acts like a data-driven marketing assistant. + +## Feature Summary + +The AI agent will: +- Aggregate social media, OTA, review, and campaign data. +- Analyze performance by product, region, season, and timing. +- Generate insights in natural GPT-style language. +- Recommend next best actions to improve visibility, conversions, and bookings. + +## Rollout Plan: 6-Month Delivery + +| Phase | Duration | Milestones | +| :--- | :--- | :--- | +| **Discovery & Planning** | 2 weeks | Source mapping, data schemas, risk planning | +| **Core Infrastructure Build** | 6 weeks | Pipeline development, LDP engine setup | +| **AI Layer + Prompt Engineering** | 6 weeks | Smart recommendation engine, insight model tests | +| **UI + Chatbot Integration** | 4 weeks | Marketing dashboard surfaces, chatbot responses | +| **Pilot & Feedback Loop** | 3 weeks | Testing in 3–5 cities with live operator data | +| **Launch & Handoff** | 2 weeks | Final training, documentation, and monitoring setup | + +## Cloud & Infra Cost (Client-Side Estimate) + +| Item | Estimated Allocation | +| :--- | :--- | +| Infra & Cloud Services (GPT-4 API, server infra, storage) | ₹1.5–2 Cr | +| Security, DevOps, Monitoring | ₹0.75 Cr | +| Contingency + Risk Buffer | ₹1.5 Cr | + +## Team Structure (10 Members) + +| Role | Count | Responsibilities | +| :--- | :--- | :--- | +| Data Engineers | 2 | API integration, pipeline design, enrichment | +| Backend Engineers | 2 | Core infra, LDP performance, uptime | +| AI/ML Engineers | 2 | GPT prompt design, training, insight generation | +| Product Manager | 1 | Roadmap, delivery, stakeholder alignment | +| Product Designer | 1 | UX/UI for dashboards, insight cards, chatbot flow | +| Frontend Engineers | 2 | Dashboard & chatbot UI integration | + +## Why This Will Work for You + +- You're sitting on a goldmine of marketing and booking data — this platform unlocks its value. +- AI makes local marketing insights human-readable and intuitive, not spreadsheet-heavy. +- You’ll never have to guess the right channel, time, or campaign again. +- Built to scale across cities, seasons, tours, and customer types. +- Seamlessly integrates with your existing product ecosystem. + +## Next Step + +> Let’s schedule a free 30-minute discovery session with your team to: +> - Walk through the use cases. +> - Review how it can be embedded in your product. +> - Map early adopters and go-to-market timeline. diff --git a/docs/wiki/business/PROPOSAL.md b/docs/wiki/business/PROPOSAL.md new file mode 100644 index 0000000..8ac8b8c --- /dev/null +++ b/docs/wiki/business/PROPOSAL.md @@ -0,0 +1,31 @@ +# Smarter Tools for Local Businesses, Designed to Save Costs + +*From pricing analytics to AI automation, we craft data-first solutions that actually work.* +> [**Talk to Us →**](#) + +--- + +## Our Solutions + +Explore our plug-and-play solutions designed for businesses in tourism, retail, food, and more. + +| Solutions | Description | Call to Action | +| :--- | :--- | :--- | +| **Agentic Marketing Analyser** | Understand user behavior and improve your product. | [View Details](#) | +| **Photo Management & Easy Sharing** | Organize and share your visual assets effortlessly. | [Explore](#) | +| **Fraud Detection** (Coming Soon) | Protect your business with our upcoming fraud detection tools. | [See Use Case](#) | + +--- + +## Why Choose Us? + +* **Customizable**: Tailored to your use case, not a bloated SaaS tool. +* **Affordable**: Pricing that respects early-stage teams. +* **Private & Secure**: 100% privacy-safe, your data stays with you. + +--- + +> ### 💡 Don’t see exactly what you need? +> Let’s co-create a tool for your business. +> +> [**Talk to Us →**](#) diff --git a/docs/wiki/library/BASE.md b/docs/wiki/library/BASE.md new file mode 100644 index 0000000..fdfccca --- /dev/null +++ b/docs/wiki/library/BASE.md @@ -0,0 +1,24 @@ +# Base + +The "base class" of a new Python library is a foundational component that sets the architectural standard for other classes within the library, especially when you anticipate multiple, varied implementations of a core concept. + +Its primary purpose is to define a common interface and/or shared functionality that derived classes will inherit or adhere to. + +Here's what should generally inform the design of a base class for a new Python library: + +## What is the Core Concept/Abstraction? + +What fundamental operation or entity does your library revolve around? (e.g., a Connector to different databases, a Parser for various file formats, a Strategy for different algorithms, a DataSource for different data origins). The base class should represent this abstraction. + +## What are the Common Operations/Interface? + +What actions or behaviors must any concrete implementation of this core concept provide? These will become your abstract methods. + +What common utility methods or shared logic can be provided directly by the base class to avoid code duplication in derived classes? These will be your concrete methods. + +### To Be Abstract or Not Abstract (ABCs): + +If you want to enforce an interface: Use an Abstract Base Class (ABC) from the abc module. This is highly recommended when you want to ensure that any class inheriting from your base class must implement certain methods. If a concrete class fails to implement an @abstractmethod, Python will raise a TypeError upon instantiation. + +Example Use Case: A StorageBase class with abstract get() and put() methods. Any new storage backend (e.g., S3Storage, FileSystemStorage) must provide these methods. + diff --git a/docs/wiki/library/EXCEPTION.md b/docs/wiki/library/EXCEPTION.md new file mode 100644 index 0000000..92807b6 --- /dev/null +++ b/docs/wiki/library/EXCEPTION.md @@ -0,0 +1,164 @@ +# Exception + +Exception handling is crucial for building robust, reliable, and user-friendly Python libraries. Good exception handling communicates issues clearly to the library user, helps with debugging, and prevents silent failures. + +Here are the best practices for exception handling in a Python library: + +1. **Don't Silence Exceptions (The Golden Rule):** + + * **Avoid `except: pass` or `except Exception: pass`.** This is the most common and dangerous anti-pattern. It hides bugs, makes debugging impossible, and leads to unexpected behavior in user applications. + * **Instead, at a minimum, log the exception and re-raise it, or transform it into a more specific, higher-level exception.** + +2. **Be Specific with `except` Clauses:** + + * Catch only the specific exceptions you expect and know how to handle. + * **Bad:** + ```python + try: + # network operation + except Exception as e: + print(f"An error occurred: {e}") + # This catches everything, including KeyboardInterrupt, SystemExit, etc. + ``` + * **Good:** + ```python + import requests + try: + response = requests.get("http://example.com/api") + response.raise_for_status() + except requests.exceptions.Timeout: + raise MyLibraryTimeoutError("API request timed out.") from None + except requests.exceptions.ConnectionError: + raise MyLibraryNetworkError("Could not connect to the API server.") from None + except requests.exceptions.HTTPError as e: + if e.response.status_code == 404: + raise MyLibraryResourceNotFoundError("Requested resource not found.") from None + else: + # Re-raise generic HTTP errors or wrap in a generic library error + raise MyLibraryAPIError(f"API returned an error: {e.response.status_code}") from e + except Exception as e: # Catching a broader Exception at the very end as a last resort + # Log the unexpected exception details for debugging + import logging + logging.exception("An unexpected error occurred in API call") + raise MyLibraryUnknownError("An unexpected error occurred.") from e + ``` + +3. **Raise Custom Exceptions:** + + * **Why:** This is paramount for libraries. Custom exceptions provide clear, semantic meaning to errors originating from your library. Users can then specifically catch *your* library's errors without accidentally catching unrelated issues from other parts of their application or other libraries. + * **How:** Create a base exception for your library, and then derive more specific exceptions from it. + ```python + # In your library's exceptions.py (or similar) + class MyLibraryError(Exception): + """Base exception for MyLibrary.""" + pass + + class MyLibraryConnectionError(MyLibraryError): + """Raised when a connection to a service fails.""" + pass + + class MyLibraryConfigError(MyLibraryError): + """Raised when the library configuration is invalid.""" + pass + + class MyLibraryResourceNotFoundError(MyLibraryError): + """Raised when a specific resource cannot be found.""" + pass + ``` + * **Usage:** + ```python + if not os.path.exists(config_path): + raise MyLibraryConfigError(f"Configuration file not found at: {config_path}") + ``` + +4. **Provide Clear and Informative Error Messages:** + + * When raising or re-raising an exception, the message should explain *what went wrong*, *why it went wrong*, and ideally, *how the user might fix it* (if applicable). + * Include relevant context: input values, file paths, IDs, error codes from external services. + * **Bad:** `raise MyLibraryError("Something went wrong.")` + * **Good:** `raise MyLibraryConnectionError(f"Failed to connect to {url}. Please check your network connection.")` + +5. **Use Exception Chaining (`raise ... from ...`):** + + * When you catch a lower-level exception and re-raise a new, higher-level (custom) exception, use `raise NewException(...) from OriginalException`. + * This preserves the original exception's traceback, providing a full "cause" chain, which is invaluable for debugging. + * Use `from None` if you *don't* want the original exception to be implicitly chained (e.g., if it's an internal detail you've fully handled and transformed). + + + + ```python + import json + class MyLibraryParseError(MyLibraryError): pass + + try: + data = json.loads(invalid_json_string) + except json.JSONDecodeError as e: + # Chaining preserves the original JSONDecodeError traceback + raise MyLibraryParseError("Failed to parse JSON data.") from e + ``` + +6. **Use `finally` for Cleanup:** + + * The `finally` block *always* executes, regardless of whether an exception occurred in the `try` block or not. + * This is ideal for releasing resources like file handles, network connections, database cursors, or locks. + + + + ```python + file_handle = None + try: + file_handle = open("my_file.txt", "w") + file_handle.write("Hello") + except IOError as e: + raise MyLibraryIOError("Could not write to file.") from e + finally: + if file_handle: + file_handle.close() # Guarantees the file is closed + ``` + +7. **Prefer `with` statements for Resource Management:** + + * For resources that support the context manager protocol (like files, locks, database connections), the `with` statement is generally preferred over `try-finally` for cleanup. It automatically handles `__enter__` and `__exit__` methods, ensuring resources are properly acquired and released even if exceptions occur. + + + + ```python + try: + with open("my_file.txt", "w") as f: + f.write("Hello") + # File is automatically closed here, even if f.write() failed + except IOError as e: + raise MyLibraryIOError("Could not write to file.") from e + ``` + +8. **Log Exceptions, Don't Print:** + + * Use Python's `logging` module instead of `print()` for debugging and operational messages. + * Logging allows users of your library to configure how and where messages are stored (console, file, syslog, etc.) and at what level of detail (DEBUG, INFO, WARNING, ERROR, CRITICAL). + * `logging.exception()` is particularly useful as it automatically includes traceback information. + + + + ```python + import logging + logger = logging.getLogger(__name__) # Or get a common library logger + + try: + # some risky operation + except SomeError as e: + logger.error(f"Failed operation due to: {e}") # Basic error message + logger.exception("Detailed traceback for debugging this failure:") # Full traceback + raise # Re-raise after logging + ``` + +9. **Design Your API Around Exceptions:** + + * Document the exceptions your public functions and methods might raise. This forms part of your library's contract with its users. Users need to know what errors to anticipate and handle. + * Avoid leaking internal implementation details via low-level exceptions. Wrap them in your library's custom exceptions. + +10. **Avoid Catching `BaseException`:** + + * `BaseException` is the root of *all* exceptions, including `SystemExit` (raised by `sys.exit()`) and `KeyboardInterrupt` (Ctrl+C). Catching `BaseException` will prevent your program from exiting cleanly or responding to interrupts. + * Generally, you should only catch `Exception` (which `SystemExit` and `KeyboardInterrupt` do *not* inherit from). + +By adhering to these best practices, you can create Python libraries that are robust, easy to debug, and provide a clear, predictable error handling experience for their users. \ No newline at end of file diff --git a/docs/wiki/library/TEST.md b/docs/wiki/library/TEST.md new file mode 100644 index 0000000..a597f63 --- /dev/null +++ b/docs/wiki/library/TEST.md @@ -0,0 +1,156 @@ +# Test + + +Testing is paramount for a Python library to ensure its correctness, reliability, maintainability, and ease of use for its consumers. A well-tested library inspires confidence and reduces the burden on its users. + +Here are the best practices for testing in a Python library: + +## I. Core Principles + +1. **Correctness:** Ensure the library behaves exactly as expected for all valid inputs and scenarios. +2. **Reliability:** Ensure the library handles edge cases, invalid inputs, and error conditions gracefully without crashing or producing incorrect results. +3. **Prevent Regressions:** Catch bugs introduced in new code changes that break existing functionality. +4. **Documentation:** Tests serve as executable documentation for how to use the library's public API. +5. **Maintainability:** Well-structured tests make it easier to refactor code confidently. + +## II. Types of Tests + +1. **Unit Tests:** + + * **Focus:** Test the smallest possible unit of code (a single function, method, or class) in isolation. + * **Isolation is Key:** All external dependencies (database calls, API requests, file system interactions, complex object dependencies) should be **mocked or stubbed** to ensure that only the unit under test is being validated. + * **Characteristics:** Fast, granular, easy to pinpoint failures. + * **Purpose:** Verify the correctness of individual algorithms and logic. + +2. **Integration Tests:** + + * **Focus:** Test how different units or components of your library interact with each other, or how your library interacts with external systems (e.g., a database, an external API, the file system). + * **Less Isolation:** These tests will involve actual interaction with some dependencies, though often with test-specific configurations (e.g., an in-memory database, a local mock server). + * **Characteristics:** Slower than unit tests, but provide higher confidence in the system's overall functionality. + * **Purpose:** Verify that components work together as intended. + +3. **End-to-End (E2E) Tests (Less common for pure libraries):** + + * If your library has a CLI, a web interface built on top of it, or is a full application, E2E tests would simulate real user scenarios from start to finish. For most pure libraries, integration tests often cover this scope. + +## III. Recommended Tools & Frameworks + +1. **`pytest` (Strongly Recommended):** + + * **Advantages:** Less boilerplate code, simple `assert` statements, powerful fixtures for setup/teardown, excellent plugin ecosystem (`pytest-cov` for coverage, `pytest-mock` for mocking, `pytest-xdist` for parallel execution). + * **Standard:** It's become the de-facto standard for Python testing due to its ease of use and flexibility. + +2. **`unittest` (Built-in):** + + * **Advantages:** Part of Python's standard library, no external dependencies needed. + * **Considerations:** More verbose syntax (`assertEqual`, `assertRaises`), class-based test suites. Good for simpler projects or when external dependencies are strictly forbidden. + +## IV. Best Practices for Writing Tests + +1. **Test Public APIs (Interface, not Implementation):** + + * Focus on testing the functions, classes, and methods that users of your library will directly interact with. + * Avoid testing private or internal helper functions directly unless they contain complex, isolated logic that warrants their own unit tests. If you refactor internals, these tests shouldn't break. + +2. **Test Isolation and Mocks:** + + * **Rule:** Each test should run independently of others and produce the same result every time, regardless of the order of execution. + * **Mocks:** For unit tests, use mocking libraries (`unittest.mock` or `pytest-mock`) to simulate the behavior of external dependencies or complex internal objects. This keeps tests fast and prevents failures due to external factors. + * **Example (using `pytest-mock`):** + ```python + def test_fetch_data_from_api(mocker): + mock_response = mocker.Mock() + mock_response.json.return_value = {"key": "value"} + mocker.patch('requests.get', return_value=mock_response) # Mock requests.get + + result = my_library.fetch_data() # Your library function that calls requests.get + assert result == {"key": "value"} + ``` + +3. **Clear, Readable, and Self-Contained Tests:** + + * **Arrange-Act-Assert (AAA) Pattern:** + * **Arrange:** Set up the test environment (input data, mocks, initial state). + * **Act:** Execute the code under test. + * **Assert:** Verify the outcome (return values, side effects, exceptions raised). + * **Meaningful Test Names:** Test function names should clearly indicate what scenario they are testing and what the expected outcome is (e.g., `test_add_two_positive_numbers_returns_sum`, `test_parse_empty_string_raises_value_error`). + +4. **Test Edge Cases and Error Conditions:** + + * Test with: `None` values, empty strings/lists/dictionaries, boundary conditions (min/max values), invalid inputs, files that don't exist, network errors, permissions issues, etc. + * **Testing Exceptions:** Assert that the correct exceptions are raised under specific conditions. + ```python + import pytest + def test_divide_by_zero_raises_zero_division_error(): + with pytest.raises(ZeroDivisionError, match="division by zero"): + 1 / 0 + ``` + +5. **Use Fixtures Wisely (`pytest`):** + + * Fixtures provide a clean way to set up preconditions for tests (e.g., creating temporary files, setting up a database connection, providing pre-initialized objects). + * They promote code reuse and improve readability by centralizing setup/teardown logic. + * **Example:** + ```python + import pytest + import tempfile + + @pytest.fixture + def temp_file_path(): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp: + tmp.write("test content") + file_path = tmp.name + yield file_path # Provide the path to the test + os.remove(file_path) # Clean up after the test + + def test_read_from_temp_file(temp_file_path): + with open(temp_file_path, 'r') as f: + content = f.read() + assert content == "test content" + ``` + +6. **Parameterized Tests (`pytest.mark.parametrize`):** + + * When you have a function that needs to be tested with multiple sets of inputs and expected outputs, parameterization reduces code duplication. + + + + ```python + import pytest + + @pytest.mark.parametrize("input_a, input_b, expected_sum", [ + (1, 2, 3), + (0, 0, 0), + (-1, 5, 4), + (100, 200, 300) + ]) + def test_add_function(input_a, input_b, expected_sum): + assert (input_a + input_b) == expected_sum + ``` + +7. **Strive for High Test Coverage (But Don't Obsess):** + + * Use tools like `pytest-cov` (or `coverage.py`) to measure test coverage. Aim for a high percentage (e.g., 80-90%+ for core logic). + * **Caution:** High coverage doesn't guarantee correctness; it only tells you what lines were executed. You still need good assertions and tests for various scenarios. Focus on *meaningful* coverage over just line coverage. + +8. **Integrate with CI/CD:** + + * Automate your tests to run on every commit, push, or pull request using Continuous Integration (CI) services (e.g., GitHub Actions, GitLab CI, Jenkins). This catches regressions early. + +9. **Tests as Documentation:** + + * Well-written tests serve as the best, always-up-to-date examples of how to use your library's features. They demonstrate expected inputs, outputs, and behaviors for various scenarios. + +10. **Refactor Tests:** + + * Just like production code, tests need to be maintained and refactored. Keep them clean, readable, and efficient. Avoid excessive complexity in tests themselves. + +## V. What to Avoid + + * **Tests that depend on order:** Ensure each test is independent. + * **Testing private methods extensively:** Focus on the public API; if a private method is complex enough to warrant its own detailed tests, it might be a candidate for its own public function or class. + * **Over-mocking:** Only mock what's necessary. Too much mocking can make tests brittle (sensitive to internal refactors) and lose their ability to catch real integration issues. + * **Ignoring test failures:** A failing test means a bug or an outdated test. Address it immediately. + * **Slow unit tests:** Unit tests should run quickly. If they are slow, it often indicates an issue with external dependencies that should be mocked. + +By diligently applying these practices, you'll build a Python library that is not only functional but also robust, maintainable, and a pleasure for others to use. \ No newline at end of file diff --git a/docs/wiki/plan/ISSUES.md b/docs/wiki/plan/ISSUES.md new file mode 100644 index 0000000..b83fdc7 --- /dev/null +++ b/docs/wiki/plan/ISSUES.md @@ -0,0 +1,17 @@ +# How to Contribute + +We're thrilled that you're interested in contributing to the Local Data Platform! Your help is essential for keeping it great. + +This section provides guidelines for contributing to the project. Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open-source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. + +## 🏆 Open Issues to Contribute On + + +| Title | Theme | Status | Comments | Votes | +|-------|-------|--------|----------|-------| +| Example: Add BigQuery Ingestion | Ingestion | In Progress | 5 | 12 | +| Example: Improve Error Handling | Core | Under Review | 3 | 8 | +| Example: Add Parquet Export | Egression | Planned | 2 | 6 | + +--- \ No newline at end of file diff --git a/docs/wiki/usecases/PHOTO_MANAGEMENT.md b/docs/wiki/usecases/PHOTO_MANAGEMENT.md new file mode 100644 index 0000000..30b2f92 --- /dev/null +++ b/docs/wiki/usecases/PHOTO_MANAGEMENT.md @@ -0,0 +1,19 @@ +# Use Case: Local Photo Management & Easy Sharing + +## The Problem + +In the digital age, our photo collections grow exponentially, quickly consuming precious local storage. Managing thousands of photos, finding specific ones, and securely sharing select albums with friends and family without relying on privacy-invasive cloud services becomes a significant personal data challenge. + +* **Storage Bloat:** High-resolution photos take up immense disk space. +* **Organization Chaos:** Photos are scattered, un-tagged, and difficult to search. +* **Privacy Concerns:** Uploading personal photos to public or semi-public cloud albums often compromises privacy. +* **Sharing Friction:** Sending large batches of photos is cumbersome, often leading to using sub-optimal methods or public platforms. + +## How LDP Solves It + +The Local Data Platform (LDP) provides a robust, privacy-first, and highly customizable solution for managing your personal photo library. By leveraging LDP, you can: + +1. **Intelligent Local Compression:** Drastically reduce file sizes of your photos using Python's rich ecosystem of image processing libraries (e.g., Pillow, OpenCV, scikit-image) and various compression algorithms (e.g., JPEG optimization, WebP conversion) – all performed locally on your machine. +2. **Automated Organization & Tagging:** Process photo metadata (EXIF data like date, time, location) to automatically organize your collection. Integrate custom tagging systems to make photos easily searchable. +3. **Privacy-Preserving Sharing:** Generate temporary, secure, and shareable links that serve photos directly from your local machine, within your local network or via controlled internet access (e.g., through a temporary tunnel). This allows you to share with ease without ever permanently uploading your entire collection to a third-party service. + diff --git a/docs/wiki/workflow/PHOTO_MANAGEMENT.md b/docs/wiki/workflow/PHOTO_MANAGEMENT.md new file mode 100644 index 0000000..451e06a --- /dev/null +++ b/docs/wiki/workflow/PHOTO_MANAGEMENT.md @@ -0,0 +1,86 @@ +## Example + +Imagine a typical scenario where you have thousands of vacation photos. Here's how LDP helps: + +```python +import os +from datetime import datetime +from local_data_platform.storage_base import StorageBase +# Assuming you'd implement a concrete FileSystemStorage or similar +from local_data_platform.in_memory_storage import InMemoryStorage # Or a new FileSystemStorage +from local_data_platform.photo_processing import PhotoCompressor, PhotoOrganizer # Hypothetical modules/classes +from local_data_platform.local_server import LocalFileShareServer # Hypothetical module/class + +# 1. Define your local photo storage +# In a real scenario, this would likely be a FileSystemStorage +# For demonstration, let's use a dummy in-memory one or assume a setup +class FileSystemStorage(StorageBase): + def __init__(self, base_path: str): + self.base_path = base_path + os.makedirs(base_path, exist_ok=True) + self._data = {} # Simulating file paths/content for example + + def put(self, key: str, value: bytes): + # In real-world, save binary 'value' to 'os.path.join(self.base_path, key)' + self._data[key] = value + print(f"Stored: {key}") + + def get(self, key: str, default=None): + # In real-world, read binary from 'os.path.join(self.base_path, key)' + return self._data.get(key, default) + +# Setup a specific storage path for photos +photo_storage = FileSystemStorage(base_path="./my_photo_vault") + +# 2. Process your raw photos +photo_compressor = PhotoCompressor(quality=80, output_format="webp") +photo_organizer = PhotoOrganizer(storage_backend=photo_storage) + +raw_photo_paths = ["./vacation/img_001.jpg", "./vacation/img_002.png"] # Paths to your original photos + +# Simulate loading and processing photos +processed_photos_info = [] +for path in raw_photo_paths: + # In reality, read image data from 'path' + image_data_raw = b"..." # Dummy binary data + + # Apply compression + compressed_data = photo_compressor.compress(image_data_raw) + + # Generate a new key/path for storage (e.g., based on hash or metadata) + photo_id = f"compressed_vacation_{os.path.basename(path).split('.')[0]}.webp" + + # Store the compressed photo + photo_storage.put(photo_id, compressed_data) + + # Extract metadata and organize + metadata = {"date": datetime.now().isoformat(), "tags": ["vacation", "beach"]} + photo_organizer.organize(photo_id, metadata) + processed_photos_info.append({"id": photo_id, "path": os.path.join(photo_storage.base_path, photo_id)}) + +print("\nPhotos processed and stored locally.") + +# 3. Share a selection of photos easily and privately +photos_to_share_ids = [processed_photos_info[0]['id']] # Just sharing the first one for example + +# The LocalFileShareServer would temporarily serve these files +share_server = LocalFileShareServer( + storage_backend=photo_storage, + allowed_ids=photos_to_share_ids, + expiration_minutes=60 +) + +# This would start a simple web server in a background thread or process +# and provide a URL that others on the same network can access. +print("\nStarting local sharing server...") +share_url = share_server.start_sharing() +print(f"Share these photos via: {share_url}") +print("Server will automatically stop after 60 minutes or when you close the application.") + +# In a real application, you'd keep the script running for the server to serve, +# or integrate it into a long-running LDP daemon/UI. +# For this example, we'll just print the URL and simulate stopping. +share_server.stop_sharing() +print("Sharing server stopped.") + +``` \ No newline at end of file diff --git a/how_to_setup.md b/how_to_setup.md new file mode 100644 index 0000000..009a914 --- /dev/null +++ b/how_to_setup.md @@ -0,0 +1,70 @@ +# How to Set Up for Development + +This guide explains how to set up your local environment for contributing to the `local-data-platform` project. Following these steps will ensure you have a consistent development environment that matches our CI pipeline. + +## Prerequisites + +Before you begin, ensure you have the following installed on your system: + +- **Git**: For version control. +- **Python**: Version 3.12 or newer. You can check with `python --version`. +- **Poetry**: For dependency management. We recommend installing it with `pipx` to avoid dependency conflicts. + ```sh + # Install pipx if you don't have it + python -m pip install --user pipx + python -m pipx ensurepath + + # Install poetry using pipx + pipx install poetry + ``` + +## Step 1: Clone the Repository + +Clone the project from GitHub and navigate into the project directory: + +```sh +git clone https://github.com/tusharchou/local-data-platform.git +cd local-data-platform +``` + +## Step 2: Install Dependencies + +This project uses Poetry to manage dependencies. To install all packages required for development, testing, and building documentation, run: + +```sh +poetry install --with dev,docs +``` +This command will create a virtual environment within the project folder and install all the necessary libraries. You can activate it by running `poetry shell`. + +## Step 3: Verify Your Setup + +To ensure everything is configured correctly, run the linters, tests, and build the documentation. + +### Run Linters +We use `flake8` to enforce code style. Check for any linting issues with: +```sh +poetry run flake8 src/ tests/ +``` + +### Run Tests +Our tests are written with `pytest`. Execute the test suite by running: +```sh +poetry run pytest +``` + +### Build Documentation +The documentation is built with `MkDocs`. To preview the docs locally with live-reloading, run: +```sh +poetry run mkdocs serve +``` +You can then open `http://127.0.0.1:8000` in your web browser. To perform a strict build like our CI process, use `poetry run mkdocs build --strict`. + +You are now ready to contribute to `local-data-platform`! + +## Troubleshooting + +If you encounter issues, especially with dependencies, try a clean re-installation: + +```sh +make reinstall +``` \ No newline at end of file diff --git a/local-data-platform/local_data_platform/__init__.py b/local_data_platform/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/__init__.py rename to local_data_platform/__init__.py diff --git a/local-data-platform/local_data_platform/catalog/__init__.py b/local_data_platform/catalog/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/catalog/__init__.py rename to local_data_platform/catalog/__init__.py diff --git a/local-data-platform/local_data_platform/catalog/local/__init__.py b/local_data_platform/catalog/local/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/catalog/local/__init__.py rename to local_data_platform/catalog/local/__init__.py diff --git a/local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py b/local_data_platform/catalog/local/iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/catalog/local/iceberg/__init__.py rename to local_data_platform/catalog/local/iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/engine/__init__.py b/local_data_platform/engine/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/engine/__init__.py rename to local_data_platform/engine/__init__.py diff --git a/local-data-platform/local_data_platform/etl.py b/local_data_platform/etl.py similarity index 100% rename from local-data-platform/local_data_platform/etl.py rename to local_data_platform/etl.py diff --git a/local-data-platform/local_data_platform/exceptions.py b/local_data_platform/exceptions.py similarity index 100% rename from local-data-platform/local_data_platform/exceptions.py rename to local_data_platform/exceptions.py diff --git a/local-data-platform/local_data_platform/format/__init__.py b/local_data_platform/format/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/format/__init__.py rename to local_data_platform/format/__init__.py diff --git a/local-data-platform/local_data_platform/format/csv/__init__.py b/local_data_platform/format/csv/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/format/csv/__init__.py rename to local_data_platform/format/csv/__init__.py diff --git a/local-data-platform/local_data_platform/format/iceberg/__init__.py b/local_data_platform/format/iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/format/iceberg/__init__.py rename to local_data_platform/format/iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/format/parquet/__init__.py b/local_data_platform/format/parquet/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/format/parquet/__init__.py rename to local_data_platform/format/parquet/__init__.py diff --git a/local-data-platform/local_data_platform/hello_world.py b/local_data_platform/hello_world.py similarity index 100% rename from local-data-platform/local_data_platform/hello_world.py rename to local_data_platform/hello_world.py diff --git a/local-data-platform/local_data_platform/issue/__init__.py b/local_data_platform/issue/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/issue/__init__.py rename to local_data_platform/issue/__init__.py diff --git a/local-data-platform/local_data_platform/logger.py b/local_data_platform/logger.py similarity index 100% rename from local-data-platform/local_data_platform/logger.py rename to local_data_platform/logger.py diff --git a/local-data-platform/local_data_platform/pipeline/__init__.py b/local_data_platform/pipeline/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/__init__.py rename to local_data_platform/pipeline/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/egression/__init__.py b/local_data_platform/pipeline/egression/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/egression/__init__.py rename to local_data_platform/pipeline/egression/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/egression/csv_to_iceberg/__init__.py b/local_data_platform/pipeline/egression/csv_to_iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/egression/csv_to_iceberg/__init__.py rename to local_data_platform/pipeline/egression/csv_to_iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py b/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py rename to local_data_platform/pipeline/egression/iceberg_to_csv/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/__init__.py b/local_data_platform/pipeline/ingestion/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/ingestion/__init__.py rename to local_data_platform/pipeline/ingestion/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/bigquery_to_csv/__init__.py b/local_data_platform/pipeline/ingestion/bigquery_to_csv/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/ingestion/bigquery_to_csv/__init__.py rename to local_data_platform/pipeline/ingestion/bigquery_to_csv/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py b/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py rename to local_data_platform/pipeline/ingestion/csv_to_iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py b/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py rename to local_data_platform/pipeline/ingestion/parquet_to_iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/pipeline/ingestion/pyarrow/__init__.py b/local_data_platform/pipeline/ingestion/pyarrow/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/pipeline/ingestion/pyarrow/__init__.py rename to local_data_platform/pipeline/ingestion/pyarrow/__init__.py diff --git a/local-data-platform/local_data_platform/store/__init__.py b/local_data_platform/store/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/__init__.py rename to local_data_platform/store/__init__.py diff --git a/local-data-platform/local_data_platform/store/source/__init__.py b/local_data_platform/store/source/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/source/__init__.py rename to local_data_platform/store/source/__init__.py diff --git a/local-data-platform/local_data_platform/store/source/gcp/__init__.py b/local_data_platform/store/source/gcp/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/source/gcp/__init__.py rename to local_data_platform/store/source/gcp/__init__.py diff --git a/local-data-platform/local_data_platform/store/source/gcp/bigquery/__init__.py b/local_data_platform/store/source/gcp/bigquery/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/source/gcp/bigquery/__init__.py rename to local_data_platform/store/source/gcp/bigquery/__init__.py diff --git a/local-data-platform/local_data_platform/store/source/json/__init__.py b/local_data_platform/store/source/json/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/source/json/__init__.py rename to local_data_platform/store/source/json/__init__.py diff --git a/local_data_platform/store/source/near/__init__.py b/local_data_platform/store/source/near/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/local-data-platform/local_data_platform/store/source/parquet/__init__.py b/local_data_platform/store/source/parquet/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/source/parquet/__init__.py rename to local_data_platform/store/source/parquet/__init__.py diff --git a/local-data-platform/local_data_platform/store/target/__init__.py b/local_data_platform/store/target/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/target/__init__.py rename to local_data_platform/store/target/__init__.py diff --git a/local-data-platform/local_data_platform/store/target/iceberg/__init__.py b/local_data_platform/store/target/iceberg/__init__.py similarity index 100% rename from local-data-platform/local_data_platform/store/target/iceberg/__init__.py rename to local_data_platform/store/target/iceberg/__init__.py diff --git a/local-data-platform/local_data_platform/tmp/warehouse/pyiceberg_catalog.db b/local_data_platform/tmp/warehouse/pyiceberg_catalog.db similarity index 100% rename from local-data-platform/local_data_platform/tmp/warehouse/pyiceberg_catalog.db rename to local_data_platform/tmp/warehouse/pyiceberg_catalog.db diff --git a/mkdocs.yml b/mkdocs.yml index ee7bf64..03326e5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,12 +7,30 @@ repo_name: local-data-platform nav: - Home: index.md - - User Guide: - - Recipes: recipes.md - - API Reference: api.md - - Contributing: - - User Issues: user_issues.md - - Feature Requests: developer_feature_requests.md + - "About LDP": wiki/VISION.md + - Business Solution: + - Business Problem: wiki/business/PROPOSAL.md + - Agentic Marketing Analyser: wiki/business/MARKETING_DATA_ANALYSIS.md + - "Photo Management": wiki/usecases/PHOTO_MANAGEMENT.md + - "Conceptual Workflow": + - "Photo Management": wiki/workflow/PHOTO_MANAGEMENT.md + - Features: + - Library: + - "Base": wiki/library/BASE.md + - "Exceptions": wiki/library/EXCEPTION.md + - "Test": wiki/library/TEST.md + - Development: + - "Github": wiki/GITHUB.md + + - Contribute Now: + - "How To Contribute": wiki/plan/ISSUES.md + - Project Wiki: + - Overview: wiki/PROJECT_OVERVIEW.md + - Problem Statement: wiki/PROBLEM_STATEMENT.md + - Technical Specs: wiki/TECHNICAL_SPECIFICATIONS.md + - Project Structure: wiki/PROJECT_STRUCTURE.md + - Development Process: wiki/DEVELOPMENT.md + theme: name: material @@ -56,6 +74,7 @@ markdown_extensions: plugins: - search - autorefs + - tags - mkdocstrings: handlers: python: diff --git a/poetry.lock b/poetry.lock index c2cb796..0abeb9d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -25,6 +25,28 @@ files = [ [package.extras] dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, +] + +[package.dependencies] +soupsieve = ">1.2" +typing-extensions = ">=4.0.0" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "cachetools" version = "5.5.2" @@ -1301,6 +1323,17 @@ files = [ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] +[[package]] +name = "soupsieve" +version = "2.7" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, +] + [[package]] name = "strictyaml" version = "1.7.3" @@ -1417,4 +1450,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "2d19db6500226f2c392eccfde08c2876784901c005764bca5867d017ee0a6f43" +content-hash = "24a03a384403a85bc073b0f2bd05da36a6e64b7b08ab3daf26bd655cf01eb6f5" diff --git a/pyproject.toml b/pyproject.toml index d4a1af8..34f3301 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,3 @@ -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - [tool.poetry] name = "local-data-platform" version = "0.1.1" @@ -14,16 +10,22 @@ packages = [{include = "local_data_platform", from = "src"}] [tool.poetry.dependencies] python = "^3.12" pyiceberg = ">=0.5.0" +requests = "*" +beautifulsoup4 = "*" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.0.0" +flake8 = ">=5.0.0" -[tool.poetry.group.docs] -optional = true [tool.poetry.group.docs.dependencies] mkdocs = "^1.6.0" mkdocs-material = "9.5.21" mkdocstrings = {extras = ["python"], version = "^0.25.0"} pymdown-extensions = "^10.8.1" -[tool.poetry.group.dev] -optional = true -[tool.poetry.group.dev.dependencies] -pytest = ">=7.0.0" -flake8 = ">=5.0.0" + +[tool.poetry.scripts] +fetch-rtd-urls = "scripts.fetch_rtd_urls:main" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/append_pr_history.py b/scripts/append_pr_history.py new file mode 100644 index 0000000..904f1cd --- /dev/null +++ b/scripts/append_pr_history.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +""" +Script to append PR history and directory changes to docs/PR_HISTORY.md after a merge. +Intended for use in CI/CD or as a manual post-merge tool. +""" +import os +import subprocess +from datetime import datetime + +PR_HISTORY_PATH = os.path.join('docs', 'PR_HISTORY.md') + +def get_env_var(name, fallback=None): + return os.environ.get(name, fallback) + +def get_last_merge_commit(): + result = subprocess.run(['git', 'log', '--merges', '-1', '--pretty=%H'], capture_output=True, text=True, check=True) + return result.stdout.strip() + +def get_changed_dirs(base, head): + result = subprocess.run(['git', 'diff', '--dirstat=files,0', base, head], capture_output=True, text=True, check=True) + dirs = {f"/{line.split()[-1].rstrip('/')}/" for line in result.stdout.splitlines() if line.strip()} + return sorted(list(dirs)) + +def append_pr_history(pr_number, pr_title, merger, date_merged, description, changed_dirs): + with open(PR_HISTORY_PATH, 'a', encoding='utf-8') as f: + f.write(f"\n### PR #{pr_number}: {pr_title}\n\n") + f.write(f"**Merged By:** {merger} on {date_merged}\n\n") + f.write(f"**Description:**\n\n```\n{description}\n```\n\n") + f.write(f"**Directory Changes:**\n\n") + for d in changed_dirs: + f.write(f"- `{d}`\n") + f.write("\n---\n") + +if __name__ == '__main__': + merge_commit = get_last_merge_commit() + # The second parent of a merge commit is the head of the merged branch + parent_commit = f'{merge_commit}^2' + changed_dirs = get_changed_dirs(parent_commit, merge_commit) + + append_pr_history( + pr_number=get_env_var('PR_NUMBER', 'N/A'), + pr_title=get_env_var('PR_TITLE', 'No title provided'), + merger=get_env_var('PR_MERGER', 'N/A'), + date_merged=datetime.now().strftime('%Y-%m-%d'), + description=get_env_var('PR_DESCRIPTION', 'No description provided'), + changed_dirs=changed_dirs + ) + print(f"PR history successfully updated in {PR_HISTORY_PATH}") \ No newline at end of file diff --git a/scripts/fetch_closed_items.py b/scripts/fetch_closed_items.py new file mode 100644 index 0000000..00efaf2 --- /dev/null +++ b/scripts/fetch_closed_items.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Script to fetch and display closed Pull Requests and Issues from a GitHub repository +and write them to a Markdown file. +""" +import os +from typing import List +from local_data_platform.github import get_items, Item + +# --- Configuration --- +REPO_OWNER = "tusharchou" +REPO_NAME = "local-data-platform" +OUTPUT_PATH = os.path.join('docs', 'closed_items.md') +# --------------------- + +def format_items_as_markdown(items: List[Item], item_type: str) -> str: + """Formats a list of GitHub items into a Markdown list.""" + if not items: + return f"No closed {item_type} found or failed to fetch." + + markdown_list = [] + for item in items: + author = item.author + if item.closed_at: + closed_date = item.closed_at.strftime('%Y-%m-%d') + date_info = f" on {closed_date} " + else: + date_info = "" + markdown_list.append(f"- **[{item_type} #{item.number}]({item.url})**: {item.title} (closed{date_info}by @{author})") + return "\n".join(markdown_list) + +def main(): + """Main function to fetch closed items and write them to a Markdown file.""" + print("Fetching all closed items...") + + all_closed_items = get_items(REPO_OWNER, REPO_NAME, state="closed") + closed_prs = [item for item in all_closed_items if item.is_pr] + closed_issues = [item for item in all_closed_items if not item.is_pr] + + content = "# All Closed Items\n\n" + content += "This page lists all closed Pull Requests and Issues, sorted by most recently updated.\n\n" + content += "## Closed Pull Requests\n\n" + content += format_items_as_markdown(closed_prs, "PR") + content += "\n\n## Closed Issues\n\n" + content += format_items_as_markdown(closed_issues, "Issue") + + os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True) + with open(OUTPUT_PATH, 'w', encoding='utf-8') as f: + f.write(content) + + print(f"Successfully generated closed items report at {OUTPUT_PATH}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/fetch_rtd_urls.py b/scripts/fetch_rtd_urls.py new file mode 100644 index 0000000..de575df --- /dev/null +++ b/scripts/fetch_rtd_urls.py @@ -0,0 +1,58 @@ +import requests +from bs4 import BeautifulSoup +from urllib.parse import urljoin, urlparse + + +def fetch_all_rtd_urls(base_url): + visited = set() + to_visit = [base_url] + result = [] + + while to_visit: + url = to_visit.pop() + if url in visited: + continue + visited.add(url) + try: + resp = requests.get(url) + resp.raise_for_status() + except Exception as e: + print(f"Failed to fetch {url}: {e}") + continue + + soup = BeautifulSoup(resp.text, "html.parser") + result.append(url) + for link in soup.find_all("a", href=True): + href = link["href"] + # Only follow internal links + if href.startswith("http"): + if not href.startswith(base_url): + continue + elif href.startswith("/"): + href = urljoin(base_url, href) + else: + href = urljoin(url, href) + # Only crawl pages within the docs site + if urlparse(href).netloc == urlparse(base_url).netloc and href not in visited: + to_visit.append(href) + + return sorted(result) + + +def write_urls_to_wiki(urls, output_path): + with open(output_path, "w") as f: + f.write("# Active Hosted Docs URLs\n\n") + for url in urls: + f.write(f"- [{url}]({url})\n") + + +def main(): + # Updated to your actual Read the Docs URL and branch + BASE_URL = "https://local-data-platform.readthedocs.io/en/docs-sidebar-recipes-from-fix-readthedocs/" + urls = fetch_all_rtd_urls(BASE_URL) + write_urls_to_wiki(urls, "docs/wiki/ACTIVE_DOCS_URLS.md") + print(f"Found {len(urls)} URLs. Output written to docs/wiki/ACTIVE_DOCS_URLS.md") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_issue_list.py b/scripts/generate_issue_list.py new file mode 100644 index 0000000..86f1a60 --- /dev/null +++ b/scripts/generate_issue_list.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +""" +Script to generate the 'user_issues.md' page with a filterable list of open GitHub issues. +""" +import os +import re +from typing import List +from local_data_platform.github import get_items, Item + +REPO_OWNER = "tusharchou" +REPO_NAME = "local-data-platform" +OUTPUT_PATH = os.path.join('docs', 'user_issues.md') + +def parse_labels(labels: List[str]): + """Parses labels to find status and theme.""" + status = "Planned" # Default status + theme = "General" # Default theme + for label_name in labels: + label_name = label_name.lower() + if label_name.startswith('status:'): + status = label_name.replace('status:', '').replace('-', ' ').title() + elif label_name.startswith('theme:'): + theme = label_name.replace('theme:', '').replace('-', ' ').title() + return status, theme + +def generate_page_content(items: List[Item]): + """Generates the full Markdown content for the user_issues.md page.""" + header = """# Open Issues + +This page lists all open issues in the repository. Use the filters below to sort by status or theme. +""" + + issue_cards = [] + for item in items: + if item.is_pr: + continue # Skip pull requests + + status, theme = parse_labels(item.labels) + # Truncate body for preview + body_preview = (item.description or 'No description provided.') + body_preview = body_preview.replace('\n', ' ').replace('\r', ' ').strip() + body_preview = re.sub(r'\s+', ' ', body_preview) + body_preview = (body_preview[:150] + '...') if len(body_preview) > 150 else body_preview + + card = f""" +--- +tags: + - Status - {status} + - Theme - {theme} +--- +### #{item.number} - {item.title} +*Status: {status} | Theme: {theme}* + +> {body_preview} +""" + issue_cards.append(card) + + if not issue_cards: + return header + "\nNo open issues found." + + return header + "\n" + "\n---\n".join(issue_cards) + + +if __name__ == "__main__": + print("Generating 'Open Issues' page...") + open_items = get_items(REPO_OWNER, REPO_NAME, state="open") + page_content = generate_page_content(open_items) + os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True) + with open(OUTPUT_PATH, 'w', encoding='utf-8') as f: + f.write(page_content) + print(f"Successfully generated page at {OUTPUT_PATH}") \ No newline at end of file diff --git a/scripts/github_api.py b/scripts/github_api.py new file mode 100644 index 0000000..e69de29 diff --git a/src/local_data_platform/__init__.py b/src/local_data_platform/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/local_data_platform/github/__init__.py b/src/local_data_platform/github/__init__.py new file mode 100644 index 0000000..a372760 --- /dev/null +++ b/src/local_data_platform/github/__init__.py @@ -0,0 +1,101 @@ +import os +import requests +from dataclasses import dataclass +from datetime import datetime +from typing import Optional, List +from local_data_platform.exceptions import GitHubAPIError + +@dataclass +class Item: + """Represents a GitHub Issue or Pull Request.""" + number: int + title: str + author: str + description: Optional[str] + created_at: datetime + closed_at: Optional[datetime] + url: str + is_pr: bool + labels: List[str] + +def _fetch_paginated_data(api_url: str, params: dict, headers: dict) -> List[dict]: + """Handles pagination for GitHub API requests.""" + all_items = [] + page_num = 1 + while api_url: + print(f"Fetching page {page_num} from {api_url}...") + try: + # For subsequent pages, params are already in the URL, so we pass None + current_params = params if page_num == 1 else None + response = requests.get(api_url, headers=headers, params=current_params) + response.raise_for_status() + + fetched_items = response.json() + if not fetched_items: + break + + all_items.extend(fetched_items) + + if 'next' in response.links: + api_url = response.links['next']['url'] + page_num += 1 + else: + api_url = None + except requests.exceptions.RequestException as e: + raise GitHubAPIError(f"Error fetching data from GitHub: {e}") from e + return all_items + +def get_items(repo_owner: str, repo_name: str, state: str = "all") -> List[Item]: + """ + Fetches Issues and Pull Requests from a GitHub repository. + + Args: + repo_owner: The owner of the repository. + repo_name: The name of the repository. + state: The state of the items to fetch ('open', 'closed', 'all'). + + Returns: + A list of Item objects. + """ + token = os.environ.get("GITHUB_TOKEN") + headers = {"Accept": "application/vnd.github.v3+json"} + if token: + headers["Authorization"] = f"Bearer {token}" + else: + print(f"Warning: GITHUB_TOKEN not set. Making unauthenticated requests to fetch items in '{state}' state.") + + api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/issues" + params = {"state": state, "per_page": 100, "sort": "updated", "direction": "desc"} + + try: + raw_items = _fetch_paginated_data(api_url, params, headers) + except GitHubAPIError as e: + print(e) + return [] + + items = [] + for raw_item in raw_items: + # Safely parse datetime strings + created_at = datetime.fromisoformat( + raw_item['created_at'].replace('Z', '+00:00') + ) + closed_at = None + if raw_item.get('closed_at'): + closed_at = datetime.fromisoformat( + raw_item['closed_at'].replace('Z', '+00:00') + ) + + item = Item( + number=raw_item['number'], + title=raw_item['title'], + author=raw_item['user']['login'], + description=raw_item.get('body'), + created_at=created_at, + closed_at=closed_at, + url=raw_item['html_url'], + is_pr='pull_request' in raw_item, + labels=[label['name'] for label in raw_item.get('labels', [])] + ) + items.append(item) + + return items \ No newline at end of file diff --git a/src/tmp/warehouse/pyiceberg_catalog.db b/src/tmp/warehouse/pyiceberg_catalog.db new file mode 100644 index 0000000000000000000000000000000000000000..388fec89b40f136d4e0346716e24fc9b27e2fbda GIT binary patch literal 20480 zcmeI&&u^1p7zc3Zeso*5)r(n{c;K>RP5QP_paVT8Sd2uO`ikl(tuv8#JbV(7%fGwp!nPR;_QBbv-Zb9~?>hZ}#>I(#rBMhv^|dlIvI} ztaFQ~bLzL*Fiw$WdGd;{>d#+R>&McI+Of1fo@7B9CR32cFPdM;znfp&IH`$(*L2ur zbmsGcqD9+r&}&<>C@f{x-i2gW%x>iK;#GB`G`+^K82wfzmA}95_TatvQR#p-+H>y~xD~QBEn;VsW|U3ui7iN@4ql!|nZ=ER{O`)s zJ0uzvq~D(7->L5SURdL6W-SudulXAm2tWV=5P$##AOHafKmY;|xJ!ZLFteoVJIQ9U z+3F0&$%8SuW2f~sI}NMDd_TCbVtVOWq35v3^?dfQR4SI@(^lz%hU^0$ak5g{@kPfj zrJkcS7@(H;l!06(6{n%gMwt=j>ITtFw?a%^)re^@jqNmaW*cg8a7^)I6g2NX3x%kT zt~Okk5=YnVZWd)iDJ>IIp^9QqO_5z~GK-`la?`COn`G1OKdr9^*&WA6EdIlr7(aAU zP$8yjP}?>sQ4C!s8dDWwYt$v0!fe;&w<1$InfskxTqac33|nIg(aHvgD;t#1ib9EQ zs1;MOnW-s1m2ZVV*_m7-