From 3534a54cb9ec6706ca0391f5b6c6577622a67815 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Mon, 11 Nov 2024 15:02:51 -0800 Subject: [PATCH 01/25] update config files for forge-py add pytest to test configs make sure valid json --- .github/workflows/deploy.yml | 23 +++++++++++++++++ .github/workflows/test_configs.yml | 25 +++++++++++++++++++ config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg | 17 +++++++------ config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg | 17 +++++++------ config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg | 17 +++++++------ config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg | 17 +++++++------ config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg | 12 ++++++--- config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg | 12 ++++++--- config-files/COWVR_STPH8_L2_EDR_V10.0.cfg | 12 ++++++--- config-files/COWVR_STPH8_L2_EDR_V9.0.cfg | 12 ++++++--- config-files/PRIM_SMAP_L2_V1.cfg | 10 ++++++-- .../SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg | 10 ++++++-- config-files/SMAP_RSS_L2_SSS_V5.cfg | 11 +++++--- config-files/SMAP_RSS_L2_SSS_V6.cfg | 10 ++++++-- test_configs.py | 17 +++++++++++++ 15 files changed, 173 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/test_configs.yml create mode 100644 test_configs.py diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d32a365..cc1d921 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,11 +10,32 @@ on: workflow_dispatch: jobs: + + test: + runs-on: ubuntu-latest + steps: + - name: Check out the code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + + - name: Run pytest + run: pytest + # First job in the workflow installs and verifies the software deploy_sit: name: Deploy SIT # The type of runner that the job will run on runs-on: ubuntu-latest + needs: test steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it @@ -38,6 +59,7 @@ jobs: name: Deploy UAT # The type of runner that the job will run on runs-on: ubuntu-latest + needs: test steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it @@ -61,6 +83,7 @@ jobs: name: Deploy OPS # The type of runner that the job will run on runs-on: ubuntu-latest + needs: test steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml new file mode 100644 index 0000000..4293b47 --- /dev/null +++ b/.github/workflows/test_configs.yml @@ -0,0 +1,25 @@ +name: Test Configs + +on: + push: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Check out the code + uses: actions/checkout@v3 + + - 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 pytest + + - name: Run pytest + run: pytest diff --git a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg index 91cfd9f..5a125d2 100644 --- a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg @@ -10,13 +10,16 @@ ] }, "footprint": { - "alpha": 0.06, - "thinning_fac": 220, - "cutoff_lat": 80, - "smooth_poles": [ - 78, - 80 - ] + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.06, + "thinning": { + "method": "standard", + "value": 220 + }, + "cutoff_lat": 80, + "smooth_poles": [78,80] + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg index 20b1d7b..b124752 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg @@ -10,13 +10,16 @@ ] }, "footprint": { - "alpha": 0.035, - "thinning_fac": 300, - "cutoff_lat": 80, - "smooth_poles": [ - 78, - 80 - ] + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.035, + "thinning": { + "method": "standard", + "value": 300 + }, + "cutoff_lat": 80, + "smooth_poles": [78,80] + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg index b43e68a..f2c1543 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg @@ -11,13 +11,16 @@ ] }, "footprint": { - "alpha": 0.04, - "thinning_fac": 200, - "cutoff_lat": 80, - "smooth_poles": [ - 78, - 80 - ] + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.04, + "thinning": { + "method": "standard", + "value": 200 + }, + "cutoff_lat": 80, + "smooth_poles": [78,80] + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg index f250bc4..14b8efb 100644 --- a/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg @@ -10,13 +10,16 @@ ] }, "footprint": { - "alpha": 0.03, - "thinning_fac": 240, - "cutoff_lat": 80, - "smooth_poles": [ - 78, - 80 - ] + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.03, + "thinning": { + "method": "standard", + "value": 240 + }, + "cutoff_lat": 80, + "smooth_poles": [78,80] + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg index 8bc8d31..be8f957 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg @@ -4,9 +4,15 @@ "lonVar": "obs_lon", "is360": false, "footprint": { - "thinning_fac": 200, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "strategy": "alpha_shape", + "alpha_shape":{ + "thinning": { + "value": "200", + "method" : "standard" + }, + "alpha": 0.03, + "group": "GeolocationAndFlags" + } }, "footprinter": "forge-py" } \ No newline at end of file diff --git a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg index 449376b..70856fb 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg @@ -4,9 +4,15 @@ "lonVar": "obs_lon", "is360": false, "footprint": { - "thinning_fac": 200, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "strategy": "alpha_shape", + "alpha_shape":{ + "thinning": { + "value": "200", + "method" : "standard" + }, + "alpha": 0.03, + "group": "GeolocationAndFlags" + } }, "footprinter": "forge-py" } \ No newline at end of file diff --git a/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg b/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg index 739a03a..4efdfaa 100644 --- a/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg +++ b/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg @@ -4,9 +4,15 @@ "lonVar": "obs_lon", "is360": false, "footprint": { - "thinning_fac": 200, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "strategy": "alpha_shape", + "alpha_shape":{ + "thinning": { + "value": "200", + "method" : "standard" + }, + "alpha": 0.03, + "group": "GeolocationAndFlags" + } }, "footprinter": "forge-py" } \ No newline at end of file diff --git a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg index c545f3b..f3078b3 100644 --- a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg @@ -4,9 +4,15 @@ "lonVar": "obs_lon", "is360": false, "footprint": { - "thinning_fac": 200, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "strategy": "alpha_shape", + "alpha_shape":{ + "thinning": { + "value": [1, 1], + "method" : "bin_avg" + }, + "alpha": 0.03, + "group": "GeolocationAndFlags" + } }, "footprinter": "forge-py" } \ No newline at end of file diff --git a/config-files/PRIM_SMAP_L2_V1.cfg b/config-files/PRIM_SMAP_L2_V1.cfg index 2b7a9ba..0d6c8b5 100644 --- a/config-files/PRIM_SMAP_L2_V1.cfg +++ b/config-files/PRIM_SMAP_L2_V1.cfg @@ -11,8 +11,14 @@ ] }, "footprint": { - "alpha": 0.35, - "thinning_fac": 4 + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.35, + "thinning": { + "method": "standard", + "value": 4 + } + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg index 134fe9f..86f2cc6 100644 --- a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg +++ b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg @@ -11,8 +11,14 @@ ] }, "footprint": { - "thinning_fac": 30, - "alpha": 0.035 + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.035, + "thinning": { + "method": "standard", + "value": 30 + } + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/SMAP_RSS_L2_SSS_V5.cfg b/config-files/SMAP_RSS_L2_SSS_V5.cfg index 64433c1..5f359cd 100644 --- a/config-files/SMAP_RSS_L2_SSS_V5.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V5.cfg @@ -3,10 +3,15 @@ "lonVar": "cellon", "timeVar": "time", "is360": true, - "tolerance": 0.4, "footprint": { - "alpha": 0.03, - "thinning_fac": 30 + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.03, + "thinning": { + "method": "bin_avg", + "value": [1.5, 1.5] + } + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/config-files/SMAP_RSS_L2_SSS_V6.cfg b/config-files/SMAP_RSS_L2_SSS_V6.cfg index 667074e..5998c1a 100644 --- a/config-files/SMAP_RSS_L2_SSS_V6.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V6.cfg @@ -11,8 +11,14 @@ ] }, "footprint": { - "alpha": 0.03, - "thinning_fac": 30 + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.03, + "thinning": { + "method": "standard", + "value": 30 + } + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/test_configs.py b/test_configs.py new file mode 100644 index 0000000..a5452da --- /dev/null +++ b/test_configs.py @@ -0,0 +1,17 @@ +import os +import json +import pytest + +# Directory containing the configuration files +CONFIG_DIR = 'config-files' + +@pytest.mark.parametrize("config_file", [ + os.path.join(CONFIG_DIR, f) for f in os.listdir(CONFIG_DIR) if f.endswith('.cfg') +]) +def test_json_loadable(config_file): + """Test if a configuration file can be loaded as JSON.""" + with open(config_file, 'r') as f: + try: + json.load(f) + except json.JSONDecodeError as e: + pytest.fail(f"Failed to load JSON from {config_file}: {e}") From da239d53ed05d8b5ba1005a360c31af8e1f699f4 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 20 Nov 2024 14:32:15 -0800 Subject: [PATCH 02/25] update config files --- config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg | 8 ++++---- config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg | 8 ++++---- config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg | 8 ++++---- config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg | 16 ++++++++++------ config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg | 8 ++++---- config-files/COWVR_STPH8_L2_EDR_V9.0.cfg | 8 ++++---- config-files/PRIM_SMAP_L2_V1.cfg | 8 ++++---- .../SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg | 6 ++++-- config-files/SMAP_RSS_L2_SSS_V5.cfg | 4 ++-- config-files/SMAP_RSS_L2_SSS_V6.cfg | 6 +++--- 10 files changed, 43 insertions(+), 37 deletions(-) diff --git a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg index 5a125d2..1d6be94 100644 --- a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg @@ -12,10 +12,10 @@ "footprint": { "strategy": "alpha_shape", "alpha_shape": { - "alpha": 0.06, + "alpha": 0.2, "thinning": { - "method": "standard", - "value": 220 + "method": "big_avg", + "value": [1.5,1.5] }, "cutoff_lat": 80, "smooth_poles": [78,80] @@ -76,4 +76,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg index b124752..84c3dd9 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg @@ -12,10 +12,10 @@ "footprint": { "strategy": "alpha_shape", "alpha_shape": { - "alpha": 0.035, + "alpha": 0.1, "thinning": { - "method": "standard", - "value": 300 + "method": "bin_avg", + "value": [1.5,1.5] }, "cutoff_lat": 80, "smooth_poles": [78,80] @@ -68,4 +68,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg index f2c1543..4c1bcb0 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg @@ -13,10 +13,10 @@ "footprint": { "strategy": "alpha_shape", "alpha_shape": { - "alpha": 0.04, + "alpha": 0.065, "thinning": { - "method": "standard", - "value": 200 + "method": "big_avg", + "value": [1.5, 1.5] }, "cutoff_lat": 80, "smooth_poles": [78,80] @@ -77,4 +77,4 @@ "ppd": 16, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg b/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg index c312e63..787d1d8 100644 --- a/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg +++ b/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg @@ -11,12 +11,16 @@ ] }, "footprint": { - "findValid": true, - "strategy": "polarsides", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" + "strategy": "alpha_shape", + "alpha_shape": { + "alpha": 0.04, + "thinning": { + "method": "standard", + "value": 220 + }, + "cutoff_lat": 80, + "smooth_poles": [78,80] + } }, "imgVariables": [ { diff --git a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg index 70856fb..8d1b427 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg @@ -1,7 +1,7 @@ { "shortName": "COWVR_STPH8_L1_TSDR_V9.0", - "latVar": "obs_lat", - "lonVar": "obs_lon", + "latVar": "GeolocationAndFlags/obs_lat", + "lonVar": "GeolocationAndFlags/obs_lon", "is360": false, "footprint": { "strategy": "alpha_shape", @@ -11,8 +11,8 @@ "method" : "standard" }, "alpha": 0.03, - "group": "GeolocationAndFlags" + "fill_value": -99990.0 } }, "footprinter": "forge-py" -} \ No newline at end of file +} diff --git a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg index f3078b3..ccf71a9 100644 --- a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg @@ -1,7 +1,7 @@ { "shortName": "COWVR_STPH8_L2_EDR_V9.0", - "latVar": "obs_lat", - "lonVar": "obs_lon", + "latVar": "GeolocationAndFlags/obs_lat", + "lonVar": "GeolocationAndFlags/obs_lon", "is360": false, "footprint": { "strategy": "alpha_shape", @@ -11,8 +11,8 @@ "method" : "bin_avg" }, "alpha": 0.03, - "group": "GeolocationAndFlags" + "fill_value": -99999.0 } }, "footprinter": "forge-py" -} \ No newline at end of file +} diff --git a/config-files/PRIM_SMAP_L2_V1.cfg b/config-files/PRIM_SMAP_L2_V1.cfg index 0d6c8b5..5b87601 100644 --- a/config-files/PRIM_SMAP_L2_V1.cfg +++ b/config-files/PRIM_SMAP_L2_V1.cfg @@ -13,10 +13,10 @@ "footprint": { "strategy": "alpha_shape", "alpha_shape": { - "alpha": 0.35, + "alpha": 0.035, "thinning": { - "method": "standard", - "value": 4 + "method": "bin_avg", + "value": [1.5,1.5] } } }, @@ -63,4 +63,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg index 86f2cc6..2fe35af 100644 --- a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg +++ b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg @@ -17,7 +17,9 @@ "thinning": { "method": "standard", "value": 30 - } + }, + "cutoff_lat": 88, + "smooth_poles": [82,90] } }, "footprinter": "forge-py", @@ -43,4 +45,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/SMAP_RSS_L2_SSS_V5.cfg b/config-files/SMAP_RSS_L2_SSS_V5.cfg index 5f359cd..41be463 100644 --- a/config-files/SMAP_RSS_L2_SSS_V5.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V5.cfg @@ -9,7 +9,7 @@ "alpha": 0.03, "thinning": { "method": "bin_avg", - "value": [1.5, 1.5] + "value": [0.75, 0.75] } } }, @@ -47,4 +47,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/SMAP_RSS_L2_SSS_V6.cfg b/config-files/SMAP_RSS_L2_SSS_V6.cfg index 5998c1a..85bcbe2 100644 --- a/config-files/SMAP_RSS_L2_SSS_V6.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V6.cfg @@ -15,8 +15,8 @@ "alpha_shape": { "alpha": 0.03, "thinning": { - "method": "standard", - "value": 30 + "method": "bin_avg", + "value": [1,1] } } }, @@ -45,4 +45,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} From f50a6aee3a5ec65f2c80b5b4d37d6b3d46fb65b9 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 20 Nov 2024 18:52:56 -0800 Subject: [PATCH 03/25] add schema --- config-files/ALTIKA_SARAL_L2_OST_XOGDR.cfg | 1 + config-files/AMSR2-REMSS-L2P-v7.2.cfg | 1 + config-files/AMSR2-REMSS-L2P-v8.2.cfg | 1 + config-files/AMSR2-REMSS-L2P-v8a.cfg | 1 + config-files/AMSR2-REMSS-L2P_RT-v8.2.cfg | 1 + config-files/AMSR2-REMSS-L2P_RT_v8a.cfg | 1 + config-files/AMSRE-REMSS-L2P-v7a.cfg | 1 + config-files/AQUARIUS_L2_SSS_CAP_V5.cfg | 1 + config-files/AQUARIUS_L2_SSS_V5.cfg | 5 +- config-files/ASCATA-L2-25km.cfg | 1 + config-files/ASCATA-L2-Coastal.cfg | 1 + config-files/ASCATB-L2-25km.cfg | 1 + config-files/ASCATB-L2-Coastal.cfg | 1 + config-files/ASCATC-L2-25km.cfg | 1 + config-files/ASCATC-L2-Coastal.cfg | 1 + config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg | 13 +- config-files/AVHRRF_MA-STAR-L2P-v2.80.cfg | 1 + config-files/AVHRRF_MB-STAR-L2P-v2.80.cfg | 1 + config-files/AVHRRF_MC-STAR-L2P-v2.80.cfg | 1 + config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg | 13 +- config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg | 12 +- config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg | 6 +- config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg | 5 +- .../AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg | 1 + config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg | 15 +- config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg | 6 +- config-files/COWVR_STPH8_L2_EDR_V10.0.cfg | 15 +- config-files/COWVR_STPH8_L2_EDR_V9.0.cfg | 11 +- config-files/JASON-1_L2_OST_GPN_E.cfg | 1 + config-files/JASON-1_L2_OST_GPR_E.cfg | 1 + config-files/JASON-1_L2_OST_GPS_E.cfg | 1 + config-files/JASON_3_L2_OST_OGDR_GPS.cfg | 1 + .../JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F.cfg | 2 +- .../JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F.cfg | 2 +- .../MERGED_TP_J1_OSTM_OST_CYCLES_V42.cfg | 1 + config-files/MODIS_A-JPL-L2P-v2014.0.cfg | 1 + config-files/MODIS_A-JPL-L2P-v2019.0.cfg | 1 + config-files/MODIS_T-JPL-L2P-v2014.0.cfg | 1 + config-files/MODIS_T-JPL-L2P-v2019.0.cfg | 1 + config-files/NAVO-L2P-AVHRR19_G.cfg | 3 +- .../OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2.cfg | 1 + config-files/PRIM_SMAP_L2_V1.cfg | 7 +- config-files/QSCAT_LEVEL_2B_OWV_COMP_12.cfg | 1 + ...T_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1.cfg | 1 + .../QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1.cfg | 1 + .../SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg | 7 +- config-files/SMAP_JPL_L2B_NRT_SSS_CAP_V5.cfg | 1 + config-files/SMAP_RSS_L2_SSS_V4.cfg | 1 + config-files/SMAP_RSS_L2_SSS_V5.cfg | 8 +- config-files/SMAP_RSS_L2_SSS_V6.cfg | 7 +- ...D_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1.cfg | 2 +- config-files/TMI-REMSS-L2P-v4.cfg | 1 + config-files/VIIRS_N20-OSPO-L2P-v2.61.cfg | 1 + config-files/VIIRS_NPP-JPL-L2P-v2016.2.cfg | 1 + config-files/VIIRS_NPP-NAVO-L2P-v1.0.cfg | 1 + config-files/VIIRS_NPP-NAVO-L2P-v2.0.cfg | 1 + config-files/VIIRS_NPP-NAVO-L2P-v3.0.cfg | 1 + config-files/VIIRS_NPP-OSPO-L2P-v2.41.cfg | 1 + schema.json | 290 ++++++++++++++++++ test_configs.py | 27 +- 60 files changed, 441 insertions(+), 55 deletions(-) create mode 100644 schema.json diff --git a/config-files/ALTIKA_SARAL_L2_OST_XOGDR.cfg b/config-files/ALTIKA_SARAL_L2_OST_XOGDR.cfg index f17a5c1..14d2155 100644 --- a/config-files/ALTIKA_SARAL_L2_OST_XOGDR.cfg +++ b/config-files/ALTIKA_SARAL_L2_OST_XOGDR.cfg @@ -1,4 +1,5 @@ { + "shortName": "ALTIKA_SARAL_L2_OST_XOGDR", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSR2-REMSS-L2P-v7.2.cfg b/config-files/AMSR2-REMSS-L2P-v7.2.cfg index 54f548d..24f24a1 100644 --- a/config-files/AMSR2-REMSS-L2P-v7.2.cfg +++ b/config-files/AMSR2-REMSS-L2P-v7.2.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSR2-REMSS-L2P-v7.2", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSR2-REMSS-L2P-v8.2.cfg b/config-files/AMSR2-REMSS-L2P-v8.2.cfg index c545829..74bb82b 100644 --- a/config-files/AMSR2-REMSS-L2P-v8.2.cfg +++ b/config-files/AMSR2-REMSS-L2P-v8.2.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSR2-REMSS-L2P-v8.2", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSR2-REMSS-L2P-v8a.cfg b/config-files/AMSR2-REMSS-L2P-v8a.cfg index 54f548d..43f9d5e 100644 --- a/config-files/AMSR2-REMSS-L2P-v8a.cfg +++ b/config-files/AMSR2-REMSS-L2P-v8a.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSR2-REMSS-L2P-v8a", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSR2-REMSS-L2P_RT-v8.2.cfg b/config-files/AMSR2-REMSS-L2P_RT-v8.2.cfg index c545829..b56fc35 100644 --- a/config-files/AMSR2-REMSS-L2P_RT-v8.2.cfg +++ b/config-files/AMSR2-REMSS-L2P_RT-v8.2.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSR2-REMSS-L2P_RT-v8.2", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSR2-REMSS-L2P_RT_v8a.cfg b/config-files/AMSR2-REMSS-L2P_RT_v8a.cfg index 54f548d..5e8de07 100644 --- a/config-files/AMSR2-REMSS-L2P_RT_v8a.cfg +++ b/config-files/AMSR2-REMSS-L2P_RT_v8a.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSR2-REMSS-L2P_RT_v8a", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AMSRE-REMSS-L2P-v7a.cfg b/config-files/AMSRE-REMSS-L2P-v7a.cfg index 54f548d..e28c712 100644 --- a/config-files/AMSRE-REMSS-L2P-v7a.cfg +++ b/config-files/AMSRE-REMSS-L2P-v7a.cfg @@ -1,4 +1,5 @@ { + "shortName": "AMSRE-REMSS-L2P-v7a", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AQUARIUS_L2_SSS_CAP_V5.cfg b/config-files/AQUARIUS_L2_SSS_CAP_V5.cfg index f04869d..857e08d 100644 --- a/config-files/AQUARIUS_L2_SSS_CAP_V5.cfg +++ b/config-files/AQUARIUS_L2_SSS_CAP_V5.cfg @@ -1,4 +1,5 @@ { + "shortName": "AQUARIUS_L2_SSS_CAP_V5", "latVar": "beam_clat", "lonVar": "beam_clon", "timeVar": "sec", diff --git a/config-files/AQUARIUS_L2_SSS_V5.cfg b/config-files/AQUARIUS_L2_SSS_V5.cfg index c976bf5..1be0a61 100644 --- a/config-files/AQUARIUS_L2_SSS_V5.cfg +++ b/config-files/AQUARIUS_L2_SSS_V5.cfg @@ -1,4 +1,5 @@ { + "shortName": "AQUARIUS_L2_SSS_V5", "latVar": "Navigation/beam_clat", "lonVar": "Navigation/beam_clon", "timeVar": "Block Attributes/secGPS", @@ -40,7 +41,7 @@ "title": "Sea Surface Salinity uncertainty (random)", "units": "PSU", "min": "0", - "max": ".5", + "max": "0.5", "palette": "palette_AQUARIUS_SSS", "id": "Aquarius Data/SSS_unc_ran" }, @@ -93,4 +94,4 @@ "id": "Aquarius Data/SSS_unc" } ] -} \ No newline at end of file +} diff --git a/config-files/ASCATA-L2-25km.cfg b/config-files/ASCATA-L2-25km.cfg index d5b4719..97dbf1e 100644 --- a/config-files/ASCATA-L2-25km.cfg +++ b/config-files/ASCATA-L2-25km.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATA-L2-25km", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/ASCATA-L2-Coastal.cfg b/config-files/ASCATA-L2-Coastal.cfg index ee98494..36ccf73 100644 --- a/config-files/ASCATA-L2-Coastal.cfg +++ b/config-files/ASCATA-L2-Coastal.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATA-L2-Coastal", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/ASCATB-L2-25km.cfg b/config-files/ASCATB-L2-25km.cfg index d5b4719..a1f6266 100644 --- a/config-files/ASCATB-L2-25km.cfg +++ b/config-files/ASCATB-L2-25km.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATB-L2-25km", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/ASCATB-L2-Coastal.cfg b/config-files/ASCATB-L2-Coastal.cfg index ee98494..6bb1776 100644 --- a/config-files/ASCATB-L2-Coastal.cfg +++ b/config-files/ASCATB-L2-Coastal.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATB-L2-Coastal", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/ASCATC-L2-25km.cfg b/config-files/ASCATC-L2-25km.cfg index d5b4719..275c40b 100644 --- a/config-files/ASCATC-L2-25km.cfg +++ b/config-files/ASCATC-L2-25km.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATC-L2-25km", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/ASCATC-L2-Coastal.cfg b/config-files/ASCATC-L2-Coastal.cfg index ee98494..fa11f47 100644 --- a/config-files/ASCATC-L2-Coastal.cfg +++ b/config-files/ASCATC-L2-Coastal.cfg @@ -1,4 +1,5 @@ { + "shortName": "ASCATC-L2-Coastal", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg index 1d6be94..1fa9c68 100644 --- a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRR19_G-NAVO-L2P-v1.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", @@ -14,11 +15,17 @@ "alpha_shape": { "alpha": 0.2, "thinning": { - "method": "big_avg", - "value": [1.5,1.5] + "method": "bin_avg", + "value": [ + 1.5, + 1.5 + ] }, "cutoff_lat": 80, - "smooth_poles": [78,80] + "smooth_poles": [ + 78, + 80 + ] } }, "footprinter": "forge-py", diff --git a/config-files/AVHRRF_MA-STAR-L2P-v2.80.cfg b/config-files/AVHRRF_MA-STAR-L2P-v2.80.cfg index 0dc5c09..1cbed48 100644 --- a/config-files/AVHRRF_MA-STAR-L2P-v2.80.cfg +++ b/config-files/AVHRRF_MA-STAR-L2P-v2.80.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRRF_MA-STAR-L2P-v2.80", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AVHRRF_MB-STAR-L2P-v2.80.cfg b/config-files/AVHRRF_MB-STAR-L2P-v2.80.cfg index 0dc5c09..b2f5e0e 100644 --- a/config-files/AVHRRF_MB-STAR-L2P-v2.80.cfg +++ b/config-files/AVHRRF_MB-STAR-L2P-v2.80.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRRF_MB-STAR-L2P-v2.80", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AVHRRF_MC-STAR-L2P-v2.80.cfg b/config-files/AVHRRF_MC-STAR-L2P-v2.80.cfg index 0dc5c09..3c3b136 100644 --- a/config-files/AVHRRF_MC-STAR-L2P-v2.80.cfg +++ b/config-files/AVHRRF_MC-STAR-L2P-v2.80.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRRF_MC-STAR-L2P-v2.80", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg index 84c3dd9..eea37e2 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v1.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRRMTA_G-NAVO-L2P-v1.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", @@ -15,10 +16,16 @@ "alpha": 0.1, "thinning": { "method": "bin_avg", - "value": [1.5,1.5] + "value": [ + 1.5, + 1.5 + ] }, "cutoff_lat": 80, - "smooth_poles": [78,80] + "smooth_poles": [ + 78, + 80 + ] } }, "footprinter": "forge-py", @@ -68,4 +75,4 @@ "ppd": 4, "res": 8 } -} +} \ No newline at end of file diff --git a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg index 4c1bcb0..2512fa8 100644 --- a/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg +++ b/config-files/AVHRRMTA_G-NAVO-L2P-v2.0.cfg @@ -15,11 +15,17 @@ "alpha_shape": { "alpha": 0.065, "thinning": { - "method": "big_avg", - "value": [1.5, 1.5] + "method": "bin_avg", + "value": [ + 1.5, + 1.5 + ] }, "cutoff_lat": 80, - "smooth_poles": [78,80] + "smooth_poles": [ + 78, + 80 + ] } }, "footprinter": "forge-py", diff --git a/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg index 14b8efb..3879505 100644 --- a/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRRMTB_G-NAVO-L2P-v1.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRRMTB_G-NAVO-L2P-v1.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", @@ -18,7 +19,10 @@ "value": 240 }, "cutoff_lat": 80, - "smooth_poles": [78,80] + "smooth_poles": [ + 78, + 80 + ] } }, "footprinter": "forge-py", diff --git a/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg b/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg index 787d1d8..4c17e15 100644 --- a/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg +++ b/config-files/AVHRRMTB_G-NAVO-L2P-v2.0.cfg @@ -19,7 +19,10 @@ "value": 220 }, "cutoff_lat": 80, - "smooth_poles": [78,80] + "smooth_poles": [ + 78, + 80 + ] } }, "imgVariables": [ diff --git a/config-files/AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg b/config-files/AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg index 149443d..86ae368 100644 --- a/config-files/AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg +++ b/config-files/AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "AVHRR_SST_METOP_B-OSISAF-L2P-v1.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg index be8f957..c67a86e 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V10.0.cfg @@ -1,18 +1,17 @@ { "shortName": "COWVR_STPH8_L1_TSDR_V10.0", - "latVar": "obs_lat", - "lonVar": "obs_lon", + "latVar": "GeolocationAndFlags/obs_lat", + "lonVar": "GeolocationAndFlags/obs_lon", "is360": false, "footprint": { "strategy": "alpha_shape", - "alpha_shape":{ + "alpha_shape": { "thinning": { - "value": "200", - "method" : "standard" + "value": 200, + "method": "standard" }, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "alpha": 0.03 } }, "footprinter": "forge-py" -} \ No newline at end of file +} diff --git a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg index 8d1b427..4d2bad1 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg @@ -5,10 +5,10 @@ "is360": false, "footprint": { "strategy": "alpha_shape", - "alpha_shape":{ + "alpha_shape": { "thinning": { - "value": "200", - "method" : "standard" + "value": 200, + "method": "standard" }, "alpha": 0.03, "fill_value": -99990.0 diff --git a/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg b/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg index 4efdfaa..6ab7aec 100644 --- a/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg +++ b/config-files/COWVR_STPH8_L2_EDR_V10.0.cfg @@ -1,18 +1,17 @@ { "shortName": "COWVR_STPH8_L2_EDR_V10.0", - "latVar": "obs_lat", - "lonVar": "obs_lon", + "latVar": "GeolocationAndFlags/obs_lat", + "lonVar": "GeolocationAndFlags/obs_lon", "is360": false, "footprint": { "strategy": "alpha_shape", - "alpha_shape":{ + "alpha_shape": { "thinning": { - "value": "200", - "method" : "standard" + "value": 200, + "method": "standard" }, - "alpha": 0.03, - "group": "GeolocationAndFlags" + "alpha": 0.03 } }, "footprinter": "forge-py" -} \ No newline at end of file +} diff --git a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg index ccf71a9..8094a3c 100644 --- a/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L2_EDR_V9.0.cfg @@ -5,14 +5,17 @@ "is360": false, "footprint": { "strategy": "alpha_shape", - "alpha_shape":{ + "alpha_shape": { "thinning": { - "value": [1, 1], - "method" : "bin_avg" + "value": [ + 1, + 1 + ], + "method": "bin_avg" }, "alpha": 0.03, "fill_value": -99999.0 } }, "footprinter": "forge-py" -} +} \ No newline at end of file diff --git a/config-files/JASON-1_L2_OST_GPN_E.cfg b/config-files/JASON-1_L2_OST_GPN_E.cfg index a7b1b63..0f607e3 100644 --- a/config-files/JASON-1_L2_OST_GPN_E.cfg +++ b/config-files/JASON-1_L2_OST_GPN_E.cfg @@ -1,4 +1,5 @@ { + "shortName": "JASON-1_L2_OST_GPN_E", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/JASON-1_L2_OST_GPR_E.cfg b/config-files/JASON-1_L2_OST_GPR_E.cfg index 8a3821b..e79078d 100644 --- a/config-files/JASON-1_L2_OST_GPR_E.cfg +++ b/config-files/JASON-1_L2_OST_GPR_E.cfg @@ -1,4 +1,5 @@ { + "shortName": "JASON-1_L2_OST_GPR_E", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/JASON-1_L2_OST_GPS_E.cfg b/config-files/JASON-1_L2_OST_GPS_E.cfg index a7b1b63..4ac4ded 100644 --- a/config-files/JASON-1_L2_OST_GPS_E.cfg +++ b/config-files/JASON-1_L2_OST_GPS_E.cfg @@ -1,4 +1,5 @@ { + "shortName": "JASON-1_L2_OST_GPS_E", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/JASON_3_L2_OST_OGDR_GPS.cfg b/config-files/JASON_3_L2_OST_OGDR_GPS.cfg index e04ae88..96b2bc1 100644 --- a/config-files/JASON_3_L2_OST_OGDR_GPS.cfg +++ b/config-files/JASON_3_L2_OST_OGDR_GPS.cfg @@ -1,4 +1,5 @@ { + "shortName": "JASON_3_L2_OST_OGDR_GPS", "latVar": "/data_01/latitude", "lonVar": "/data_01/longitude", "timeVar": "/data_01/time", diff --git a/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F.cfg b/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F.cfg index e1344e7..7aae2fa 100644 --- a/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F.cfg +++ b/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F.cfg @@ -1,5 +1,5 @@ { - "shortName": "JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F", + "shortName": "JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F", "latVar": "data_01/latitude", "lonVar": "data_01/longitude", "timeVar": "data_01/time", diff --git a/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F.cfg b/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F.cfg index 7aae2fa..e1344e7 100644 --- a/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F.cfg +++ b/config-files/JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F.cfg @@ -1,5 +1,5 @@ { - "shortName": "JASON_CS_S6A_L2_ALT_HR_RED_OST_NRT_F", + "shortName": "JASON_CS_S6A_L2_ALT_HR_RED_OST_STC_F", "latVar": "data_01/latitude", "lonVar": "data_01/longitude", "timeVar": "data_01/time", diff --git a/config-files/MERGED_TP_J1_OSTM_OST_CYCLES_V42.cfg b/config-files/MERGED_TP_J1_OSTM_OST_CYCLES_V42.cfg index 56c9e2a..6c83bd4 100644 --- a/config-files/MERGED_TP_J1_OSTM_OST_CYCLES_V42.cfg +++ b/config-files/MERGED_TP_J1_OSTM_OST_CYCLES_V42.cfg @@ -1,4 +1,5 @@ { + "shortName": "MERGED_TP_J1_OSTM_OST_CYCLES_V42", "latVar": "latitude", "lonVar": "longitude", "timeVar": "time", diff --git a/config-files/MODIS_A-JPL-L2P-v2014.0.cfg b/config-files/MODIS_A-JPL-L2P-v2014.0.cfg index 899b521..a097053 100644 --- a/config-files/MODIS_A-JPL-L2P-v2014.0.cfg +++ b/config-files/MODIS_A-JPL-L2P-v2014.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "MODIS_A-JPL-L2P-v2014.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/MODIS_A-JPL-L2P-v2019.0.cfg b/config-files/MODIS_A-JPL-L2P-v2019.0.cfg index 6fb8af5..074aee2 100644 --- a/config-files/MODIS_A-JPL-L2P-v2019.0.cfg +++ b/config-files/MODIS_A-JPL-L2P-v2019.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "MODIS_A-JPL-L2P-v2019.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/MODIS_T-JPL-L2P-v2014.0.cfg b/config-files/MODIS_T-JPL-L2P-v2014.0.cfg index 899b521..8107e59 100644 --- a/config-files/MODIS_T-JPL-L2P-v2014.0.cfg +++ b/config-files/MODIS_T-JPL-L2P-v2014.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "MODIS_T-JPL-L2P-v2014.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/MODIS_T-JPL-L2P-v2019.0.cfg b/config-files/MODIS_T-JPL-L2P-v2019.0.cfg index 899b521..70a1422 100644 --- a/config-files/MODIS_T-JPL-L2P-v2019.0.cfg +++ b/config-files/MODIS_T-JPL-L2P-v2019.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "MODIS_T-JPL-L2P-v2019.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/NAVO-L2P-AVHRR19_G.cfg b/config-files/NAVO-L2P-AVHRR19_G.cfg index 7893b9a..230dc38 100644 --- a/config-files/NAVO-L2P-AVHRR19_G.cfg +++ b/config-files/NAVO-L2P-AVHRR19_G.cfg @@ -1,4 +1,5 @@ { + "shortName": "NAVO-L2P-AVHRR19_G", "latVar": "lat", "lonVar": "lon", "timeVar": "time", @@ -90,4 +91,4 @@ "ppd": 4, "res": 8 } -} \ No newline at end of file +} diff --git a/config-files/OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2.cfg b/config-files/OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2.cfg index 26ab7b0..773bc9e 100644 --- a/config-files/OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2.cfg +++ b/config-files/OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2.cfg @@ -1,4 +1,5 @@ { + "shortName": "OS2_OSCAT_LEVEL_2B_OWV_COMP_12_V2", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/PRIM_SMAP_L2_V1.cfg b/config-files/PRIM_SMAP_L2_V1.cfg index 5b87601..7fe3dc9 100644 --- a/config-files/PRIM_SMAP_L2_V1.cfg +++ b/config-files/PRIM_SMAP_L2_V1.cfg @@ -16,7 +16,10 @@ "alpha": 0.035, "thinning": { "method": "bin_avg", - "value": [1.5,1.5] + "value": [ + 1.5, + 1.5 + ] } } }, @@ -63,4 +66,4 @@ "ppd": 4, "res": 8 } -} +} \ No newline at end of file diff --git a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12.cfg b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12.cfg index ea00126..1c39a47 100644 --- a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12.cfg +++ b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12.cfg @@ -1,4 +1,5 @@ { + "shortName": "QSCAT_LEVEL_2B_OWV_COMP_12", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1.cfg b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1.cfg index ea00126..7f0edc6 100644 --- a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1.cfg +++ b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1.cfg @@ -1,4 +1,5 @@ { + "shortName": "QSCAT_LEVEL_2B_OWV_COMP_12_KUSST_LCRES_4.1", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1.cfg b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1.cfg index ea00126..0ce8ee5 100644 --- a/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1.cfg +++ b/config-files/QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1.cfg @@ -1,4 +1,5 @@ { + "shortName": "QSCAT_LEVEL_2B_OWV_COMP_12_LCR_3.1", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg index 2fe35af..2e10a24 100644 --- a/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg +++ b/config-files/SCATSAT1_ESDR_L2_WIND_STRESS_V1.1.cfg @@ -19,7 +19,10 @@ "value": 30 }, "cutoff_lat": 88, - "smooth_poles": [82,90] + "smooth_poles": [ + 82, + 90 + ] } }, "footprinter": "forge-py", @@ -45,4 +48,4 @@ "ppd": 4, "res": 8 } -} +} \ No newline at end of file diff --git a/config-files/SMAP_JPL_L2B_NRT_SSS_CAP_V5.cfg b/config-files/SMAP_JPL_L2B_NRT_SSS_CAP_V5.cfg index 27ca8e5..a5ccb63 100644 --- a/config-files/SMAP_JPL_L2B_NRT_SSS_CAP_V5.cfg +++ b/config-files/SMAP_JPL_L2B_NRT_SSS_CAP_V5.cfg @@ -1,4 +1,5 @@ { + "shortName": "SMAP_JPL_L2B_NRT_SSS_CAP_V5", "latVar": "lat", "lonVar": "lon", "timeVar": "row_time", diff --git a/config-files/SMAP_RSS_L2_SSS_V4.cfg b/config-files/SMAP_RSS_L2_SSS_V4.cfg index 24dab79..b7c5651 100644 --- a/config-files/SMAP_RSS_L2_SSS_V4.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V4.cfg @@ -1,4 +1,5 @@ { + "shortName": "SMAP_RSS_L2_SSS_V4", "latVar": "cellat", "lonVar": "cellon", "timeVar": "time", diff --git a/config-files/SMAP_RSS_L2_SSS_V5.cfg b/config-files/SMAP_RSS_L2_SSS_V5.cfg index 41be463..1152153 100644 --- a/config-files/SMAP_RSS_L2_SSS_V5.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V5.cfg @@ -1,4 +1,5 @@ { + "shortName": "SMAP_RSS_L2_SSS_V5", "latVar": "cellat", "lonVar": "cellon", "timeVar": "time", @@ -9,7 +10,10 @@ "alpha": 0.03, "thinning": { "method": "bin_avg", - "value": [0.75, 0.75] + "value": [ + 0.75, + 0.75 + ] } } }, @@ -47,4 +51,4 @@ "ppd": 4, "res": 8 } -} +} \ No newline at end of file diff --git a/config-files/SMAP_RSS_L2_SSS_V6.cfg b/config-files/SMAP_RSS_L2_SSS_V6.cfg index 85bcbe2..1c9a7df 100644 --- a/config-files/SMAP_RSS_L2_SSS_V6.cfg +++ b/config-files/SMAP_RSS_L2_SSS_V6.cfg @@ -16,7 +16,10 @@ "alpha": 0.03, "thinning": { "method": "bin_avg", - "value": [1,1] + "value": [ + 1, + 1 + ] } } }, @@ -45,4 +48,4 @@ "ppd": 4, "res": 8 } -} +} \ No newline at end of file diff --git a/config-files/SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1.cfg b/config-files/SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1.cfg index 1d2547f..2ac2e8b 100644 --- a/config-files/SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1.cfg +++ b/config-files/SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1.cfg @@ -1,5 +1,5 @@ { - "shortName": "SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1", + "shortName": "SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_SCIENCE_V1", "latVar": "latitude", "lonVar": "longitude", "timeVar": "time", diff --git a/config-files/TMI-REMSS-L2P-v4.cfg b/config-files/TMI-REMSS-L2P-v4.cfg index 54f548d..085e0f2 100644 --- a/config-files/TMI-REMSS-L2P-v4.cfg +++ b/config-files/TMI-REMSS-L2P-v4.cfg @@ -1,4 +1,5 @@ { + "shortName": "TMI-REMSS-L2P-v4", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_N20-OSPO-L2P-v2.61.cfg b/config-files/VIIRS_N20-OSPO-L2P-v2.61.cfg index 0cd1210..356f396 100644 --- a/config-files/VIIRS_N20-OSPO-L2P-v2.61.cfg +++ b/config-files/VIIRS_N20-OSPO-L2P-v2.61.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_N20-OSPO-L2P-v2.61", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_NPP-JPL-L2P-v2016.2.cfg b/config-files/VIIRS_NPP-JPL-L2P-v2016.2.cfg index 4bdacce..029c5d6 100644 --- a/config-files/VIIRS_NPP-JPL-L2P-v2016.2.cfg +++ b/config-files/VIIRS_NPP-JPL-L2P-v2016.2.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_NPP-JPL-L2P-v2016.2", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_NPP-NAVO-L2P-v1.0.cfg b/config-files/VIIRS_NPP-NAVO-L2P-v1.0.cfg index 0cd1210..0b4f4be 100644 --- a/config-files/VIIRS_NPP-NAVO-L2P-v1.0.cfg +++ b/config-files/VIIRS_NPP-NAVO-L2P-v1.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_NPP-NAVO-L2P-v1.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_NPP-NAVO-L2P-v2.0.cfg b/config-files/VIIRS_NPP-NAVO-L2P-v2.0.cfg index 0cd1210..33a053b 100644 --- a/config-files/VIIRS_NPP-NAVO-L2P-v2.0.cfg +++ b/config-files/VIIRS_NPP-NAVO-L2P-v2.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_NPP-NAVO-L2P-v2.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_NPP-NAVO-L2P-v3.0.cfg b/config-files/VIIRS_NPP-NAVO-L2P-v3.0.cfg index 0cd1210..91e6388 100644 --- a/config-files/VIIRS_NPP-NAVO-L2P-v3.0.cfg +++ b/config-files/VIIRS_NPP-NAVO-L2P-v3.0.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_NPP-NAVO-L2P-v3.0", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/config-files/VIIRS_NPP-OSPO-L2P-v2.41.cfg b/config-files/VIIRS_NPP-OSPO-L2P-v2.41.cfg index 0cd1210..b24fd37 100644 --- a/config-files/VIIRS_NPP-OSPO-L2P-v2.41.cfg +++ b/config-files/VIIRS_NPP-OSPO-L2P-v2.41.cfg @@ -1,4 +1,5 @@ { + "shortName": "VIIRS_NPP-OSPO-L2P-v2.41", "latVar": "lat", "lonVar": "lon", "timeVar": "time", diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..2084414 --- /dev/null +++ b/schema.json @@ -0,0 +1,290 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "shortName": { + "type": "string", + "description": "Short name of the dataset, required." + }, + "latVar": { + "type": "string", + "description": "Path to the latitude variable in the data, required." + }, + "lonVar": { + "type": "string", + "description": "Path to the longitude variable in the data, required." + }, + "timeVar": { + "type": "string", + "description": "Name of the time variable, optional." + }, + "is360": { + "type": "boolean", + "description": "Indicates if longitude is in 0-360 format, required." + }, + "tiles": { + "type": "object", + "properties": { + "steps": { + "type": "array", + "items": { + "type": "integer", + "additionalProperties": false, + "description": "Step size for each tile, represented as integers." + }, + "minItems": 2, + "maxItems": 3, + "additionalProperties": false, + "description": "An array of two integers representing the tile step sizes." + } + }, + "required": ["steps"], + "additionalProperties": false, + "description": "Tile configuration with step sizes for the grid." + }, + "global_grid": { + "type": "boolean", + "description": "Indicates whether the grid is global or not. Optional." + }, + "footprinter": { + "type": "string", + "enum": ["forge-py"], + "description": "The footprinter to use, must be 'forge-py'." + }, + "tolerance": { + "type": "number", + "description": "Tolerance value (optional). Can be a float or integer." + }, + "footprint": { + "type": "object", + "properties": { + "findValid": { + "type": "boolean", + "description": "Indicates whether to find valid footprint values." + }, + "b": { + "type": "string", + "description": "The 'b' footprint pattern." + }, + "s2": { + "type": "string", + "description": "The 's2' footprint pattern." + }, + "t": { + "type": "string", + "description": "The 't' footprint pattern." + }, + "s1": { + "type": "string", + "description": "The 's1' footprint pattern." + }, + "removeOrigin": { + "type": "boolean", + "description": "Unknown usage for forge" + }, + "strategy": { + "type": "string", + "enum": ["open_cv", "alpha_shape", "linestring", "periodic", "swot_linestring", "smap", "polarsides", "polar"], + "description": "Footprint calculation strategy, required." + }, + "open_cv": { + "type": "object", + "properties": { + "pixel_height": { + "type": "integer", + "minimum": 1, + "description": "Height of the pixel grid, optional." + }, + "simplify": { + "type": "number", + "minimum": 0, + "description": "Simplification tolerance for contours, optional." + }, + "min_area": { + "type": "integer", + "minimum": 0, + "description": "Minimum area for a valid footprint, optional." + }, + "fill_kernel": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + }, + "minItems": 2, + "maxItems": 2, + "description": "Kernel size for filling small gaps, optional." + } + }, + "additionalProperties": false, + "description": "Parameters for the OpenCV strategy, optional." + }, + "alpha_shape": { + "type": "object", + "properties": { + "alpha": { + "type": "number", + "minimum": 0, + "description": "Alpha parameter for the alpha shape algorithm, optional." + }, + "thinning": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["bin_avg", "standard"], + "description": "Thinning method, optional." + }, + "value": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2 + } + ] + } + }, + "additionalProperties": false, + "description": "Parameters for thinning, optional." + }, + "cutoff_lat": { + "type": "number", + "description": "Latitude cutoff for the footprint, optional." + }, + "smooth_poles": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2, + "description": "Latitude range for smoothing near poles, optional." + }, + "simplify": { + "type": "number", + "minimum": 0, + "description": "Simplification tolerance for contours, optional." + }, + "min_area": { + "type": "number", + "minimum": 0, + "description": "Minimum area for a valid footprint, optional." + }, + "fill_value": { + "type": "number", + "description": "Fill value for invalid areas, optional." + } + }, + "additionalProperties": false, + "description": "Parameters for the alpha shape strategy, optional." + } + }, + "additionalProperties": false, + "description": "Footprint calculation parameters, optional." + }, + "imgVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "ID of the image variable, required." + }, + "title": { + "type": "string", + "description": "Title of the image variable, optional." + }, + "units": { + "type": "string", + "description": "Units of the image variable, optional." + }, + "fill_missing": { + "type": "boolean", + "description": "Fill in the image if the data resolution is too high and needs to be filled in spots" + }, + "fill_value": { + "type": "number", + "description": "Fill value for a variable if theres no fill value" + }, + "is_swot_expert": { + "type": "boolean", + "description": "boolean to indicate if variable is a swot expert variable for a specific algorithm" + }, + "ppd": { + "type": "integer", + "description": "Pixels per degree (ppd) for the variable." + }, + "min": { + "oneOf": [ + { + "type": "number", + "description": "Minimum value of the variable as a number (integer or float)." + }, + { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$", + "description": "Minimum value of the variable as a string. Must be a valid number." + } + ] + }, + "max": { + "oneOf": [ + { + "type": "number", + "description": "Maximum value of the variable as a number (integer or float)." + }, + { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$", + "description": "Maximum value of the variable as a string. Must be a valid number." + } + ] + }, + "palette": { + "type": "string", + "description": "Palette used for visualization, optional." + }, + "legends": { + "type": "array", + "items": { + "type": "string", + "description": "Legend file name, expected to be a string representing a file path. Note should be invalid for new tig should be removed" + }, + "description": "List of legend files." + } + }, + "required": ["id", "min", "max"], + "additionalProperties": false, + "description": "Properties of an image variable." + }, + "description": "List of image variables, optional." + }, + "image": { + "type": "object", + "properties": { + "ppd": { + "type": "integer", + "description": "Pixels per degree (ppd) for the image." + }, + "res": { + "type": "integer", + "description": "Resolution of the image (res)." + } + }, + "required": ["ppd", "res"], + "additionalProperties": false, + "description": "Image configuration with pixels per degree and resolution." + } + + }, + "required": ["shortName", "latVar", "lonVar", "is360"], + "additionalProperties": false +} diff --git a/test_configs.py b/test_configs.py index a5452da..6dc9e10 100644 --- a/test_configs.py +++ b/test_configs.py @@ -1,17 +1,30 @@ import os import json import pytest +from jsonschema import validate, ValidationError # Directory containing the configuration files CONFIG_DIR = 'config-files' +# Load the schema once +with open("schema.json", "r") as schema_file: + SCHEMA = json.load(schema_file) + +# Parameterize the test with all `.cfg` files in CONFIG_DIR @pytest.mark.parametrize("config_file", [ os.path.join(CONFIG_DIR, f) for f in os.listdir(CONFIG_DIR) if f.endswith('.cfg') ]) -def test_json_loadable(config_file): - """Test if a configuration file can be loaded as JSON.""" - with open(config_file, 'r') as f: - try: - json.load(f) - except json.JSONDecodeError as e: - pytest.fail(f"Failed to load JSON from {config_file}: {e}") +def test_json_schema(config_file): + """Test JSON configuration files against the schema.""" + # Load the JSON config + try: + with open(config_file, "r") as f: + config = json.load(f) + except json.JSONDecodeError as e: + pytest.fail(f"Failed to load JSON from {config_file}: {e}") + + # Validate the JSON against the schema + try: + validate(instance=config, schema=SCHEMA) + except ValidationError as e: + pytest.fail(f"Validation failed for {config_file}: {e}") \ No newline at end of file From 2c294c81028b714b3fc92c3d909e961dc1d7649d Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 21 Nov 2024 09:18:44 -0800 Subject: [PATCH 04/25] update schema --- schema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/schema.json b/schema.json index 2084414..0e838d1 100644 --- a/schema.json +++ b/schema.json @@ -98,12 +98,12 @@ "simplify": { "type": "number", "minimum": 0, - "description": "Simplification tolerance for contours, optional." + "description": "Simplification tolerance for a polygon, optional." }, "min_area": { "type": "integer", "minimum": 0, - "description": "Minimum area for a valid footprint, optional." + "description": "Minimum area for a polygon or it will be removed, optional." }, "fill_kernel": { "type": "array", @@ -170,12 +170,12 @@ "simplify": { "type": "number", "minimum": 0, - "description": "Simplification tolerance for contours, optional." + "description": "Simplification tolerance for a polygon, optional." }, "min_area": { "type": "number", "minimum": 0, - "description": "Minimum area for a valid footprint, optional." + "description": "Minimum area for a polygon or it will be removed, optional." }, "fill_value": { "type": "number", @@ -261,7 +261,7 @@ "description": "List of legend files." } }, - "required": ["id", "min", "max"], + "required": ["id", "min", "max", "palette"], "additionalProperties": false, "description": "Properties of an image variable." }, From ef46223d94944506b6ad06c155eae94a8de83dc0 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 21 Nov 2024 09:21:33 -0800 Subject: [PATCH 05/25] add pip install json schema to github actions --- .github/workflows/deploy.yml | 3 ++- .github/workflows/test_configs.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cc1d921..966ed0d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -26,7 +26,8 @@ jobs: run: | python -m pip install --upgrade pip pip install pytest - + pip install jsonschema + - name: Run pytest run: pytest diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 4293b47..46b98b6 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -20,6 +20,7 @@ jobs: run: | python -m pip install --upgrade pip pip install pytest + pip install jsonschema - name: Run pytest run: pytest From 8c1ed6038643d0154037c0bd1c0cb278371b6109 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 26 Nov 2024 14:07:54 -0800 Subject: [PATCH 06/25] more updates --- Jenkins/Jenkinsfile | 141 ------ LICENSE | 201 ++++++++ README.md | 9 +- config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg | 10 +- podaac/forge_tig_configuration/__init__.py | 0 .../example_config.numbers | Bin 0 -> 117332 bytes .../example_config.xlsx | Bin 0 -> 11932 bytes .../generate_hitide_config.py | 175 +++++++ .../new_generate_config.py | 104 ++++ poetry.lock | 466 ++++++++++++++++++ pyproject.toml | 16 + terraform/main.tf | 12 - tests/__init__.py | 0 schema.json => tests/schema.json | 0 test_configs.py => tests/test_configs.py | 0 unprocessed-files.txt | 16 - 16 files changed, 971 insertions(+), 179 deletions(-) delete mode 100644 Jenkins/Jenkinsfile create mode 100644 LICENSE create mode 100644 podaac/forge_tig_configuration/__init__.py create mode 100644 podaac/forge_tig_configuration/example_config.numbers create mode 100644 podaac/forge_tig_configuration/example_config.xlsx create mode 100644 podaac/forge_tig_configuration/generate_hitide_config.py create mode 100644 podaac/forge_tig_configuration/new_generate_config.py create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 terraform/main.tf create mode 100644 tests/__init__.py rename schema.json => tests/schema.json (100%) rename test_configs.py => tests/test_configs.py (100%) delete mode 100644 unprocessed-files.txt diff --git a/Jenkins/Jenkinsfile b/Jenkins/Jenkinsfile deleted file mode 100644 index 693243d..0000000 --- a/Jenkins/Jenkinsfile +++ /dev/null @@ -1,141 +0,0 @@ -pipeline { - agent none - options { - preserveStashes(buildCount: 5) - } - environment{ - ARTIFACTORY_DOCKER_REGISTRY = "cae-artifactory.jpl.nasa.gov:16003" - } - stages{ - stage("Checkout"){ - agent { - label "devops1" - } - steps{ - checkout([ - $class: 'GitSCM', - branches: scm.branches, - extensions: scm.extensions + [ - [$class: 'CleanBeforeCheckout'], - [$class: 'WipeWorkspace'], - [$class: 'LocalBranch', localBranch: '**'], - [$class: 'PruneStaleBranch'] - ], - userRemoteConfigs: scm.userRemoteConfigs - ]) - script{ - the_env = sh(returnStdout: true, script: "printenv").trim() - } - writeYaml file: 'build-info.yaml', data: ['build_environment': the_env ], overwrite: true - stash name: 'build-info', includes: 'build-info.yaml' - } - } - stage("Run Build"){ - agent { - label 'devops1' - } - stages{ - stage("Version"){ - stages { - stage("Pre Alpha"){ - when { - anyOf{ - branch 'feature/*' - changeRequest target: 'develop' - } - } - steps{ - unstash name: 'build-info' - script{ - build_info = readYaml file: 'build-info.yaml' - build_info.deploy_env = "sit" - } - writeYaml file: 'build-info.yaml', data: build_info, overwrite: true - stash name: 'build-info', includes: 'build-info.yaml' - } - } - stage("Alpha"){ - when { - branch 'develop' - } - steps{ - unstash name: 'build-info' - script{ - build_info = readYaml file: 'build-info.yaml' - build_info.deploy_env = "sit" - } - writeYaml file: 'build-info.yaml', data: build_info, overwrite: true - stash name: 'build-info', includes: 'build-info.yaml' - } - } - stage("Release Candidate"){ - when { - changeRequest target: 'master' - } - steps{ - unstash name: 'build-info' - script{ - build_info = readYaml file: 'build-info.yaml' - build_info.deploy_env = "uat" - } - writeYaml file: 'build-info.yaml', data: build_info, overwrite: true - stash name: 'build-info', includes: 'build-info.yaml' - } - } - stage("Release"){ - when { - branch 'master' - } - steps{ - unstash name: 'build-info' - script{ - build_info = readYaml file: 'build-info.yaml' - build_info.deploy_env = "ops" - } - writeYaml file: 'build-info.yaml', data: build_info, overwrite: true - stash name: 'build-info', includes: 'build-info.yaml' - } - } - } - } - } - } - stage("Deploy"){ - when { - anyOf{ - branch 'develop' - branch 'master' - changeRequest target: 'master' - changelog '^/jenkins deploy' - } - } - agent { - docker { - image "${env.ARTIFACTORY_DOCKER_REGISTRY}/podaac/service/deploy-terraform-0.12.31" - label 'devops1' - alwaysPull true - args '-v /home/cm/.aws:/home/dockeruser/.aws:ro' - } - } - environment { - GIT_COMMIT_SHORT = sh(script: "printf \$(git rev-parse --short ${GIT_COMMIT})", returnStdout: true).trim() - } - steps{ - unstash name: 'build-info' - script{ - build_info = readYaml file: 'build-info.yaml' - } - echo "Publishing Configurations to ${build_info.deploy_env} S3" - dir('terraform'){ - sh "terraform init -reconfigure -input=false -backend-config=bucket=podaac-services-${build_info.deploy_env}-terraform -backend-config=profile=ngap-service-${build_info.deploy_env}" - sh "terraform output hitide-bucket-name" - script{ - bucket_name = sh(returnStdout: true, script: "terraform output hitide-bucket-name").trim() - } - } - sh("aws s3 sync ./config-files s3://${bucket_name}/dataset-configs --delete --profile ngap-service-${build_info.deploy_env} --metadata githash=${GIT_COMMIT_SHORT} --size-only") - sh("aws s3 sync ./palettes s3://${bucket_name}/palettes --delete --profile ngap-service-${build_info.deploy_env} --metadata githash=${GIT_COMMIT_SHORT} --size-only") - } - } - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..218c4a7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 California Institute of Technology + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 275dccb..5524807 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,2 @@ # forge-tig-configuration -Configuration files for collections processed by FORGE and TIG - -* refer to the unprocessed-files.txt about list of unprocessed files (may need to be done manually) - - -Copyright 2023, by the California Institute of Technology. ALL RIGHTS RESERVED. United States Government Sponsorship acknowledged. Any commercial use must be negotiated with the Office of Technology Transfer at the California Institute of Technology. - -This software may be subject to U.S. export control laws. By accepting this software, the user agrees to comply with all applicable U.S. export laws and regulations. User has the responsibility to obtain export licenses, or other export authority as may be required before exporting such information to foreign countries or providing access to foreign persons. \ No newline at end of file +Configuration files for collections processed by FORGE and TIG \ No newline at end of file diff --git a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg index 1fa9c68..0cb4a98 100644 --- a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg @@ -11,7 +11,7 @@ ] }, "footprint": { - "strategy": "alpha_shape", + "strategy": "open_cv", "alpha_shape": { "alpha": 0.2, "thinning": { @@ -26,7 +26,13 @@ 78, 80 ] - } + }, + "open_cv": { + "pixel_height":1000, + "simplify":0.3, + "min_area": 30, + "fill_kernel": [30,30] + } }, "footprinter": "forge-py", "imgVariables": [ diff --git a/podaac/forge_tig_configuration/__init__.py b/podaac/forge_tig_configuration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/podaac/forge_tig_configuration/example_config.numbers b/podaac/forge_tig_configuration/example_config.numbers new file mode 100644 index 0000000000000000000000000000000000000000..1621506899ee8884fcb9b96de1adba7fa38c602d GIT binary patch literal 117332 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%0A%TYKl9v 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 Q2l#hb8bLBIgVsOtAB~E*SpWb4 literal 0 HcmV?d00001 diff --git a/podaac/forge_tig_configuration/example_config.xlsx b/podaac/forge_tig_configuration/example_config.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..719fa9b044eb064d527dcb85876518e6a2d97185 GIT binary patch literal 11932 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.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[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 = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +files = [ + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "numpy" +version = "2.1.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, + {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, + {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, + {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, + {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, + {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, + {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, + {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, + {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, + {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, + {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, + {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pytest" +version = "8.3.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "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 = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "rpds-py" +version = "0.21.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, + {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, + {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, + {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, + {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, + {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, + {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, + {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, + {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, + {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, + {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.1.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "f71bd846ac600581516d4b6558a128bd48d878b39b0fa5cedac6e3dbf9373bf9" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..444db35 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "forge-tig-configuration" +version = "0.1.0" +description = "" +authors = ["Simon Liu "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +pytest = "^8.3.3" +jsonschema = "^4.23.0" +pandas = "^2.2.3" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/terraform/main.tf b/terraform/main.tf deleted file mode 100644 index 8b184ff..0000000 --- a/terraform/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -# configure the S3 backend for storing state. This allows different -# team members to control and update terraform state. -terraform { - backend "s3" { - # This must be updated for each unique deployment/stage! - # should be of the form services/APP_NAME/STAGE/terraform.tfstate - # We can't use variables in the key name here, so we need to be extra - # careful with this! - key = "services/hitide/terraform.tfstate" - region = "us-west-2" - } -} \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/schema.json b/tests/schema.json similarity index 100% rename from schema.json rename to tests/schema.json diff --git a/test_configs.py b/tests/test_configs.py similarity index 100% rename from test_configs.py rename to tests/test_configs.py diff --git a/unprocessed-files.txt b/unprocessed-files.txt deleted file mode 100644 index a46c2cc..0000000 --- a/unprocessed-files.txt +++ /dev/null @@ -1,16 +0,0 @@ - INFO:__main__:There are 14 of files unprocessed: - ERROR:__main__:Unprocessed: PODAAC-GHMDT-2PJ01.cfg - ERROR:__main__:Unprocessed: PODAAC-GHG16-2PO27.cfg - ERROR:__main__:Unprocessed: PODAAC-GHAMS-2PR01.cfg - ERROR:__main__:Unprocessed: PODAAC-SMP30-2SOCS.cfg - ERROR:__main__:Unprocessed: PODAAC-SMP3A-2SOCS.cfg - ERROR:__main__:Unprocessed: PODAAC-GHVRS-2PN16.cfg - ERROR:__main__:Unprocessed: PODAAC-GHMDA-2PJ01.cfg - ERROR:__main__:Unprocessed: AVHRR_SST_METOP_B-OSISAF-L2P-v1.0.cfg - ERROR:__main__:Unprocessed: PODAAC-OSCAR-03D01.cfg - ERROR:__main__:Unprocessed: PODAAC-GHGMR-4FJ04.cfg - ERROR:__main__:Unprocessed: PODAAC-J1SHA-NETGC.cfg - ERROR:__main__:Unprocessed: PODAAC-J1SHA-NETCC.cfg - ERROR:__main__:Unprocessed: PODAAC-AQR40-3S1CS.cfg - ERROR:__main__:Unprocessed: PODAAC-GHATS-2PU01.cfg - INFO:__main__:Successfully ran the program and close DB connection \ No newline at end of file From 9e2341e7296b79054dae1dcab3ac25f1634a9247 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 09:54:23 -0800 Subject: [PATCH 07/25] update to code --- .../example_config.numbers | Bin 117332 -> 0 bytes .../example_config.xlsx | Bin 11932 -> 11981 bytes .../new_generate_config.py | 182 +++++------ podaac/forge_tig_configuration/schema.json | 290 ++++++++++++++++++ .../~$example_config.xlsx | Bin 0 -> 165 bytes poetry.lock | 276 +++++++++++------ pyproject.toml | 4 +- tests/test_configs.py | 33 +- 8 files changed, 579 insertions(+), 206 deletions(-) delete mode 100644 podaac/forge_tig_configuration/example_config.numbers create mode 100644 podaac/forge_tig_configuration/schema.json create mode 100644 podaac/forge_tig_configuration/~$example_config.xlsx diff --git a/podaac/forge_tig_configuration/example_config.numbers b/podaac/forge_tig_configuration/example_config.numbers deleted file mode 100644 index 1621506899ee8884fcb9b96de1adba7fa38c602d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117332 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%0A%TYKl9v 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 Q2l#hb8bLBIgVsOtAB~E*SpWb4 diff --git a/podaac/forge_tig_configuration/example_config.xlsx b/podaac/forge_tig_configuration/example_config.xlsx index 719fa9b044eb064d527dcb85876518e6a2d97185..b8a375a712f63528202e50d6553858ad77776b5f 100644 GIT binary patch delta 5522 zcmZ9Qbx;&s_s3~iVre9#Q$kW$asjC&m(E36Sh|~~1SAw#TDnAX=}<&KS`aA-ffW{z z5~PuO^_}1Ro_C)6$DKR(oSA!P?){weJtrSF4nxfn04@*OHs9l5VdWrK2)GfTYd#e+ zS|Ovyi##oL`e4eMk)tyFB0v|2VPz;F9*KwTA`5+p&HbzqtX^u zPL5rZ$%N;kufTyy?Lule)1|n~~Vklmn+x!?%_7R;XVkCJMMktB$KT})HNIU}fYCSLG! zNiFU?dyC~fm8y_b6c;;%q;Z?@Jya-{6kUV-W@ItT{YhxNDjZ;(I!mVz{l6ht@LNX$38a7zZ1U+^?l6q(Js6 zO&H>mRNaUK_VI0gtTO?Yx0T(Ro6pX-=MqqsJz4YQPKjK>1}K3<#8{d;3&gTZMlk@M zbGkK76FQ3x2d=%*?R3P&9A||t!#W~&j;)1`u9sq!0iqGFBU32g(Roif>56s1|G{84<(0h+b@l(3Guv+aeIWvjs%1~hg#!v+op`cu!1 zo<)(~B0mt&HunLPafma^-P)URv9Oqkkl(2p5TXx*+}-kxRy`L*C?bkl?%0oHG4~wZ zRjS$o_W4h-GXp{c@<|#K_CHDDOYS397ot84mY0;;N_^gi<8cOFe9Auez%Cbnn^={I%m%_0)D1WMyj zL@V2`asvx!QbP!3o(Zl{c&c+BSK?qvu~j;deR|8Ls(OvogVqv|+J^gO5}I_aGj7?e zPQ7;@q;%>t3(TPsQl@$LJz!S}!e(}cO4Xb$?d_8KmC$5U_6u_O>gROO6@;fZ*F?=Y zAYs^c=(6Ry=8(UJogQc}fB>A&9-%7^vml!X& zZ24(qPorgNe-rphwUT<3S6sfJ#ShyTCA_aqNZbAy9!IceOr^_fdtmJ2M#QUJTrxT` z1iH!xkslmp_AlPw$2F`u?cUtr%#Q3%f_b`##uMb=b1@)4oCcQ6Og&R5exR(05Xz)* z+Y}0Te!jrcX43i+y1?X|mmjA5i?8{b)`>n1scc^Jk^0HZoE}ouvSOAzwXj5QH%In5 zpUr4%Xa3HAj=fX+F7cXEPraDaugJ<~?ZBF${UFnKcHdZzZhPXT5V18BSE~AQP|MPO zoX-eA3wv}n3}UHtp{i)qYLX2{80-q8cg7*nqHd1R$Zq`EQ+s6QlBEdg7X1eEU?H6$ znKey)6#6GQU@O(bM{JN}K_4{Xv^Ph_8{*G!CIO3*AB<;KlhYMezw7nx3nK2p#=^S0 z!}>ebPAvnY*=Pt4O>N~OiQtdo-7-D82{^N0<+{T=`?7*tgh30;2-$1UAvorhg{!5e z0RF+!y&V#5QLzFy(%K`^vPHU_0m_()5XL2ihrc6yRlPj(G&7%oDmgLJfZ;?@=F;_~gDg(eNd5t-ox#J2}KX``$ zWB2F9_XDlZzLaZGG@nm-G91*y#~fDhhOf!rBSr4LBMu9{;2HeG2;#qtqzmCA$}^T; z{2?UnK;V>_V-?9cxtxH4FL=D2vM!cy`8t?OkuLx?!lCHpT$u}t62Ps3x!%jm?QL4(|BO~~*t3u$NW`*86E{%$eu$kS9d z9W_nYT?rym0VU{q4I|f28chTV{MT!1g339?TVW3m_0*oUgBm)wk8=L>;#w0jOMPCO;B*p z7)7^-RyXqJgFO-X76@aoqU}?tF~eo#Na?2|vQNKM)aXVnxLGcnT6E6MtOQzq1Do{m zXp2Sp{jZbRsvr5o@8(@06Vt1@x0|nNCyDb^>C-Of9Qwwuv^#$Q&1H2)Vbhg4X#7Va zhO?%HOH}r1+CY4m>6wG~9xvoRkG|YaAnWXzct?;p)jC6)BqArWpV+D11u3z*8f`n@ zo?{JWls4Bk4iW{0M85^I?a6jMCbHJx{XX-mG4yM)^K1xccuzwJH>u7R{nnoA`AY*~ z8M!^+%e%)P-oBtrK?-1Q#3=&Bb$}87!%G?r*vCc@+c33yOn@zswdr;7j^0uKTEd?Q`Jiey8TP^7X0YjqoFgFz)zAaZ~yt}Mdh>$lIF z0jC^3o_YKQPZXxi+~G58WaEln`{BS9qCoaO+$1<}TfSd3^)mGkx6-*UrbUImfYWDy zyQ9j=+ct)z7yURS>?dL>vyvkT-f)2o4aWK|G=LL%p4njXO>9T`?SPvYkuM-9YU>P3xauN2 zy%h2}Q5c0OK)H}NFCi+Xm_Lgj@w$OIlheXu%P6|h7R&Qbnyf%Y@2WOROPDUOH|v7+ zt$W97!g_%x{D7>a;-XAniP&X4J*Bj?&-z`Od!$m|dE;Z=!$Z3=;)N5pR)ffX!2G&T zk1a;)3VtWcJQl#_x0y3%89>FWS5+t1a=fGJfzi61ak$y>{y=oCj4or%ysuq8^A~FiZ4If4kVKC30+F zerx5=eWdXQ=@67drc9C=1f%|>$;*&(bEO8ttaX|Ezr9jLTl*Oe`<>Hi zYWc^tI%`m;`?`c|R`PKk-p6A!VU=b+K1@4?&JC9)X@}b_a62imhqOHpbC`{RvYH2` zm9s00O#iUGo3c+7!-*?ki90cKFz96Zz`!OYS!m+TODkE>dS#EHoP}Mj&Z8Ksdp@k>f$Od1K-p*kgTVvJI}3gy7zA?Bxg@f?pkDzCIajo%as# z9m+vPs_Lts6;SM^t5_$t_j=6h=j?;Q%P$%lIc1&#e+jpgM^1luGanxJvzd3jMNq|^d!v#f zZ66yd(fh+vKSJ1!D=Pk#KGORp+h0%mgkmW-8JV7)nMteF<~TOYV)sPJw{q}$@Is#2 zT)ED*%~rz~nn49(5H4PK+%w2~8umK&l=TpdAoqNuOeW)y&zdZ$c%?YxK>CH-%=qIZ zD(_%vnQ^T;@>wz4s5h9QyZXn({?#(?4EN`(1iMGFr;r3WIvwHV1gh@m_0H_7Lw-t* zG;5l>ftei7q5$_CTVv%NSc4x&HgxgWPZ(gc)wub`_mo4*dvbX!ur=5$d~~aX8WS=KHcg z@-5y45%am-pN;0reVD&t-0vq+!yG@U?G18&_Anq1s+V%@k52tv)3M95V!b0`An4C0 zsZUS|3za5~BkNXSQHn9uw7Y3Hybd2W5r!M-l}cY--@jeKkeCcK@n=#y3OileyYf#j zG~R?76K4^bb5N)11h?_~m=M+GHws2~Qna?6c9-s2wmnUO!U&j_j|Lqa#1}lOA0vza~%?Oy40hmkpim@17%K$Qk^L6r5(d#RZ40gw07`Z=au1 z*8{AFQuZ^^Bbq>XyNP~?K{^YuJc2}Tg5!!YNqZ%+eSl}3L%?Vw?!mDLZqUQ_Askb> z8Jw4*j@zNBK62GDfN|RbIqqnuR*NcJ&WnUS5YJIBZYk@7#;_Bb{PHr2==-NM<++$2 zTWrEN_xGgqwdX638FJy2R=+QHP{E?_Wk$mi4<08fS5B)hsKqlIykiqF_miCF$jRNK!}IdAK|4(mKFz^= zQkS+@3(ay8$Ss~E)vPAYY_DJQoUm!adsg93V+z3hK zywYWeG`(TSOrcba`j%marv;+dNs~Tr(?)OygLg<@OC5W-vp(LUDJM*NN~xEywM`jx z_~Stb<2X(CeT>r)&J>h&D?SV}EWoXy?ZtA`8iyNl(JE<7w}5z}1AcZ~)3M5wzKxPP z%vmM5*?zZ0?lgTV2ZR)Tx^;ogccZ&>SA_fS3<5=}rLA_1b(CRE8D<_G9nxp20X4s9 z8w6$;fFC^U$*-_GPhe9?@@r=V*vw~fQCsJBtNWj0dQV1Q-p~cy<$ewM=neB%N+dUzdIP_fVPD>eA&+s}c^m>uqjzs(|g%KWDRHD#78At1nV4?ZH;OcDa^uICIFI`WoUy)+qAbOR3Z`or}Ss!AcLJ|aTY75&T}TVT^A&^EQh0gAIEpKjxkst;8V-DZoDRe;}!SU++E}=McpAfD)8fEipIX`Q=J~ZT$69Z7^CQbU5!F4BDtFllj=|2 z60kS!>D}(U8d@Hob~?X*S4m}TFz7nVeTB6jS`}kkq|4AX^2`YYEEzxIx%6k=~!wf^c5g>hTR06ri z1VOp^QX8R{S}{IgR5pF^!R1*9YU)OIQb1kz!S!5h0nq#9LjL|hZ@0IdNC50!U@$8u zMsH;I#AU955zxAuh+Q9e(=v1EQq@HH;h^mlY4)TzH`St~d4P07s%+>-n~RD-RlN9d z`gH-H^se~5BSj^4wMOzA?rzLE`7xlrnd@!xl%Qr)aMrzG6RxkG+?QlkSDq0p^|#3H zS8w`tptVc|I_Hf!lM840RM~=5~QdQEwW3H_WnPW?!TRu z$^SZ~3(+$Dzpy9%x6cAnL`W3}iyj#$#Le_?z4Fg_QOI^7Hl}|g_m3TeJQCt%`uiPZ h{ve6}r^8D5kMYmS2`TYV0sAY`_aP@ijlf^ie*mm=OVR)U delta 5425 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 I`TkM=4?CX>{Qv*} diff --git a/podaac/forge_tig_configuration/new_generate_config.py b/podaac/forge_tig_configuration/new_generate_config.py index c565545..19baa54 100644 --- a/podaac/forge_tig_configuration/new_generate_config.py +++ b/podaac/forge_tig_configuration/new_generate_config.py @@ -1,104 +1,108 @@ import pandas as pd -from pprint import pprint import click - -# Define fixed spreadsheet names -REQUIRED_SETTINGS_SHEET = "required-settings" -FORGE_PY_SHEET = "forge-py" -TIG_SHEET = "tig" - -def read_excel_sheet_as_dict(file_path, sheet_name): - """ - Reads an Excel sheet and converts the first row into a dictionary, - using headers as keys and the first row as values. Excludes empty values. - - Args: - file_path (str): Path to the Excel file. - sheet_name (str): Name of the sheet to read. - - Returns: - dict: A dictionary with header-value pairs. - """ - try: - df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl') - if df.empty: - print(f"Error: The sheet '{sheet_name}' has no data rows.") +import json +import os +import numpy as np +from jsonschema import validate +from typing import Dict, Any, Optional + +class HiTideConfigGenerator: + REQUIRED_SETTINGS_SHEET = "required-settings" + FORGE_PY_SHEET = "forge-py" + TIG_SHEET = "tig" + + @staticmethod + def _convert_value(value: Any) -> Any: + """Convert non-standard types to JSON-serializable types.""" + if pd.isna(value): + return None + if isinstance(value, (pd.Timestamp, np.datetime64)): + return str(value) + if isinstance(value, (np.bool_, bool)): + return bool(value) + if isinstance(value, (np.integer, int)): + return int(value) + if isinstance(value, (np.floating, float)): + return float(value) + return value + + @classmethod + def read_sheet_as_dict(cls, file_path: str, sheet_name: str) -> Optional[Dict[str, Any]]: + """Read Excel sheet and convert to dictionary with type-safe conversion.""" + try: + df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl') + if df.empty: + raise ValueError(f"Sheet '{sheet_name}' is empty") + + first_row = df.iloc[0] + return {k: cls._convert_value(v) for k, v in first_row.items() if pd.notna(v)} + except Exception as e: + click.echo(f"Error reading {sheet_name}: {e}", err=True) return None - # Use the header as keys and the first row as values - first_row = df.iloc[0] - return {key: value for key, value in first_row.items() if pd.notna(value)} - except Exception as e: - print(f"Error reading sheet '{sheet_name}': {e}") - return None - -def file_to_dict_list(file_path, sheet_name): - """ - Reads a sheet and converts it into a list of dictionaries, where each dictionary - represents a row with headers as keys and row values as values. Excludes empty values. - - Args: - file_path (str): Path to the file. - sheet_name (str): Name of the Excel sheet to read. - - Returns: - dict: A dictionary with the list of row dictionaries. - """ - try: - df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl') - df.dropna(how='all', inplace=True) # Drop rows with all empty values - return {'imgVariables': [ - {k: v for k, v in row.items() if pd.notna(v) and v != ""} - for row in df.to_dict(orient="records") - ]} - except Exception as e: - print(f"Error reading sheet '{sheet_name}': {e}") - return None - -def generate_configuration(file_path): - """ - Generates a configuration dictionary from the Excel file. + @classmethod + def read_sheet_as_list(cls, file_path: str, sheet_name: str) -> Optional[Dict[str, Any]]: + """Read sheet as list of non-empty dictionaries.""" + try: + df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl') + df.dropna(how='all', inplace=True) + return {'imgVariables': [ + {k: cls._convert_value(v) for k, v in row.items() if pd.notna(v) and v != ""} + for row in df.to_dict(orient="records") + ]} + except Exception as e: + click.echo(f"Error reading {sheet_name}: {e}", err=True) + return None - Args: - file_path (str): Path to the Excel file. + @classmethod + def generate_configuration(cls, file_path: str) -> Optional[Dict[str, Any]]: + """Generate configuration from Excel file.""" + # Load required data + required_settings = cls.read_sheet_as_dict(file_path, cls.REQUIRED_SETTINGS_SHEET) + forge_py = cls.read_sheet_as_dict(file_path, cls.FORGE_PY_SHEET) + tig_data = cls.read_sheet_as_list(file_path, cls.TIG_SHEET) - Returns: - dict: The generated configuration. - """ - # Read required sheets - required_settings = read_excel_sheet_as_dict(file_path, REQUIRED_SETTINGS_SHEET) - forge_py = read_excel_sheet_as_dict(file_path, FORGE_PY_SHEET) - tig_data = file_to_dict_list(file_path, TIG_SHEET) + if not all([required_settings, forge_py, tig_data]): + return None - if not required_settings or not forge_py: - print("Error: Missing required settings or forge-py data.") - return None + # Construct strategy configuration + strategy = forge_py.get("strategy") + strategy_args = {k: v for k, v in forge_py.items() if k != "strategy"} + strategy_dict = {"footprint": {"strategy": strategy, strategy: strategy_args}} - # Build strategy dictionary - strategy = forge_py.get("strategy") - filtered_args = {k: v for k, v in forge_py.items() if k != "strategy"} - strategy_dict = {"footprint": {"strategy": strategy, strategy: filtered_args}} + # Default settings + defaults = { + 'tiles': {'steps': [30, 14]}, + 'image': {'ppd': 8, 'res': 8} + } - # Merge everything - merged_config = required_settings | strategy_dict | tig_data - return merged_config + # Merge configurations + return {**required_settings, **strategy_dict, **tig_data, **defaults} + @staticmethod + def load_schema() -> Dict[str, Any]: + """Load JSON schema for validation.""" + schema_path = os.path.join(os.path.dirname(__file__), "schema.json") + with open(schema_path, "r") as file: + return json.load(file) @click.command() -@click.option('-g', '--granule', help='Sample granule file', required=True) -def generate_hitide_config_command(granule): - """Command call to generate config""" +@click.option('-f', '--file', help='Excel file with configuration settings', required=True) +def generate_hitide_config(file: str): + """Generate and validate HiTide configuration.""" + generator = HiTideConfigGenerator() + config = generator.generate_configuration(file) + + if not config: + click.echo("Configuration generation failed.", err=True) + return - generate_hitide_config(granule, dataset_id, include_image_variables, longitude, latitude, time, footprint_strategy) + try: + schema = generator.load_schema() + validate(instance=config, schema=schema) + click.echo(json.dumps(config, indent=2)) + except Exception as e: + click.echo(f"Validation error: {e}", err=True) if __name__ == '__main__': - #generate_hitide_config_command() # pylint: disable=no-value-for-parameter - - - # Example usage - file_path = "/Users/simonl/Desktop/work/podaac/forge-tig-configuration/podaac/forge_tig_configuration/example_config.xlsx" - config = generate_configuration(file_path) - - - if config: - pprint(config) + generate_hitide_config() \ No newline at end of file diff --git a/podaac/forge_tig_configuration/schema.json b/podaac/forge_tig_configuration/schema.json new file mode 100644 index 0000000..0e838d1 --- /dev/null +++ b/podaac/forge_tig_configuration/schema.json @@ -0,0 +1,290 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "shortName": { + "type": "string", + "description": "Short name of the dataset, required." + }, + "latVar": { + "type": "string", + "description": "Path to the latitude variable in the data, required." + }, + "lonVar": { + "type": "string", + "description": "Path to the longitude variable in the data, required." + }, + "timeVar": { + "type": "string", + "description": "Name of the time variable, optional." + }, + "is360": { + "type": "boolean", + "description": "Indicates if longitude is in 0-360 format, required." + }, + "tiles": { + "type": "object", + "properties": { + "steps": { + "type": "array", + "items": { + "type": "integer", + "additionalProperties": false, + "description": "Step size for each tile, represented as integers." + }, + "minItems": 2, + "maxItems": 3, + "additionalProperties": false, + "description": "An array of two integers representing the tile step sizes." + } + }, + "required": ["steps"], + "additionalProperties": false, + "description": "Tile configuration with step sizes for the grid." + }, + "global_grid": { + "type": "boolean", + "description": "Indicates whether the grid is global or not. Optional." + }, + "footprinter": { + "type": "string", + "enum": ["forge-py"], + "description": "The footprinter to use, must be 'forge-py'." + }, + "tolerance": { + "type": "number", + "description": "Tolerance value (optional). Can be a float or integer." + }, + "footprint": { + "type": "object", + "properties": { + "findValid": { + "type": "boolean", + "description": "Indicates whether to find valid footprint values." + }, + "b": { + "type": "string", + "description": "The 'b' footprint pattern." + }, + "s2": { + "type": "string", + "description": "The 's2' footprint pattern." + }, + "t": { + "type": "string", + "description": "The 't' footprint pattern." + }, + "s1": { + "type": "string", + "description": "The 's1' footprint pattern." + }, + "removeOrigin": { + "type": "boolean", + "description": "Unknown usage for forge" + }, + "strategy": { + "type": "string", + "enum": ["open_cv", "alpha_shape", "linestring", "periodic", "swot_linestring", "smap", "polarsides", "polar"], + "description": "Footprint calculation strategy, required." + }, + "open_cv": { + "type": "object", + "properties": { + "pixel_height": { + "type": "integer", + "minimum": 1, + "description": "Height of the pixel grid, optional." + }, + "simplify": { + "type": "number", + "minimum": 0, + "description": "Simplification tolerance for a polygon, optional." + }, + "min_area": { + "type": "integer", + "minimum": 0, + "description": "Minimum area for a polygon or it will be removed, optional." + }, + "fill_kernel": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + }, + "minItems": 2, + "maxItems": 2, + "description": "Kernel size for filling small gaps, optional." + } + }, + "additionalProperties": false, + "description": "Parameters for the OpenCV strategy, optional." + }, + "alpha_shape": { + "type": "object", + "properties": { + "alpha": { + "type": "number", + "minimum": 0, + "description": "Alpha parameter for the alpha shape algorithm, optional." + }, + "thinning": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["bin_avg", "standard"], + "description": "Thinning method, optional." + }, + "value": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2 + } + ] + } + }, + "additionalProperties": false, + "description": "Parameters for thinning, optional." + }, + "cutoff_lat": { + "type": "number", + "description": "Latitude cutoff for the footprint, optional." + }, + "smooth_poles": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2, + "description": "Latitude range for smoothing near poles, optional." + }, + "simplify": { + "type": "number", + "minimum": 0, + "description": "Simplification tolerance for a polygon, optional." + }, + "min_area": { + "type": "number", + "minimum": 0, + "description": "Minimum area for a polygon or it will be removed, optional." + }, + "fill_value": { + "type": "number", + "description": "Fill value for invalid areas, optional." + } + }, + "additionalProperties": false, + "description": "Parameters for the alpha shape strategy, optional." + } + }, + "additionalProperties": false, + "description": "Footprint calculation parameters, optional." + }, + "imgVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "ID of the image variable, required." + }, + "title": { + "type": "string", + "description": "Title of the image variable, optional." + }, + "units": { + "type": "string", + "description": "Units of the image variable, optional." + }, + "fill_missing": { + "type": "boolean", + "description": "Fill in the image if the data resolution is too high and needs to be filled in spots" + }, + "fill_value": { + "type": "number", + "description": "Fill value for a variable if theres no fill value" + }, + "is_swot_expert": { + "type": "boolean", + "description": "boolean to indicate if variable is a swot expert variable for a specific algorithm" + }, + "ppd": { + "type": "integer", + "description": "Pixels per degree (ppd) for the variable." + }, + "min": { + "oneOf": [ + { + "type": "number", + "description": "Minimum value of the variable as a number (integer or float)." + }, + { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$", + "description": "Minimum value of the variable as a string. Must be a valid number." + } + ] + }, + "max": { + "oneOf": [ + { + "type": "number", + "description": "Maximum value of the variable as a number (integer or float)." + }, + { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$", + "description": "Maximum value of the variable as a string. Must be a valid number." + } + ] + }, + "palette": { + "type": "string", + "description": "Palette used for visualization, optional." + }, + "legends": { + "type": "array", + "items": { + "type": "string", + "description": "Legend file name, expected to be a string representing a file path. Note should be invalid for new tig should be removed" + }, + "description": "List of legend files." + } + }, + "required": ["id", "min", "max", "palette"], + "additionalProperties": false, + "description": "Properties of an image variable." + }, + "description": "List of image variables, optional." + }, + "image": { + "type": "object", + "properties": { + "ppd": { + "type": "integer", + "description": "Pixels per degree (ppd) for the image." + }, + "res": { + "type": "integer", + "description": "Resolution of the image (res)." + } + }, + "required": ["ppd", "res"], + "additionalProperties": false, + "description": "Image configuration with pixels per degree and resolution." + } + + }, + "required": ["shortName", "latVar", "lonVar", "is360"], + "additionalProperties": false +} diff --git a/podaac/forge_tig_configuration/~$example_config.xlsx b/podaac/forge_tig_configuration/~$example_config.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5a932052db2a5d1e1d32a453f59be330b8becc3b GIT binary patch literal 165 zcmWgj%}g%JFV0UZQSeVo%S=vH2rW)6QXm9G8GIQs8Il=_81fm4fjEt!gh7G9A4sQx R#Z!U2P@qgIP=x};5CA3W7%cz* literal 0 HcmV?d00001 diff --git a/poetry.lock b/poetry.lock index b53e65f..29d94ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -19,6 +19,20 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -30,6 +44,17 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "et-xmlfile" +version = "2.0.0" +description = "An implementation of lxml.xmlfile for the standard library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"}, + {file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -154,6 +179,20 @@ files = [ {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, ] +[[package]] +name = "openpyxl" +version = "3.1.5" +description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, +] + +[package.dependencies] +et-xmlfile = "*" + [[package]] name = "packaging" version = "24.2" @@ -268,13 +307,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -330,101 +369,112 @@ rpds-py = ">=0.7.0" [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.22.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, - {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, - {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, - {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, - {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, - {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, - {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, - {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, - {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, - {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, - {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, - {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, + {file = "rpds_py-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a4366f264fa60d3c109f0b27af0cd9eb8d46746bd70bd3d9d425f035b6c7e286"}, + {file = "rpds_py-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e34a3e665d38d0749072e6565400c8ce9abae976e338919a0dfbfb0e1ba43068"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38cacf1f378571450576f2c8ce87da6f3fddc59d744de5c12b37acc23285b1e1"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8cbb040fec8eddd5a6a75e737fd73c9ce37e51f94bacdd0b178d0174a4758395"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d80fd710b3307a3c63809048b72c536689b9b0b31a2518339c3f1a4d29c73d7a"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b5d17d8f5b885ce50e0cda85f99c0719e365e98b587338535fa566a48375afb"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7a048ec1ebc991331d709be4884dc318c9eaafa66dcde8be0933ac0e702149"}, + {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:306da3dfa174b489a3fc63b0872e2226a5ddf94c59875a770d72aff945d5ed96"}, + {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7b4450093c0c909299770226fb0285be47b0a57545bae25b5c4e51566b0e587"}, + {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0903ffdb5b9007e503203b6285e4ff0faf96d875c19f1d103b475acf7d9f7311"}, + {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1522025cda9e57329aade769f56e5793b2a5da7759a21914ee10e67e17e601e"}, + {file = "rpds_py-0.22.0-cp310-cp310-win32.whl", hash = "sha256:49e084d47a66027ac72844f9f52f13d347a9a1f05d4f84381b420e47f836a7fd"}, + {file = "rpds_py-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:d9ceca96df54cb1675a0b7f52f1c6d5d1df62c5b40741ba211780f1b05a282a2"}, + {file = "rpds_py-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:771c9a3851beaa617d8c8115d65f834a2b52490f42ee2b88b13f1fc5529e9e0c"}, + {file = "rpds_py-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341a07a4b55126bfae68c9bf24220a73d456111e5eb3dcbdab9fd16de2341224"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7649c8b8e4bd1ccc5fcbd51a855d57a617deeba19c66e3d04b1abecc61036b2"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f513758e7cda8bc262e80299a8e3395d7ef7f4ae705be62632f229bc6c33208"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba1fc34d0b2f6fd53377a4c954116251eba6d076bf64f903311f4a7d27d10acd"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632d2fdddd9fbe3ac8896a119fd18a71fc95ca9c4cbe5223096c142d8c4a2b1d"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:326e42f2b49462e05f8527a1311ce98f9f97c484b3e443ec0ea4638bed3aebcf"}, + {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9bbdba9e75b1a9ee1dd1335034dad998ef1acc08492226c6fd50aa773bdfa7d"}, + {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:41f65a97bf2c4b161c9f8f89bc37058346bec9b36e373c8ad00a16c957bff625"}, + {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0686f2c16eafdc2c6b4ce6e86e5b3092e87db09ae64be2787616444eb35b9756"}, + {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e7c9aa2353eb0b0d845323857197daa036c2ff8624df990b0d886d22a8f665e"}, + {file = "rpds_py-0.22.0-cp311-cp311-win32.whl", hash = "sha256:2d2fc3ab021be3e0b5aec6d4164f2689d231b8bfc5185cc454314746aa4aee72"}, + {file = "rpds_py-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:87453d491369cd8018016d2714a13e8461975161703c18ee31eecf087a8ae5d4"}, + {file = "rpds_py-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e9d4293b21c69ee4f9e1a99ac4f772951d345611c614a0cfae2ec6b565279bc9"}, + {file = "rpds_py-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:67e013a17a3db4d98cc228fd5aeb36a51b0f5cf7330b9102a552060f1fe4e560"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b639a19e1791b646d27f15d17530a51722cc728d43b2dff3aeb904f92d91bac"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1357c3092702078b7782b6ebd5ba9b22c1a291c34fbf9d8f1a48237466ac7758"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:842855bbb113a19c393c6de5aa6ed9a26c6b13c2fead5e49114d39f0d08b94d8"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ae7927cd2b869ca4dc645169d8af5494a29c99afd0ea0f24dd00c811ab1d8b8"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91bfef5daa2a5a4fe62f8d317fc91a626073639f951f851bd2cb252d01bc6c5"}, + {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fc4824e38c1e91a73bc820e7caacaf19d0acd557465aceef0420ca59489b390"}, + {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:92d28a608127b357da47c99e0d0e0655ca2060286540fe9f2a25a2e8ac666e05"}, + {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c637188b930175c256f13adbfc427b83ec7e64476d1ec9d6608f312bb84e06c3"}, + {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93bbd66f46dddc41e8c656130c97c0fb515e0fa44e1eebb2592769dbbd41b2f5"}, + {file = "rpds_py-0.22.0-cp312-cp312-win32.whl", hash = "sha256:54d8f94dec5765a9edc19610fecf0fdf9cab36cbb9def1213188215f735a6f98"}, + {file = "rpds_py-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:931bf3d0705b2834fed29354f35170fa022fe22a95542b61b7c66aca5f8a224f"}, + {file = "rpds_py-0.22.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2a57300cc8b034c5707085249efd09f19116bb80278d0ec925d7f3710165c510"}, + {file = "rpds_py-0.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c398a5a8e258dfdc5ea2aa4e5aa2ca3207f654a8eb268693dd1a76939074a588"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a6cc4eb1e86364331928acafb2bb41d8ab735ca3caf2d6019b9f6dac3f4f65d"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:574c5c94213bc9990805bfd7e4ba3826d3c098516cbc19f0d0ef0433ad93fa06"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c0321bc03a1c513eca1837e3bba948b975bcf3a172aebc197ab3573207f137a"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d276280649305c1da6cdd84585d48ae1f0efa67434d8b10d2df95228e59a05bb"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c17b43fe9c6da16885e3fe28922bcd1a029e61631fb771c7d501019b40bcc904"}, + {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48c95997af9314f4034fe5ba2d837399e786586e220835a578d28fe8161e6ae5"}, + {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9aa4af6b879bb75a3c7766fbf49d77f4097dd12b548ecbbd8b3f85caa833281"}, + {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8426f97117b914b9bfb2a7bd46edc148e8defda728a55a5df3a564abe70cd7a4"}, + {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:034964ea0ea09645bdde13038b38abb14be0aa747f20fcfab6181207dd9e0483"}, + {file = "rpds_py-0.22.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:3dc7c64b56b82428894f056e9ff6e8ee917ff74fc26b65211a33602c2372e928"}, + {file = "rpds_py-0.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1212cb231f2002934cd8d71a0d718fdd9d9a2dd671e0feef8501038df3508026"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f21e1278c9456cd601832375c778ca44614d3433996488221a56572c223f04a"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:875fe8dffb43c20f68379ee098b035a7038d7903c795d46715f66575a7050b19"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e23dcdd4b2ff9c6b3317ea7921b210d39592f8ca1cdea58ada25b202c65c0a69"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fb8efc9e579acf1e556fd86277fecec320c21ca9b5d39db96433ad8c45bc4a"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe23687924b25a2dee52fab15976fd6577ed8518072bcda9ff2e2b88ab1f168b"}, + {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5469b347445d1c31105f33e7bfc9a8ba213d48e42641a610dda65bf9e3c83f5"}, + {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a810a57ce5e8ecf8eac6ec4dab534ff80c34e5a2c31db60e992009cd20f58e0f"}, + {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d9bb9242b38a664f307b3b897f093896f7ed51ef4fe25a0502e5a368de9151ea"}, + {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b4660943030406aaa40ec9f51960dd88049903d9536bc3c8ebb5cc4e1f119bbe"}, + {file = "rpds_py-0.22.0-cp313-cp313t-win32.whl", hash = "sha256:208ce1d8e3af138d1d9b21d7206356b7f29b96675e0113aea652cf024e4ddfdc"}, + {file = "rpds_py-0.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e6da2e0500742e0f157f005924a0589f2e2dcbfdd6cd0cc0abce367433e989be"}, + {file = "rpds_py-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f980a0640599a74f27fd9d50c84c293f1cb7afc2046c5c6d3efaf8ec7cdbc326"}, + {file = "rpds_py-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca505fd3767a09a139737f3278bc8a485cb64043062da89bcba27e2f2ea78d33"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba235e00e0878ba1080b0f2a761f143b2a2d1c354f3d8e507fbf2f3de401bf18"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81e7a27365b02fe70a77f1365376879917235b3fec551d19b4c91b51d0bc1d07"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32a0e24cab2daae0503b06666d516e90a080c1a95aff0406b9f03c6489177c4b"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a73ed43d64209e853bba567a543170267a5cd64f359540b0ca2d597e329ba172"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0abcce5e874474d3eab5ad53be03dae2abe651d248bdeaabe83708e82969e78"}, + {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4e9946c8c7def17e4fcb5eddb14c4eb6ebc7f6f309075e6c8d23b133c104607"}, + {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:758098b38c344d9a7f279baf0689261777e601f620078ef5afdc9bd3339965c3"}, + {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9ad4640a409bc2b7d22b7921e7660f0db96c5c8c69fbb2e8f3261d4f71d33983"}, + {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8c48fc7458fe3a74dcdf56ba3534ff41bd421f69436df09ff3497fdaac18b431"}, + {file = "rpds_py-0.22.0-cp39-cp39-win32.whl", hash = "sha256:fde778947304e55fc732bc8ea5c6063e74244ac1808471cb498983a210aaf62c"}, + {file = "rpds_py-0.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:5fdf91a7c07f40e47b193f2acae0ed9da35d09325d7c3c3279f722b7cbf3d264"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c8fd7a16f7a047e06c747cfcf2acef3ac316132df1c6077445b29ee6f3f3a70b"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b6e4bcfc32f831bfe3d6d8a5acedfbfd5e252a03c83fa24813b277a3a8a13ca"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eadd2417e83a77ce3ae4a0efd08cb0ebdfd317b6406d11020354a53ad458ec84"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9dc2113e0cf0dd637751ca736186fca63664939ceb9f9f67e93ade88c69c0c9"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2c00acdf68f1f69a476b770af311a7dc3955b7de228b04a40bcc51ac4d743b"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfdabdf8519c93908b2bf0f87c3f86f9e88bab279fb4acfd0907519ca5a1739f"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8338db3c76833d02dc21c3e2c42534091341d26e4f7ba32c6032bb558a02e07b"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8ad4dfda52e64af3202ceb2143a62deba97894b71c64a4405ee80f6b3ea77285"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b94b074dcce39976db22ea75c7aea8b22d95e6d3b62f76e20e1179a278521d8"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d4f2af3107fe4dc40c0d1a2409863f5249c6796398a1d83c1d99a0b3fa6cfb8d"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:bb11809b0de643a292a82f728c494a2bbef0e30a7c42d37464abbd6bef7ca7b1"}, + {file = "rpds_py-0.22.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c1c21030ed494deb10226f90e2dbd84a012d59810c409832714a3dd576527be2"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:64a0c965a1e299c9b280006bdb15c276c427c45360aed676305dc36bcaa4d13c"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:2498ff422823be087b48bc82710deb87ac34f6b7c8034ee39920647647de1e60"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59e63da174ff287db05ef7c21d75974a5bac727ed60452aeb3a14278477842a8"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1c04fb380bc8efaae2fdf17ed6cd5d223da78a8b0b18a610f53d4c5d6e31dfd"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04919ffa9a728c446b27b6b625fa1d00ece221bdb9d633e978a7e0353a12c0e"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24c28df05bd284879d0fac850ba697077d2a33b7ebcaea6318d6b6cdfdc86ddc"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33622dc63c295788eed09dbb1d11bed178909d3267b02d873116ee6be368244"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7539dbb8f705e13629ba6f23388976aad809e387f32a6e5c0712e4e8d9bfcce7"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:b8906f537978da3f7f0bd1ba37b69f6a877bb43312023b086582707d2835bf2f"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:62ab12fe03ffc49978d29de9c31bbb216610157f7e5ca8e172fed6642aead3be"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:762206ba3bf1d6c8c9e0055871d3c0d5b074b7c3120193e6c067e7866f106ab1"}, + {file = "rpds_py-0.22.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed0102146574e5e9f079b2e1a06e6b5b12a691f9c74a65b93b7f3d4feda566c6"}, + {file = "rpds_py-0.22.0.tar.gz", hash = "sha256:32de71c393f126d8203e9815557c7ff4d72ed1ad3aa3f52f6c7938413176750a"}, ] [[package]] @@ -440,13 +490,43 @@ files = [ [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -463,4 +543,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "f71bd846ac600581516d4b6558a128bd48d878b39b0fa5cedac6e3dbf9373bf9" +content-hash = "bdf26488bb6097daf4204491f9bf21505eb58498dd7bd5ca7d594dee13088e9d" diff --git a/pyproject.toml b/pyproject.toml index 444db35..4e9d66c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,9 @@ python = "^3.10" pytest = "^8.3.3" jsonschema = "^4.23.0" pandas = "^2.2.3" - +openpyxl = "^3.1.5" +click = "^8.1" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/test_configs.py b/tests/test_configs.py index 6dc9e10..9564a00 100644 --- a/tests/test_configs.py +++ b/tests/test_configs.py @@ -1,30 +1,27 @@ import os import json import pytest +from functools import lru_cache from jsonschema import validate, ValidationError +from podaac.forge_tig_configuration import new_generate_config -# Directory containing the configuration files -CONFIG_DIR = 'config-files' +@lru_cache(maxsize=1) +def get_schema(): + """Cache schema loading for performance.""" + return new_generate_config.HiTideConfigGenerator.load_schema() -# Load the schema once -with open("schema.json", "r") as schema_file: - SCHEMA = json.load(schema_file) +def validate_config(config_file): + """Validate single configuration file.""" + with open(config_file, "r") as f: + config = json.load(f) + validate(instance=config, schema=get_schema()) -# Parameterize the test with all `.cfg` files in CONFIG_DIR @pytest.mark.parametrize("config_file", [ - os.path.join(CONFIG_DIR, f) for f in os.listdir(CONFIG_DIR) if f.endswith('.cfg') + os.path.join('config-files', f) for f in os.listdir('config-files') if f.endswith('.cfg') ]) def test_json_schema(config_file): - """Test JSON configuration files against the schema.""" - # Load the JSON config + """Validate JSON configurations against schema.""" try: - with open(config_file, "r") as f: - config = json.load(f) - except json.JSONDecodeError as e: - pytest.fail(f"Failed to load JSON from {config_file}: {e}") - - # Validate the JSON against the schema - try: - validate(instance=config, schema=SCHEMA) - except ValidationError as e: + validate_config(config_file) + except (json.JSONDecodeError, ValidationError) as e: pytest.fail(f"Validation failed for {config_file}: {e}") \ No newline at end of file From 8962d64944b26e3fab383ca193946d3214f4ad6f Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 11:36:05 -0800 Subject: [PATCH 08/25] more testing --- .github/workflows/test_configs.yml | 2 +- example_config.xlsx | Bin 0 -> 12015 bytes .../example_config.xlsx | Bin 11981 -> 0 bytes .../generate_hitide_config.py | 175 ------------------ .../new_generate_config.py | 92 ++++++--- .../~$example_config.xlsx | Bin 165 -> 0 bytes pyproject.toml | 2 +- 7 files changed, 68 insertions(+), 203 deletions(-) create mode 100644 example_config.xlsx delete mode 100644 podaac/forge_tig_configuration/example_config.xlsx delete mode 100644 podaac/forge_tig_configuration/generate_hitide_config.py delete mode 100644 podaac/forge_tig_configuration/~$example_config.xlsx diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 46b98b6..1f30ebf 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.x' + python-version: '3.10' - name: Install dependencies run: | diff --git a/example_config.xlsx b/example_config.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6b2953667e7672121c2eac71a840fabe40c95fdd GIT binary patch literal 12015 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 V3NT<#0|1c!_<)_Yg7J@U{|BH34!-~Z diff --git a/podaac/forge_tig_configuration/generate_hitide_config.py b/podaac/forge_tig_configuration/generate_hitide_config.py deleted file mode 100644 index db7577b..0000000 --- a/podaac/forge_tig_configuration/generate_hitide_config.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Python script used to generate hitide config for forge and tig""" - -import json -import csv -import click -import netCDF4 as nc - -def get_variables_with_paths(group, current_path=""): - """Function to recursively get variables with group paths""" - - variables_with_paths = [] - for var_name, var_obj in group.variables.items(): # pylint: disable=unused-variable - variable_path = current_path + "/" + var_name if current_path else var_name - variables_with_paths.append(variable_path) - for subgroup_name, subgroup in group.groups.items(): - subgroup_path = current_path + "/" + subgroup_name if current_path else subgroup_name - subgroup_variables = get_variables_with_paths(subgroup, subgroup_path) - variables_with_paths.extend(subgroup_variables) - return variables_with_paths - - -def read_min_max_csv(filename): - """ Function to read csv with variables, min, max values """ - - data = {} - with open(filename, newline='') as csvfile: - reader = csv.reader(csvfile) - headers = next(reader) # Read the column headers - for row in reader: - key = row[0].replace("'", "").replace(",", "") # Use the first column as the key - values = {} - for i in range(1, len(headers)): - values[headers[i]] = row[i] - data[key] = values - return data - - -def generate_hitide_config(granule, dataset_id, include_image_variables, longitude, latitude, time, footprint_strategy): # pylint: disable=too-many-branches - """Function to generate hitide configuration""" - - dataset_config = { - 'shortName': dataset_id, - 'latVar': latitude, - 'lonVar': longitude, - 'timeVar': time, - 'is360': True, - 'tiles': {'steps': [30, 14]}, - 'footprint': {}, - 'imgVariables': [], - 'image': {'ppd': 16, 'res': 8}, - 'variables': [] - } - - if footprint_strategy: - if footprint_strategy == "periodic": - dataset_config['footprint'] = { - 'strategy': footprint_strategy, - 't': '0:0,0:*', - 's1': '0:*,0:0', - 'b': '*:*,0:*', - 's2': '0:*,*:*' - } - elif footprint_strategy == "smap": - dataset_config['footprint'] = { - 'strategy': footprint_strategy, - "t": "0:0,0:*,0:0", - "s1": "0:*,0:0,0:0", - "b": "*:*,0:*,0:0", - "s2": "0:*,*:*,0:0" - } - elif footprint_strategy == "linestring": - dataset_config['footprint'] = { - 'strategy': footprint_strategy, - "t": "0:*", - "s1": "0:*", - "b": "0:*", - "s2": "0:*" - } - else: - dataset_config['footprint'] = { - 'strategy': footprint_strategy, - 't': '0:0,0:*', - 's1': '0:*,0:0', - 'b': '*:*,0:*', - 's2': '0:*,*:*' - } - - vars_data = {} - if include_image_variables: - vars_data = read_min_max_csv(include_image_variables) - - with nc.Dataset(granule, 'r') as dataset: # pylint: disable=no-member - - data_var_names = get_variables_with_paths(dataset) - dataset_config['variables'] = data_var_names - - try: - for data_var in data_var_names: - if vars_data and data_var in vars_data: - variable = dataset[data_var] - - units = variable.units if 'units' in variable.ncattrs() else '' - long_name = variable.long_name if 'long_name' in variable.ncattrs() else '' - - palette = 'paletteMedspirationIndexed' - fill_missing = False - ppd = 16 - - if data_var in vars_data: - min_val = float(vars_data[data_var]['min']) - max_val = float(vars_data[data_var]['max']) - palette = vars_data[data_var].get('palette') - fill_missing = vars_data[data_var].get('fill_missing', False) - ppd = vars_data[data_var].get('ppd', 16) - else: - min_val = variable.valid_min if 'valid_min' in variable.ncattrs() else '' - max_val = variable.valid_max if 'valid_max' in variable.ncattrs() else '' - - if not palette: - palette = 'paletteMedspirationIndexed' - - dataset_dict = { - 'id': data_var, - 'title': long_name, - 'units': units, - 'min': min_val, - 'max': max_val, - 'palette': palette - } - - if fill_missing: - fill_missing = fill_missing.lower().strip() - dataset_dict['fill_missing'] = fill_missing == "true" - - if ppd != 16 and ppd.isdigit(): - dataset_dict['ppd'] = int(ppd) - - dataset_config['imgVariables'].append(dataset_dict) - - except Exception as ex: # pylint: disable=broad-exception-caught - print(f"Error: Failed on variable {data_var}, exception: " + str(ex)) - - print(json.dumps(dataset_config, indent=4)) - - # Specify the file path where you want to save the JSON data - file_path = f"{dataset_id}.cfg" - - # Open the file in write mode and write the JSON data to it - with open(file_path, "w") as json_file: - json.dump(dataset_config, json_file, indent=4) - - return dataset_config - - -@click.command() -@click.option('-g', '--granule', help='Sample granule file', required=True) -@click.option('-d', '--dataset-id', help='Collection short name', required=True) -@click.option('-i', '--include-image-variables', help='CSV file with variables, min, max settings', required=False) -@click.option('--longitude', required=False, help='longitude variable', default="longitude") -@click.option('--latitude', required=False, help='latitude variable', default="latitude") -@click.option('--time', required=False, help='time variable', default="time") -@click.option('--footprint-strategy', help='forge footprint strategy', required=False) -def generate_hitide_config_command(granule, dataset_id, include_image_variables, longitude, latitude, time, footprint_strategy): - """Command call to generate config""" - - generate_hitide_config(granule, dataset_id, include_image_variables, longitude, latitude, time, footprint_strategy) - - -if __name__ == '__main__': - generate_hitide_config_command() # pylint: disable=no-value-for-parameter - -# Example runs: -# python gen_dataset_config.py -g SWOT_L2_LR_SSH_Basic_001_001_20160901T000000_20160901T005126_DG10_01.nc -d SWOT_L2_LR_SSH_BASIC_1.0 -i SWOT_L2_LR_SSH_BASIC_1.0.csv -# python gen_dataset_config.py -g SWOT_L2_LR_SSH_WindWave_001_001_20160901T000000_20160901T005126_DG10_01.nc -d SWOT_L2_LR_SSH_WINDWAVE_1.0 -i SWOT_L2_LR_SSH_WINDWAVE_1.0.csv -# python gen_dataset_config.py -g SWOT_L2_LR_SSH_Expert_001_001_20160901T000000_20160901T005126_DG10_01.nc -d SWOT_L2_LR_SSH_EXPERT_1.0 -i SWOT_L2_LR_SSH_EXPERT_1.0.csv diff --git a/podaac/forge_tig_configuration/new_generate_config.py b/podaac/forge_tig_configuration/new_generate_config.py index 19baa54..2f3ac59 100644 --- a/podaac/forge_tig_configuration/new_generate_config.py +++ b/podaac/forge_tig_configuration/new_generate_config.py @@ -3,8 +3,9 @@ import json import os import numpy as np -from jsonschema import validate +from jsonschema import validate, Draft7Validator from typing import Dict, Any, Optional +import ast class HiTideConfigGenerator: REQUIRED_SETTINGS_SHEET = "required-settings" @@ -26,6 +27,70 @@ def _convert_value(value: Any) -> Any: return float(value) return value + @classmethod + def filter_config_to_schema(cls, config: Dict[str, Any], schema: Dict[str, Any]) -> Dict[str, Any]: + """Filter configuration to match only schema-defined fields.""" + def recursive_filter(current_config, current_schema): + if current_schema.get('type') == 'object': + filtered_obj = {} + for key, value in current_config.items(): + prop_schema = current_schema.get('properties', {}).get(key, {}) + if prop_schema: + if prop_schema.get('type') == 'object' and isinstance(value, dict): + filtered_value = recursive_filter(value, prop_schema) + if filtered_value: + filtered_obj[key] = filtered_value + elif prop_schema.get('type') == 'array' and isinstance(value, list): + filtered_obj[key] = [ + recursive_filter(item, prop_schema.get('items', {})) + for item in value if item + ] + else: + filtered_obj[key] = value + return filtered_obj + return current_config + + return recursive_filter(config, schema) + + @classmethod + def generate_configuration(cls, file_path: str) -> Optional[Dict[str, Any]]: + """Generate configuration from Excel file.""" + # Load required data + required_settings = cls.read_sheet_as_dict(file_path, cls.REQUIRED_SETTINGS_SHEET) + forge_py = cls.read_sheet_as_dict(file_path, cls.FORGE_PY_SHEET) + tig_data = cls.read_sheet_as_list(file_path, cls.TIG_SHEET) + + if not all([required_settings, forge_py, tig_data]): + return None + + # Construct strategy configuration + strategy = forge_py.pop("strategy", None) + thinning_method = forge_py.pop("thinning_method", None) + thinning_value = forge_py.pop("thinning_value", None) + + if thinning_method and thinning_value: + try: + thinning_value = ast.literal_eval(thinning_value) + except (ValueError, SyntaxError): + pass # Leave thinning_value as is if parsing fails + forge_py["thinning"] = {"method": thinning_method, "value": thinning_value} + + strategy_dict = {"footprint": {"strategy": strategy, strategy: forge_py}} + + # Default settings + defaults = { + 'tiles': {'steps': [30, 14]}, + 'image': {'ppd': 8, 'res': 8}, + 'footprinter': 'forge-py' + } + + # Merge configurations + config = {**required_settings, **strategy_dict, **tig_data, **defaults} + + # Load and apply schema filtering + schema = cls.load_schema() + return cls.filter_config_to_schema(config, schema) + @classmethod def read_sheet_as_dict(cls, file_path: str, sheet_name: str) -> Optional[Dict[str, Any]]: """Read Excel sheet and convert to dictionary with type-safe conversion.""" @@ -54,31 +119,6 @@ def read_sheet_as_list(cls, file_path: str, sheet_name: str) -> Optional[Dict[st click.echo(f"Error reading {sheet_name}: {e}", err=True) return None - @classmethod - def generate_configuration(cls, file_path: str) -> Optional[Dict[str, Any]]: - """Generate configuration from Excel file.""" - # Load required data - required_settings = cls.read_sheet_as_dict(file_path, cls.REQUIRED_SETTINGS_SHEET) - forge_py = cls.read_sheet_as_dict(file_path, cls.FORGE_PY_SHEET) - tig_data = cls.read_sheet_as_list(file_path, cls.TIG_SHEET) - - if not all([required_settings, forge_py, tig_data]): - return None - - # Construct strategy configuration - strategy = forge_py.get("strategy") - strategy_args = {k: v for k, v in forge_py.items() if k != "strategy"} - strategy_dict = {"footprint": {"strategy": strategy, strategy: strategy_args}} - - # Default settings - defaults = { - 'tiles': {'steps': [30, 14]}, - 'image': {'ppd': 8, 'res': 8} - } - - # Merge configurations - return {**required_settings, **strategy_dict, **tig_data, **defaults} - @staticmethod def load_schema() -> Dict[str, Any]: """Load JSON schema for validation.""" diff --git a/podaac/forge_tig_configuration/~$example_config.xlsx b/podaac/forge_tig_configuration/~$example_config.xlsx deleted file mode 100644 index 5a932052db2a5d1e1d32a453f59be330b8becc3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWgj%}g%JFV0UZQSeVo%S=vH2rW)6QXm9G8GIQs8Il=_81fm4fjEt!gh7G9A4sQx R#Z!U2P@qgIP=x};5CA3W7%cz* diff --git a/pyproject.toml b/pyproject.toml index 4e9d66c..bc0908f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "forge-tig-configuration" +name = "podaac-forge-tig-config-generator" version = "0.1.0" description = "" authors = ["Simon Liu "] From 4e735d79ffa50d64c1d247cda983fd2371f6c1bf Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 11:39:28 -0800 Subject: [PATCH 09/25] update github action test --- .github/workflows/test_configs.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 1f30ebf..6142e7d 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -16,11 +16,14 @@ jobs: with: python-version: '3.10' - - name: Install dependencies + - name: Install Poetry + uses: abatilo/actions-poetry@v3 + with: + poetry-version: 1.3.2 + + - name: Install Software run: | - python -m pip install --upgrade pip - pip install pytest - pip install jsonschema + poetry build - name: Run pytest - run: pytest + run: poetry run pytest From d665aac8f99f5587cc2d64d1d8639fe65d334a12 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 12:02:20 -0800 Subject: [PATCH 10/25] test --- .github/workflows/deploy.yml | 15 ++- poetry.lock | 206 ++++++++++++++++++----------------- 2 files changed, 113 insertions(+), 108 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 966ed0d..4291181 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,14 +22,17 @@ jobs: with: python-version: '3.10' - - name: Install dependencies + - name: Install Poetry + uses: abatilo/actions-poetry@v3 + with: + poetry-version: 1.3.2 + + - name: Install Software run: | - python -m pip install --upgrade pip - pip install pytest - pip install jsonschema - + poetry build + - name: Run pytest - run: pytest + run: poetry run pytest # First job in the workflow installs and verifies the software deploy_sit: diff --git a/poetry.lock b/poetry.lock index 29d94ec..9de640f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -369,112 +369,114 @@ rpds-py = ">=0.7.0" [[package]] name = "rpds-py" -version = "0.22.0" +version = "0.22.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a4366f264fa60d3c109f0b27af0cd9eb8d46746bd70bd3d9d425f035b6c7e286"}, - {file = "rpds_py-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e34a3e665d38d0749072e6565400c8ce9abae976e338919a0dfbfb0e1ba43068"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38cacf1f378571450576f2c8ce87da6f3fddc59d744de5c12b37acc23285b1e1"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8cbb040fec8eddd5a6a75e737fd73c9ce37e51f94bacdd0b178d0174a4758395"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d80fd710b3307a3c63809048b72c536689b9b0b31a2518339c3f1a4d29c73d7a"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b5d17d8f5b885ce50e0cda85f99c0719e365e98b587338535fa566a48375afb"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7a048ec1ebc991331d709be4884dc318c9eaafa66dcde8be0933ac0e702149"}, - {file = "rpds_py-0.22.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:306da3dfa174b489a3fc63b0872e2226a5ddf94c59875a770d72aff945d5ed96"}, - {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7b4450093c0c909299770226fb0285be47b0a57545bae25b5c4e51566b0e587"}, - {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0903ffdb5b9007e503203b6285e4ff0faf96d875c19f1d103b475acf7d9f7311"}, - {file = "rpds_py-0.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1522025cda9e57329aade769f56e5793b2a5da7759a21914ee10e67e17e601e"}, - {file = "rpds_py-0.22.0-cp310-cp310-win32.whl", hash = "sha256:49e084d47a66027ac72844f9f52f13d347a9a1f05d4f84381b420e47f836a7fd"}, - {file = "rpds_py-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:d9ceca96df54cb1675a0b7f52f1c6d5d1df62c5b40741ba211780f1b05a282a2"}, - {file = "rpds_py-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:771c9a3851beaa617d8c8115d65f834a2b52490f42ee2b88b13f1fc5529e9e0c"}, - {file = "rpds_py-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341a07a4b55126bfae68c9bf24220a73d456111e5eb3dcbdab9fd16de2341224"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7649c8b8e4bd1ccc5fcbd51a855d57a617deeba19c66e3d04b1abecc61036b2"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f513758e7cda8bc262e80299a8e3395d7ef7f4ae705be62632f229bc6c33208"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba1fc34d0b2f6fd53377a4c954116251eba6d076bf64f903311f4a7d27d10acd"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632d2fdddd9fbe3ac8896a119fd18a71fc95ca9c4cbe5223096c142d8c4a2b1d"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:326e42f2b49462e05f8527a1311ce98f9f97c484b3e443ec0ea4638bed3aebcf"}, - {file = "rpds_py-0.22.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9bbdba9e75b1a9ee1dd1335034dad998ef1acc08492226c6fd50aa773bdfa7d"}, - {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:41f65a97bf2c4b161c9f8f89bc37058346bec9b36e373c8ad00a16c957bff625"}, - {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0686f2c16eafdc2c6b4ce6e86e5b3092e87db09ae64be2787616444eb35b9756"}, - {file = "rpds_py-0.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e7c9aa2353eb0b0d845323857197daa036c2ff8624df990b0d886d22a8f665e"}, - {file = "rpds_py-0.22.0-cp311-cp311-win32.whl", hash = "sha256:2d2fc3ab021be3e0b5aec6d4164f2689d231b8bfc5185cc454314746aa4aee72"}, - {file = "rpds_py-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:87453d491369cd8018016d2714a13e8461975161703c18ee31eecf087a8ae5d4"}, - {file = "rpds_py-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e9d4293b21c69ee4f9e1a99ac4f772951d345611c614a0cfae2ec6b565279bc9"}, - {file = "rpds_py-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:67e013a17a3db4d98cc228fd5aeb36a51b0f5cf7330b9102a552060f1fe4e560"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b639a19e1791b646d27f15d17530a51722cc728d43b2dff3aeb904f92d91bac"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1357c3092702078b7782b6ebd5ba9b22c1a291c34fbf9d8f1a48237466ac7758"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:842855bbb113a19c393c6de5aa6ed9a26c6b13c2fead5e49114d39f0d08b94d8"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ae7927cd2b869ca4dc645169d8af5494a29c99afd0ea0f24dd00c811ab1d8b8"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91bfef5daa2a5a4fe62f8d317fc91a626073639f951f851bd2cb252d01bc6c5"}, - {file = "rpds_py-0.22.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fc4824e38c1e91a73bc820e7caacaf19d0acd557465aceef0420ca59489b390"}, - {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:92d28a608127b357da47c99e0d0e0655ca2060286540fe9f2a25a2e8ac666e05"}, - {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c637188b930175c256f13adbfc427b83ec7e64476d1ec9d6608f312bb84e06c3"}, - {file = "rpds_py-0.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93bbd66f46dddc41e8c656130c97c0fb515e0fa44e1eebb2592769dbbd41b2f5"}, - {file = "rpds_py-0.22.0-cp312-cp312-win32.whl", hash = "sha256:54d8f94dec5765a9edc19610fecf0fdf9cab36cbb9def1213188215f735a6f98"}, - {file = "rpds_py-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:931bf3d0705b2834fed29354f35170fa022fe22a95542b61b7c66aca5f8a224f"}, - {file = "rpds_py-0.22.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2a57300cc8b034c5707085249efd09f19116bb80278d0ec925d7f3710165c510"}, - {file = "rpds_py-0.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c398a5a8e258dfdc5ea2aa4e5aa2ca3207f654a8eb268693dd1a76939074a588"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a6cc4eb1e86364331928acafb2bb41d8ab735ca3caf2d6019b9f6dac3f4f65d"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:574c5c94213bc9990805bfd7e4ba3826d3c098516cbc19f0d0ef0433ad93fa06"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c0321bc03a1c513eca1837e3bba948b975bcf3a172aebc197ab3573207f137a"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d276280649305c1da6cdd84585d48ae1f0efa67434d8b10d2df95228e59a05bb"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c17b43fe9c6da16885e3fe28922bcd1a029e61631fb771c7d501019b40bcc904"}, - {file = "rpds_py-0.22.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48c95997af9314f4034fe5ba2d837399e786586e220835a578d28fe8161e6ae5"}, - {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9aa4af6b879bb75a3c7766fbf49d77f4097dd12b548ecbbd8b3f85caa833281"}, - {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8426f97117b914b9bfb2a7bd46edc148e8defda728a55a5df3a564abe70cd7a4"}, - {file = "rpds_py-0.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:034964ea0ea09645bdde13038b38abb14be0aa747f20fcfab6181207dd9e0483"}, - {file = "rpds_py-0.22.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:3dc7c64b56b82428894f056e9ff6e8ee917ff74fc26b65211a33602c2372e928"}, - {file = "rpds_py-0.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1212cb231f2002934cd8d71a0d718fdd9d9a2dd671e0feef8501038df3508026"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f21e1278c9456cd601832375c778ca44614d3433996488221a56572c223f04a"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:875fe8dffb43c20f68379ee098b035a7038d7903c795d46715f66575a7050b19"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e23dcdd4b2ff9c6b3317ea7921b210d39592f8ca1cdea58ada25b202c65c0a69"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fb8efc9e579acf1e556fd86277fecec320c21ca9b5d39db96433ad8c45bc4a"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe23687924b25a2dee52fab15976fd6577ed8518072bcda9ff2e2b88ab1f168b"}, - {file = "rpds_py-0.22.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5469b347445d1c31105f33e7bfc9a8ba213d48e42641a610dda65bf9e3c83f5"}, - {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a810a57ce5e8ecf8eac6ec4dab534ff80c34e5a2c31db60e992009cd20f58e0f"}, - {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d9bb9242b38a664f307b3b897f093896f7ed51ef4fe25a0502e5a368de9151ea"}, - {file = "rpds_py-0.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b4660943030406aaa40ec9f51960dd88049903d9536bc3c8ebb5cc4e1f119bbe"}, - {file = "rpds_py-0.22.0-cp313-cp313t-win32.whl", hash = "sha256:208ce1d8e3af138d1d9b21d7206356b7f29b96675e0113aea652cf024e4ddfdc"}, - {file = "rpds_py-0.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e6da2e0500742e0f157f005924a0589f2e2dcbfdd6cd0cc0abce367433e989be"}, - {file = "rpds_py-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f980a0640599a74f27fd9d50c84c293f1cb7afc2046c5c6d3efaf8ec7cdbc326"}, - {file = "rpds_py-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca505fd3767a09a139737f3278bc8a485cb64043062da89bcba27e2f2ea78d33"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba235e00e0878ba1080b0f2a761f143b2a2d1c354f3d8e507fbf2f3de401bf18"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81e7a27365b02fe70a77f1365376879917235b3fec551d19b4c91b51d0bc1d07"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32a0e24cab2daae0503b06666d516e90a080c1a95aff0406b9f03c6489177c4b"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a73ed43d64209e853bba567a543170267a5cd64f359540b0ca2d597e329ba172"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0abcce5e874474d3eab5ad53be03dae2abe651d248bdeaabe83708e82969e78"}, - {file = "rpds_py-0.22.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4e9946c8c7def17e4fcb5eddb14c4eb6ebc7f6f309075e6c8d23b133c104607"}, - {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:758098b38c344d9a7f279baf0689261777e601f620078ef5afdc9bd3339965c3"}, - {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9ad4640a409bc2b7d22b7921e7660f0db96c5c8c69fbb2e8f3261d4f71d33983"}, - {file = "rpds_py-0.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8c48fc7458fe3a74dcdf56ba3534ff41bd421f69436df09ff3497fdaac18b431"}, - {file = "rpds_py-0.22.0-cp39-cp39-win32.whl", hash = "sha256:fde778947304e55fc732bc8ea5c6063e74244ac1808471cb498983a210aaf62c"}, - {file = "rpds_py-0.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:5fdf91a7c07f40e47b193f2acae0ed9da35d09325d7c3c3279f722b7cbf3d264"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c8fd7a16f7a047e06c747cfcf2acef3ac316132df1c6077445b29ee6f3f3a70b"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b6e4bcfc32f831bfe3d6d8a5acedfbfd5e252a03c83fa24813b277a3a8a13ca"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eadd2417e83a77ce3ae4a0efd08cb0ebdfd317b6406d11020354a53ad458ec84"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9dc2113e0cf0dd637751ca736186fca63664939ceb9f9f67e93ade88c69c0c9"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2c00acdf68f1f69a476b770af311a7dc3955b7de228b04a40bcc51ac4d743b"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfdabdf8519c93908b2bf0f87c3f86f9e88bab279fb4acfd0907519ca5a1739f"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8338db3c76833d02dc21c3e2c42534091341d26e4f7ba32c6032bb558a02e07b"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8ad4dfda52e64af3202ceb2143a62deba97894b71c64a4405ee80f6b3ea77285"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b94b074dcce39976db22ea75c7aea8b22d95e6d3b62f76e20e1179a278521d8"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d4f2af3107fe4dc40c0d1a2409863f5249c6796398a1d83c1d99a0b3fa6cfb8d"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:bb11809b0de643a292a82f728c494a2bbef0e30a7c42d37464abbd6bef7ca7b1"}, - {file = "rpds_py-0.22.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c1c21030ed494deb10226f90e2dbd84a012d59810c409832714a3dd576527be2"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:64a0c965a1e299c9b280006bdb15c276c427c45360aed676305dc36bcaa4d13c"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:2498ff422823be087b48bc82710deb87ac34f6b7c8034ee39920647647de1e60"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59e63da174ff287db05ef7c21d75974a5bac727ed60452aeb3a14278477842a8"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1c04fb380bc8efaae2fdf17ed6cd5d223da78a8b0b18a610f53d4c5d6e31dfd"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04919ffa9a728c446b27b6b625fa1d00ece221bdb9d633e978a7e0353a12c0e"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24c28df05bd284879d0fac850ba697077d2a33b7ebcaea6318d6b6cdfdc86ddc"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33622dc63c295788eed09dbb1d11bed178909d3267b02d873116ee6be368244"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7539dbb8f705e13629ba6f23388976aad809e387f32a6e5c0712e4e8d9bfcce7"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:b8906f537978da3f7f0bd1ba37b69f6a877bb43312023b086582707d2835bf2f"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:62ab12fe03ffc49978d29de9c31bbb216610157f7e5ca8e172fed6642aead3be"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:762206ba3bf1d6c8c9e0055871d3c0d5b074b7c3120193e6c067e7866f106ab1"}, - {file = "rpds_py-0.22.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed0102146574e5e9f079b2e1a06e6b5b12a691f9c74a65b93b7f3d4feda566c6"}, - {file = "rpds_py-0.22.0.tar.gz", hash = "sha256:32de71c393f126d8203e9815557c7ff4d72ed1ad3aa3f52f6c7938413176750a"}, + {file = "rpds_py-0.22.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ab27dd4edd84b13309f268ffcdfc07aef8339135ffab7b6d43f16884307a2a48"}, + {file = "rpds_py-0.22.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9d5b925156a746dc1f5f52376fdd1fbdd3f6ffe1fcd6f5e06f77ca79abb940a3"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201650b309c419143775c15209c620627de3c09a27c7fb58375325aec5cce260"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31264187fc934ff1024a4f56775f33c9252d3f4f3e27ec07d1995a26b52702c3"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97c5ffe47ccf92d8b17e10f8a5ce28d015aa1196edc3359684cf31504eae6a14"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9ac7280bd045f472b50306d7efeee051b69e3a2dd1b90f46bd7e86e63b1efa2"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f941fb86195f97be7f6efe04a21b223f05dfe4d1dfb159999e2f8d101e44cc4"}, + {file = "rpds_py-0.22.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f91bfc39f7a64168e08ab831fa497ec5438c1d6c6e2f9e12848d95ad11ac8523"}, + {file = "rpds_py-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:effcae2152afe7937a28376dbabb25c770ef99ed4e16a4ffeb8e6a4f7c4f06aa"}, + {file = "rpds_py-0.22.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2177e59c033bf0d1bf7de1ced561205963583caf3242c6c700a723034bfb5f8e"}, + {file = "rpds_py-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66f4f48a89cdd30ab3a47335df81c76e9a63799d0d84b29c0618371c66fa37b0"}, + {file = "rpds_py-0.22.1-cp310-cp310-win32.whl", hash = "sha256:b07fa9e634234e84096adfa4be3828c8f26e238679c122824b2b3d7131bec578"}, + {file = "rpds_py-0.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca4657e9fd0b1b5376942d403d634ce188f79064f0873aa853ab05b10185ceec"}, + {file = "rpds_py-0.22.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:608c84699b2db09c6a8743845b1a3dad36fae53eaaecb241d45b13dff74405fb"}, + {file = "rpds_py-0.22.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9dae4eb9b5534e09ba6c6ab496a757e5e394b7e7b08767d25ca37e8d36491114"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a1f000c5f6e08b298275bae00921e9fbbf2a35dae0a86db2821c058c2201a9"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:580ccbf11f02f948add4cb641843030a89f1463d7c0740cbfc9aca91e9dc34b3"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96559e05bdf938b2048353e10a7920b98f853cefe4482c2064a718d7d0a50bd7"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128cbaed7ba26116820bcb992405d6a13ea18c8fca1b8c4f59906d858e91e979"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:734783dd7da58f76222f458346ddebdb3621686a1a2a667db5049caf0c9956b9"}, + {file = "rpds_py-0.22.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c9ce6b83597d45bec44a2690857ede62fc98223772135f8a7fa90884eb726501"}, + {file = "rpds_py-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bca4428c4a957b78ded3e6e62884ab03f029dce8fa8d34818da0f80f61332b49"}, + {file = "rpds_py-0.22.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ded65691a1d3fd7d2aa89d2c91aa51f941601bb2ce099739909034d957fef4b"}, + {file = "rpds_py-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72407065ad459db9f3d052ea8c51e02534f02533fc61e51cbab3bd94166f086c"}, + {file = "rpds_py-0.22.1-cp311-cp311-win32.whl", hash = "sha256:eb013aa01b404219f28dc973d9e6310fd4db216d7299253dd355629952e0564e"}, + {file = "rpds_py-0.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:8bd9ec1db79a664f4cbb12878693b73416f4d2cb425d3e27eccc1bdfbdc826ef"}, + {file = "rpds_py-0.22.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8ec41049c90d204a6561238a9ad6c7263ebb7009d9759c98b58078d9d2fec9ba"}, + {file = "rpds_py-0.22.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:102be79c4cc47a4aeb5912401185c404cd2601c15a7163bbecff7f1bfe20b669"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a603155db408f773637f9e3a712c6e3cbc521aaa8fa2b99f9ba6106c59a2496"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dbff9402c2bdf00bf0df9905694b3c292a3847c725651938a72f554351a5fcb"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96b3759d8ab2323324e0a92b2f44834f9d88089b8d1ab6f533b61f4be3411cef"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3029f481b31f329b1fdb4ec4b56935d82210ddd9c6f86ea5a87c06f1e97b161"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d280b4bf09f719b89fd9aab3b71067acc0d0449b7d1eba99a2ade4939cef8296"}, + {file = "rpds_py-0.22.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8e97e19aa7b0b0d801a159f932ce4435f1049c8c38e2bb372bb5bee559ce50"}, + {file = "rpds_py-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:50e4b5d291105f7063259fe0125b1af902fb34499444d7c5c521dd8328b00939"}, + {file = "rpds_py-0.22.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d3777c446bb1c5fcd82dc3f8776e1a146cd91e80cc1892f8634575ace438d22f"}, + {file = "rpds_py-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:447ae1104fb32197b9262f772d565d38e834cc2e9edd89350b37b88fed636e70"}, + {file = "rpds_py-0.22.1-cp312-cp312-win32.whl", hash = "sha256:55d371b9d8b0c2a68a50413a8cb01c3c3ce1ea4f768bf77b66669a9a486e101e"}, + {file = "rpds_py-0.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:413a30a99d8683dace3765885920ed27ab662efbb6c98d81db76c397ad1ffd71"}, + {file = "rpds_py-0.22.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa2ba0176037c915d8660a4e46581d645e2c22b5373e466bc8640a794d45861a"}, + {file = "rpds_py-0.22.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ba6c66fbc6015b2f99e7176fec41793cecb00c4cc357cad038dff85e6ac42ab"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15fa4ca658f8ad22645d3531682b17e5580832efbfa87304c3e62214c79c1e8a"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7833ef6f5d6cb634f296abfd93452fb3eb44c4e9a6ae95c1021eab704c1cee2"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0467838c90435b80793cde486a318fc916ee57f2af54e4b10c72b20cbdcbaa9"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d962e2e89b3a95e3597a34b8c93ced1e98958502c5b8096c9fd69deff279f561"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ce729f1dc8a4a190c34b69f75377bddc004079b2963ab722ab91fafe040be6d"}, + {file = "rpds_py-0.22.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8080467df22feca0fc9c46567001777c6fbc2b4a2683a7137420896051874ca1"}, + {file = "rpds_py-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f9eb37d3a60b262a98ab51ee899cac039de9ca0ce68dcf1a6518a09719020b0"}, + {file = "rpds_py-0.22.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:153248f48d6f90a295a502f53ec544a3ffbd21b0bb32f5dca39c4b93a764d6a2"}, + {file = "rpds_py-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0a53592cdf98cec3dfcdb24ffec8a4797e7656b65700099af43ec7df023b6de4"}, + {file = "rpds_py-0.22.1-cp313-cp313-win32.whl", hash = "sha256:e8056adcefa2dcb67e8bc91ea5eee26df66e8b297a8cd6ff0903f85c70908fa0"}, + {file = "rpds_py-0.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:a451dba533be77454ebcffc85189108fc05f279100835ac76e7989edacb89156"}, + {file = "rpds_py-0.22.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:2ea23f1525d4f64286dbe0947c929d45c3ffe963b2dbed1d3844a2e4938bda42"}, + {file = "rpds_py-0.22.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3aaa22487477de9618ce3b37f99fbe81219ba96f3c2ca84f576f0ab451b83aba"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8954b9ffe60f479a0c0ba40987db2546c735ab02a725ea7fd89342152d4d821d"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8502a02ae3ae67084f5a0bf5a8253b19fa7a887f824e41e016cdb0ac532a06f"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a083221b6a4ecdef38a60c95d8d3223d99449cb4da2544e9644958dc16664eb9"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:542eb246d5be31b5e0a9c8ddb9539416f9b31f58f75bd4ee328bff2b5c58d6fd"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffae97d28ea4f2c613a751d087b75a97fb78311b38cc2e9a2f4587e473ace167"}, + {file = "rpds_py-0.22.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0ff8d5b13ce2357fa8b33a0a2e3775aa71df5bf7c8ba060634c9d15ab12f357"}, + {file = "rpds_py-0.22.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f057a0c546c42964836b209d8de9ea1a4f4b0432006c6343cbe633d8ca14571"}, + {file = "rpds_py-0.22.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:48ee97c7c6027fd423058675b5a39d0b5f7a1648250b671563d5c9f74ff13ff0"}, + {file = "rpds_py-0.22.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:babec324e8654a59122aaa66936a9a483faa03276db9792f51332475c2dddc4a"}, + {file = "rpds_py-0.22.1-cp313-cp313t-win32.whl", hash = "sha256:e69acdbc132c9592c8dc393af85e38e206ca847c7019a953ff625191c3a12312"}, + {file = "rpds_py-0.22.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c783e4ed68200f4e03c125690d23158b1c49c4b186d458a18debc109bbdc3c2e"}, + {file = "rpds_py-0.22.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2143c3aed85992604d758bbe67da839fb4aab3dd2e1c6dddab5b3ca7162b34a2"}, + {file = "rpds_py-0.22.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f57e2d0f8022783426121b586d7c842ea40ea832a29e28ca36c881b54c74fb28"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c0c324879d483504b07f7b18eb1b50567c434263bbe4866ecce33056162668a"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c40e02cc4f3e18fd39344edb10eebe04bd11cfd13119606b5771e5ea51630d3"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f76c6f319e57007ad52e671ec741d801324760a377e3d4992c9bb8200333ebac"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5cae9b415ea8a6a563566dbf46650222eccc5971c7daa16fbee63aef92ae543"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b09209cdfcacf5eba9cf80367130532e6c02e695252e1f64d3cfcc2356e6e19f"}, + {file = "rpds_py-0.22.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dbe428d0ac6eacaf05402adbaf137f59ad6063848182d1ff294f95ce0f24005b"}, + {file = "rpds_py-0.22.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:626b9feb01bff049a5aec4804f0c58db12585778b4902e5376a95b01f80a7a16"}, + {file = "rpds_py-0.22.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec1ccc2a9f764cd632fb8ab28fdde166250df54fc8d97315a4a6948dc5367639"}, + {file = "rpds_py-0.22.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ef92b1fbe6aa2e7885eb90853cc016b1fc95439a8cc8da6d526880e9e2148695"}, + {file = "rpds_py-0.22.1-cp39-cp39-win32.whl", hash = "sha256:c88535f83f7391cf3a45af990237e3939a6fdfbedaed2571633bfdd0bceb36b0"}, + {file = "rpds_py-0.22.1-cp39-cp39-win_amd64.whl", hash = "sha256:7839b7528faa4d134c183b1f2dd1ee4dc2ca2f899f4f0cfdf00fc04c255262a7"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a0ed14a4162c2c2b21a162c9fcf90057e3e7da18cd171ab344c1e1664f75090e"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:05fdeae9010533e47715c37df83264df0122584e40d691d50cf3607c060952a3"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4659b2e4a5008715099e216050f5c6976e5a4329482664411789968b82e3f17d"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a18aedc032d6468b73ebbe4437129cb30d54fe543cde2f23671ecad76c3aea24"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149b4d875ef9b12a8f5e303e86a32a58f8ef627e57ec97a7d0e4be819069d141"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdaee3947eaaa52dae3ceb9d9f66329e13d8bae35682b1e5dd54612938693934"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36ce951800ed2acc6772fd9f42150f29d567f0423989748052fdb39d9e2b5795"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab784621d3e2a41916e21f13a483602cc989fd45fff637634b9231ba43d4383b"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c2a214bf5b79bd39a9de1c991353aaaacafda83ba1374178309e92be8e67d411"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:85060e96953647871957d41707adb8d7bff4e977042fd0deb4fc1881b98dd2fe"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c6f3fd617db422c9d4e12cb8d84c984fe07d6d9cb0950cbf117f3bccc6268d05"}, + {file = "rpds_py-0.22.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f2d1b58a0c3a73f0361759642e80260a6d28eee6501b40fe25b82af33ef83f21"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:76eaa4c087a061a2c8a0a92536405069878a8f530c00e84a9eaf332e70f5561f"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:959ae04ed30cde606f3a0320f0a1f4167a107e685ef5209cce28c5080590bd31"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:198067aa6f3d942ff5d0d655bb1e91b59ae85279d47590682cba2834ac1b97d2"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e7e99e2af59c56c59b6c964d612511b8203480d39d1ef83edc56f2cb42a3f5d"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0545928bdf53dfdfcab284468212efefb8a6608ca3b6910c7fb2e5ed8bdc2dc0"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef7282d8a14b60dd515e47060638687710b1d518f4b5e961caad43fb3a3606f9"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3f245c2f39a5692d9123c174bc48f6f9fe3e96407e67c6d04541a767d99e72"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efb2ad60ca8637d5f9f653f9a9a8d73964059972b6b95036be77e028bffc68a3"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d8306f27418361b788e3fca9f47dec125457f80122e7e31ba7ff5cdba98343f8"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4c8dc7331e8cbb1c0ea2bcb550adb1777365944ffd125c69aa1117fdef4887f5"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:776a06cb5720556a549829896a49acebb5bdd96c7bba100191a994053546975a"}, + {file = "rpds_py-0.22.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e4f91d702b9ce1388660b3d4a28aa552614a1399e93f718ed0dacd68f23b3d32"}, + {file = "rpds_py-0.22.1.tar.gz", hash = "sha256:157a023bded0618a1eea54979fe2e0f9309e9ddc818ef4b8fc3b884ff38fedd5"}, ] [[package]] From 2b3d0d27f79b4898ecb3015dfde34d7917776fb5 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 13:51:09 -0800 Subject: [PATCH 11/25] more updates --- .../__init__.py | 0 .../generate_config.py} | 0 .../schema.json | 0 pyproject.toml | 4 ++++ tests/test_configs.py | 4 ++-- 5 files changed, 6 insertions(+), 2 deletions(-) rename podaac/{forge_tig_configuration => podaac_forge_tig_config_generator}/__init__.py (100%) rename podaac/{forge_tig_configuration/new_generate_config.py => podaac_forge_tig_config_generator/generate_config.py} (100%) rename podaac/{forge_tig_configuration => podaac_forge_tig_config_generator}/schema.json (100%) diff --git a/podaac/forge_tig_configuration/__init__.py b/podaac/podaac_forge_tig_config_generator/__init__.py similarity index 100% rename from podaac/forge_tig_configuration/__init__.py rename to podaac/podaac_forge_tig_config_generator/__init__.py diff --git a/podaac/forge_tig_configuration/new_generate_config.py b/podaac/podaac_forge_tig_config_generator/generate_config.py similarity index 100% rename from podaac/forge_tig_configuration/new_generate_config.py rename to podaac/podaac_forge_tig_config_generator/generate_config.py diff --git a/podaac/forge_tig_configuration/schema.json b/podaac/podaac_forge_tig_config_generator/schema.json similarity index 100% rename from podaac/forge_tig_configuration/schema.json rename to podaac/podaac_forge_tig_config_generator/schema.json diff --git a/pyproject.toml b/pyproject.toml index bc0908f..b70eb59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,10 @@ description = "" authors = ["Simon Liu "] readme = "README.md" +packages = [ + { include = "podaac" }, +] + [tool.poetry.dependencies] python = "^3.10" pytest = "^8.3.3" diff --git a/tests/test_configs.py b/tests/test_configs.py index 9564a00..5650de6 100644 --- a/tests/test_configs.py +++ b/tests/test_configs.py @@ -3,12 +3,12 @@ import pytest from functools import lru_cache from jsonschema import validate, ValidationError -from podaac.forge_tig_configuration import new_generate_config +from podaac.podaac_forge_tig_config_generator import generate_config @lru_cache(maxsize=1) def get_schema(): """Cache schema loading for performance.""" - return new_generate_config.HiTideConfigGenerator.load_schema() + return generate_config.HiTideConfigGenerator.load_schema() def validate_config(config_file): """Validate single configuration file.""" From 4f32b0d4f8720b6ef49f74880258a9f9da70671d Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 13:53:08 -0800 Subject: [PATCH 12/25] add poetry install step --- .github/workflows/test_configs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 6142e7d..422c693 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -24,6 +24,7 @@ jobs: - name: Install Software run: | poetry build + poetry install - name: Run pytest run: poetry run pytest From 8c6c6ed5769f12f6a41f0f9f82afc52ae6ec093d Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 13:54:03 -0800 Subject: [PATCH 13/25] update deploy step --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4291181..b6d689c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,6 +30,7 @@ jobs: - name: Install Software run: | poetry build + poetry install - name: Run pytest run: poetry run pytest From 4a7cb85ee2fd731612aa5e4c939481649bafb040 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 14:14:34 -0800 Subject: [PATCH 14/25] add cli command --- .../generate_config.py | 23 +++++++++++-------- pyproject.toml | 3 +++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/podaac/podaac_forge_tig_config_generator/generate_config.py b/podaac/podaac_forge_tig_config_generator/generate_config.py index 2f3ac59..b4c5890 100644 --- a/podaac/podaac_forge_tig_config_generator/generate_config.py +++ b/podaac/podaac_forge_tig_config_generator/generate_config.py @@ -1,11 +1,11 @@ -import pandas as pd -import click +import ast import json import os -import numpy as np -from jsonschema import validate, Draft7Validator from typing import Dict, Any, Optional -import ast +import click +import numpy as np +import pandas as pd +from jsonschema import validate class HiTideConfigGenerator: REQUIRED_SETTINGS_SHEET = "required-settings" @@ -42,7 +42,7 @@ def recursive_filter(current_config, current_schema): filtered_obj[key] = filtered_value elif prop_schema.get('type') == 'array' and isinstance(value, list): filtered_obj[key] = [ - recursive_filter(item, prop_schema.get('items', {})) + recursive_filter(item, prop_schema.get('items', {})) for item in value if item ] else: @@ -98,7 +98,7 @@ def read_sheet_as_dict(cls, file_path: str, sheet_name: str) -> Optional[Dict[st df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl') if df.empty: raise ValueError(f"Sheet '{sheet_name}' is empty") - + first_row = df.iloc[0] return {k: cls._convert_value(v) for k, v in first_row.items() if pd.notna(v)} except Exception as e: @@ -132,7 +132,7 @@ def generate_hitide_config(file: str): """Generate and validate HiTide configuration.""" generator = HiTideConfigGenerator() config = generator.generate_configuration(file) - + if not config: click.echo("Configuration generation failed.", err=True) return @@ -141,8 +141,13 @@ def generate_hitide_config(file: str): schema = generator.load_schema() validate(instance=config, schema=schema) click.echo(json.dumps(config, indent=2)) + short_name = config.get('shortName') + file = f"{short_name}.cfg" + with open(file, 'w') as file: + json.dump(config, file, indent=4) + except Exception as e: click.echo(f"Validation error: {e}", err=True) if __name__ == '__main__': - generate_hitide_config() \ No newline at end of file + generate_hitide_config() diff --git a/pyproject.toml b/pyproject.toml index b70eb59..9728bf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,3 +20,6 @@ click = "^8.1" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +generate_config = 'podaac.podaac_forge_tig_config_generator.generate_config:generate_hitide_config' From aaaec321857bdfb841ef24dd361683fb48b5a124 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 14:33:00 -0800 Subject: [PATCH 15/25] update readme --- README.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5524807..03cc2bd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,122 @@ -# forge-tig-configuration -Configuration files for collections processed by FORGE and TIG \ No newline at end of file +# HiTide Configuration Generator + +## Overview + +HiTide Configuration Generator is a Python CLI tool that generates and validates configuration files for HiTide from Excel spreadsheets. It provides a flexible and type-safe way to create configuration settings for data processing and visualization workflows. + +## Features + +- Generate configuration from Excel spreadsheets +- Type-safe value conversion +- JSON schema validation +- Flexible configuration generation +- CLI interface for easy use + +## Prerequisites + +- Python 3.10+ +- Poetry +- pip + +## Installation + +1. Clone the repository: +```bash +git clone https://github.com/podaac/forge-tig-configuration.git +cd forge-tig-configuration +``` + +2. Install Poetry (if not already installed): +```bash +pip install poetry +``` + +3. Install project dependencies: +```bash +poetry install +``` + +## Usage + +### Activating the Virtual Environment + +To activate the Poetry virtual environment: +```bash +poetry shell +``` + +### CLI Command + +```bash +generate_config -f +``` + +#### Command Options + +- `-f, --file`: **[Required]** Path to the Excel file containing configuration settings +- `-h, --help`: Show help message + +### Excel File Structure + +Your Excel file should contain three sheets: + +1. `required-settings`: Global configuration settings +2. `forge-py`: Strategy and processing configurations +3. `tig`: Image generation variables + +#### Example Excel Sheets + +- **required-settings**: Contains key-value pairs for global settings +- **forge-py**: Configuration for footprint strategy and processing parameters +- **tig**: List of image variables for processing + +## Configuration Generation Process + +1. Reads specified Excel file +2. Converts data types safely +3. Merges configurations from different sheets +4. Applies JSON schema validation +5. Generates a configuration file named after the `shortName` + +## Output + +- Prints JSON configuration to console +- Generates a `.cfg` file named after the `shortName` + +## Error Handling + +- Validates input against JSON schema +- Provides detailed error messages for configuration issues +- Handles type conversions and edge cases + +## Development + +### Running Tests + +```bash +poetry run pytest tests/ +``` + +### Adding Dependencies + +To add a new project dependency: +```bash +poetry add +``` + +To add a development dependency: +```bash +poetry add --group dev +``` + +### Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## License + +Distributed under the Apatche License. See `LICENSE` for more information. \ No newline at end of file From cb6499f7aeb71644c25aae4aa4a52fcc6a01395b Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 14:34:21 -0800 Subject: [PATCH 16/25] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03cc2bd..810e963 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Overview -HiTide Configuration Generator is a Python CLI tool that generates and validates configuration files for HiTide from Excel spreadsheets. It provides a flexible and type-safe way to create configuration settings for data processing and visualization workflows. +HiTide Configuration Generator is a Python CLI tool that generates and validates configuration files for HiTide from Excel spreadsheets. It provides a flexible and type-safe way to create configuration settings for generating footprint and thumbnails. ## Features From 78594248bc0384abb53096bd793c5532433b3cba Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 14:45:08 -0800 Subject: [PATCH 17/25] update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 810e963..e66468b 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,11 @@ Your Excel file should contain three sheets: - **forge-py**: Configuration for footprint strategy and processing parameters - **tig**: List of image variables for processing +#### How to use example speadsheet + +- Fill in data for each sheet, leave empty if the field doesn't apply. +- Documentation for forge-py configurations https://github.com/podaac/forge-py + ## Configuration Generation Process 1. Reads specified Excel file From 89df8b64059db2f02a3e2ff558dff41f602594ab Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 14:58:20 -0800 Subject: [PATCH 18/25] update ppd --- podaac/podaac_forge_tig_config_generator/generate_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/podaac/podaac_forge_tig_config_generator/generate_config.py b/podaac/podaac_forge_tig_config_generator/generate_config.py index b4c5890..3d9447c 100644 --- a/podaac/podaac_forge_tig_config_generator/generate_config.py +++ b/podaac/podaac_forge_tig_config_generator/generate_config.py @@ -80,7 +80,7 @@ def generate_configuration(cls, file_path: str) -> Optional[Dict[str, Any]]: # Default settings defaults = { 'tiles': {'steps': [30, 14]}, - 'image': {'ppd': 8, 'res': 8}, + 'image': {'ppd': 4, 'res': 8}, 'footprinter': 'forge-py' } From 0834f344b75fa6d6e4c002f4b269684966868ecf Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 15:38:31 -0800 Subject: [PATCH 19/25] add pylint and flake --- .flake8 | 10 ++ .github/workflows/deploy.yml | 7 +- .github/workflows/test_configs.yml | 7 +- .pylintrc | 20 +++ .../generate_config.py | 7 + poetry.lock | 163 +++++++++++++++++- pyproject.toml | 2 + 7 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 .flake8 create mode 100644 .pylintrc diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..d3849b2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,10 @@ +[flake8] +# Ignore specific warnings or errors. +ignore = E501 + +# Optionally, increase the max line length (if you prefer to set a limit instead of ignoring entirely). +max-line-length = 120 + +# Exclude specific files or directories. +exclude = tests/, docs/, .venv/ + diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b6d689c..3b0f5c0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,8 +32,11 @@ jobs: poetry build poetry install - - name: Run pytest - run: poetry run pytest + - name: Run Tests + run:| + poetry run flake8 podaac + poetry run pylint podaac + poetry run pytest # First job in the workflow installs and verifies the software deploy_sit: diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 422c693..4a15570 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -26,5 +26,8 @@ jobs: poetry build poetry install - - name: Run pytest - run: poetry run pytest + - name: Run Tests + run:| + poetry run flake8 podaac + poetry run pylint podaac + poetry run pytest diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..6dd4fbf --- /dev/null +++ b/.pylintrc @@ -0,0 +1,20 @@ +[MASTER] +# List of directories to ignore (relative to the current directory). +ignore=tests + +[MESSAGES CONTROL] +# Disable the following messages by symbolic name or ID. +disable=line-too-long, # C0301: Line too long + broad-exception-caught, # W0718: Catching too general exception Exception + unspecified-encoding, # W1514: Using open without explicitly specifying an encoding + redefined-argument-from-local, # R1704: Redefining argument with a local name + no-value-for-parameter # E1120: No value for argument in function call + +[FORMAT] +# Maximum allowed line length (optional override if you want to enforce a slightly longer limit). +max-line-length=120 + +[REPORTS] +# Disable the output of the report summary. +reports=no + diff --git a/podaac/podaac_forge_tig_config_generator/generate_config.py b/podaac/podaac_forge_tig_config_generator/generate_config.py index 3d9447c..434dbda 100644 --- a/podaac/podaac_forge_tig_config_generator/generate_config.py +++ b/podaac/podaac_forge_tig_config_generator/generate_config.py @@ -1,3 +1,5 @@ +"""script used to generate forge tig configuration""" + import ast import json import os @@ -7,7 +9,10 @@ import pandas as pd from jsonschema import validate + class HiTideConfigGenerator: + """Class to generate configurations""" + REQUIRED_SETTINGS_SHEET = "required-settings" FORGE_PY_SHEET = "forge-py" TIG_SHEET = "tig" @@ -126,6 +131,7 @@ def load_schema() -> Dict[str, Any]: with open(schema_path, "r") as file: return json.load(file) + @click.command() @click.option('-f', '--file', help='Excel file with configuration settings', required=True) def generate_hitide_config(file: str): @@ -149,5 +155,6 @@ def generate_hitide_config(file: str): except Exception as e: click.echo(f"Validation error: {e}", err=True) + if __name__ == '__main__': generate_hitide_config() diff --git a/poetry.lock b/poetry.lock index 9de640f..998bff0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,19 @@ # This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +[[package]] +name = "astroid" +version = "3.3.5" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, + {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + [[package]] name = "attrs" version = "24.2.0" @@ -44,6 +58,21 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "dill" +version = "0.3.9" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + [[package]] name = "et-xmlfile" version = "2.0.0" @@ -69,6 +98,22 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "flake8" +version = "7.1.1" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + [[package]] name = "iniconfig" version = "2.0.0" @@ -80,6 +125,20 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + [[package]] name = "jsonschema" version = "4.23.0" @@ -115,6 +174,17 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[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 = "numpy" version = "2.1.3" @@ -258,8 +328,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.22.4", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -290,6 +360,22 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -305,6 +391,57 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pylint" +version = "3.3.2" +description = "python code static checker" +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "pylint-3.3.2-py3-none-any.whl", hash = "sha256:77f068c287d49b8683cd7c6e624243c74f92890f767f106ffa1ddf3c0a54cb7a"}, + {file = "pylint-3.3.2.tar.gz", hash = "sha256:9ec054ec992cd05ad30a6df1676229739a73f8feeabf3912c995d17601052b01"}, +] + +[package.dependencies] +astroid = ">=3.3.5,<=3.4.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + [[package]] name = "pytest" version = "8.3.4" @@ -531,6 +668,28 @@ files = [ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + [[package]] name = "tzdata" version = "2024.2" @@ -545,4 +704,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "bdf26488bb6097daf4204491f9bf21505eb58498dd7bd5ca7d594dee13088e9d" +content-hash = "8987a4cda712cdd89e29dcd6a80918754db6b1f745aeda573f80dfffd686ef80" diff --git a/pyproject.toml b/pyproject.toml index 9728bf1..4f4d9a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,8 @@ jsonschema = "^4.23.0" pandas = "^2.2.3" openpyxl = "^3.1.5" click = "^8.1" +pylint = "^3.3.2" +flake8 = "^7.1.1" [build-system] requires = ["poetry-core"] From 3e0cf066ad148ae157303692fb8edafe66fea19e Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 3 Dec 2024 15:39:59 -0800 Subject: [PATCH 20/25] fix github action --- .github/workflows/deploy.yml | 2 +- .github/workflows/test_configs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3b0f5c0..9e5c2a2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,7 +33,7 @@ jobs: poetry install - name: Run Tests - run:| + run: | poetry run flake8 podaac poetry run pylint podaac poetry run pytest diff --git a/.github/workflows/test_configs.yml b/.github/workflows/test_configs.yml index 4a15570..37c0ca1 100644 --- a/.github/workflows/test_configs.yml +++ b/.github/workflows/test_configs.yml @@ -27,7 +27,7 @@ jobs: poetry install - name: Run Tests - run:| + run: | poetry run flake8 podaac poetry run pylint podaac poetry run pytest From e4bf8a7cc90ceff73f988a63f180a6cfbd48f25f Mon Sep 17 00:00:00 2001 From: James Wood <8115931+jamesfwood@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:51:55 -0800 Subject: [PATCH 21/25] Update CHANGELOG.md Changed to past tense --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db01a11..c8384ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Schema validations - Added a new forge tig configuration generator - Added in test to validate json structure and schema validate the configurations - - Update configurations for new forge-py + - Updated configurations for new forge-py ### Changed ### Deprecated ### Removed From 24adc05141b28ae963d6fd05a0921e81230d4221 Mon Sep 17 00:00:00 2001 From: James Wood <8115931+jamesfwood@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:56:13 -0800 Subject: [PATCH 22/25] Update AVHRR19_G-NAVO-L2P-v1.0.cfg Updated formatting --- config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg index 0cb4a98..a51bdb4 100644 --- a/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ b/config-files/AVHRR19_G-NAVO-L2P-v1.0.cfg @@ -27,12 +27,12 @@ 80 ] }, - "open_cv": { - "pixel_height":1000, - "simplify":0.3, - "min_area": 30, - "fill_kernel": [30,30] - } + "open_cv": { + "pixel_height": 1000, + "simplify": 0.3, + "min_area": 30, + "fill_kernel": [30,30] + } }, "footprinter": "forge-py", "imgVariables": [ From aeb1939c774ed33ed909e17f35f6a2d1b96864fd Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Fri, 20 Dec 2024 09:04:50 -0800 Subject: [PATCH 23/25] remove extra schema json --- tests/schema.json | 290 ---------------------------------------------- 1 file changed, 290 deletions(-) delete mode 100644 tests/schema.json diff --git a/tests/schema.json b/tests/schema.json deleted file mode 100644 index 0e838d1..0000000 --- a/tests/schema.json +++ /dev/null @@ -1,290 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "shortName": { - "type": "string", - "description": "Short name of the dataset, required." - }, - "latVar": { - "type": "string", - "description": "Path to the latitude variable in the data, required." - }, - "lonVar": { - "type": "string", - "description": "Path to the longitude variable in the data, required." - }, - "timeVar": { - "type": "string", - "description": "Name of the time variable, optional." - }, - "is360": { - "type": "boolean", - "description": "Indicates if longitude is in 0-360 format, required." - }, - "tiles": { - "type": "object", - "properties": { - "steps": { - "type": "array", - "items": { - "type": "integer", - "additionalProperties": false, - "description": "Step size for each tile, represented as integers." - }, - "minItems": 2, - "maxItems": 3, - "additionalProperties": false, - "description": "An array of two integers representing the tile step sizes." - } - }, - "required": ["steps"], - "additionalProperties": false, - "description": "Tile configuration with step sizes for the grid." - }, - "global_grid": { - "type": "boolean", - "description": "Indicates whether the grid is global or not. Optional." - }, - "footprinter": { - "type": "string", - "enum": ["forge-py"], - "description": "The footprinter to use, must be 'forge-py'." - }, - "tolerance": { - "type": "number", - "description": "Tolerance value (optional). Can be a float or integer." - }, - "footprint": { - "type": "object", - "properties": { - "findValid": { - "type": "boolean", - "description": "Indicates whether to find valid footprint values." - }, - "b": { - "type": "string", - "description": "The 'b' footprint pattern." - }, - "s2": { - "type": "string", - "description": "The 's2' footprint pattern." - }, - "t": { - "type": "string", - "description": "The 't' footprint pattern." - }, - "s1": { - "type": "string", - "description": "The 's1' footprint pattern." - }, - "removeOrigin": { - "type": "boolean", - "description": "Unknown usage for forge" - }, - "strategy": { - "type": "string", - "enum": ["open_cv", "alpha_shape", "linestring", "periodic", "swot_linestring", "smap", "polarsides", "polar"], - "description": "Footprint calculation strategy, required." - }, - "open_cv": { - "type": "object", - "properties": { - "pixel_height": { - "type": "integer", - "minimum": 1, - "description": "Height of the pixel grid, optional." - }, - "simplify": { - "type": "number", - "minimum": 0, - "description": "Simplification tolerance for a polygon, optional." - }, - "min_area": { - "type": "integer", - "minimum": 0, - "description": "Minimum area for a polygon or it will be removed, optional." - }, - "fill_kernel": { - "type": "array", - "items": { - "type": "integer", - "minimum": 1 - }, - "minItems": 2, - "maxItems": 2, - "description": "Kernel size for filling small gaps, optional." - } - }, - "additionalProperties": false, - "description": "Parameters for the OpenCV strategy, optional." - }, - "alpha_shape": { - "type": "object", - "properties": { - "alpha": { - "type": "number", - "minimum": 0, - "description": "Alpha parameter for the alpha shape algorithm, optional." - }, - "thinning": { - "type": "object", - "properties": { - "method": { - "type": "string", - "enum": ["bin_avg", "standard"], - "description": "Thinning method, optional." - }, - "value": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2 - } - ] - } - }, - "additionalProperties": false, - "description": "Parameters for thinning, optional." - }, - "cutoff_lat": { - "type": "number", - "description": "Latitude cutoff for the footprint, optional." - }, - "smooth_poles": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2, - "description": "Latitude range for smoothing near poles, optional." - }, - "simplify": { - "type": "number", - "minimum": 0, - "description": "Simplification tolerance for a polygon, optional." - }, - "min_area": { - "type": "number", - "minimum": 0, - "description": "Minimum area for a polygon or it will be removed, optional." - }, - "fill_value": { - "type": "number", - "description": "Fill value for invalid areas, optional." - } - }, - "additionalProperties": false, - "description": "Parameters for the alpha shape strategy, optional." - } - }, - "additionalProperties": false, - "description": "Footprint calculation parameters, optional." - }, - "imgVariables": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of the image variable, required." - }, - "title": { - "type": "string", - "description": "Title of the image variable, optional." - }, - "units": { - "type": "string", - "description": "Units of the image variable, optional." - }, - "fill_missing": { - "type": "boolean", - "description": "Fill in the image if the data resolution is too high and needs to be filled in spots" - }, - "fill_value": { - "type": "number", - "description": "Fill value for a variable if theres no fill value" - }, - "is_swot_expert": { - "type": "boolean", - "description": "boolean to indicate if variable is a swot expert variable for a specific algorithm" - }, - "ppd": { - "type": "integer", - "description": "Pixels per degree (ppd) for the variable." - }, - "min": { - "oneOf": [ - { - "type": "number", - "description": "Minimum value of the variable as a number (integer or float)." - }, - { - "type": "string", - "pattern": "^-?\\d+(\\.\\d+)?$", - "description": "Minimum value of the variable as a string. Must be a valid number." - } - ] - }, - "max": { - "oneOf": [ - { - "type": "number", - "description": "Maximum value of the variable as a number (integer or float)." - }, - { - "type": "string", - "pattern": "^-?\\d+(\\.\\d+)?$", - "description": "Maximum value of the variable as a string. Must be a valid number." - } - ] - }, - "palette": { - "type": "string", - "description": "Palette used for visualization, optional." - }, - "legends": { - "type": "array", - "items": { - "type": "string", - "description": "Legend file name, expected to be a string representing a file path. Note should be invalid for new tig should be removed" - }, - "description": "List of legend files." - } - }, - "required": ["id", "min", "max", "palette"], - "additionalProperties": false, - "description": "Properties of an image variable." - }, - "description": "List of image variables, optional." - }, - "image": { - "type": "object", - "properties": { - "ppd": { - "type": "integer", - "description": "Pixels per degree (ppd) for the image." - }, - "res": { - "type": "integer", - "description": "Resolution of the image (res)." - } - }, - "required": ["ppd", "res"], - "additionalProperties": false, - "description": "Image configuration with pixels per degree and resolution." - } - - }, - "required": ["shortName", "latVar", "lonVar", "is360"], - "additionalProperties": false -} From 1be6dc55cf6f57a4d7299d0294960b15024536d7 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Fri, 20 Dec 2024 09:08:09 -0800 Subject: [PATCH 24/25] fix fill value for COWVR_STPH8_L1_TSDR_V9.0.cfg --- config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg index 4d2bad1..faa4669 100644 --- a/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg +++ b/config-files/COWVR_STPH8_L1_TSDR_V9.0.cfg @@ -11,7 +11,7 @@ "method": "standard" }, "alpha": 0.03, - "fill_value": -99990.0 + "fill_value": -99999.0 } }, "footprinter": "forge-py" From 0e7a1ecb74b074454a99cd5b2cf6915fa1bfd817 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Fri, 20 Dec 2024 09:37:10 -0800 Subject: [PATCH 25/25] remove old algorithm folder --- .../old_algorithm/AVHRR19_G-NAVO-L2P-v1.0.cfg | 74 ------------------ .../AVHRRMTA_G-NAVO-L2P-v1.0.cfg | 66 ---------------- .../AVHRRMTA_G-NAVO-L2P-v2.0.cfg | 75 ------------------ .../AVHRRMTA_G-NAVO-L2P-v2.0.cfg.1 | 75 ------------------ .../AVHRRMTB_G-NAVO-L2P-v2.0.cfg | 78 ------------------- archive/old_algorithm/PRIM_SMAP_L2_V1.cfg | 58 -------------- archive/old_algorithm/PRIM_SMAP_L2_V1.cfg.1 | 58 -------------- archive/old_algorithm/SMAP_RSS_L2_SSS_V6.cfg | 44 ----------- 8 files changed, 528 deletions(-) delete mode 100644 archive/old_algorithm/AVHRR19_G-NAVO-L2P-v1.0.cfg delete mode 100644 archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v1.0.cfg delete mode 100644 archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg delete mode 100644 archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg.1 delete mode 100644 archive/old_algorithm/AVHRRMTB_G-NAVO-L2P-v2.0.cfg delete mode 100644 archive/old_algorithm/PRIM_SMAP_L2_V1.cfg delete mode 100644 archive/old_algorithm/PRIM_SMAP_L2_V1.cfg.1 delete mode 100644 archive/old_algorithm/SMAP_RSS_L2_SSS_V6.cfg diff --git a/archive/old_algorithm/AVHRR19_G-NAVO-L2P-v1.0.cfg b/archive/old_algorithm/AVHRR19_G-NAVO-L2P-v1.0.cfg deleted file mode 100644 index b96e4c8..0000000 --- a/archive/old_algorithm/AVHRR19_G-NAVO-L2P-v1.0.cfg +++ /dev/null @@ -1,74 +0,0 @@ -{ - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": false, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "findValid": true, - "strategy": "polar", - "b": "*:*,0:*", - "s2": "0:*,*:*", - "t": "0:0,0:*", - "s1": "0:*,0:0" - }, - "imgVariables": [ - { - "id": "sea_surface_temperature", - "title": "sea surface sub-skin temperature", - "units": "K", - "min": "275", - "max": "305", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_bias", - "title": "SSES bias", - "units": "K", - "min": "-1.25", - "max": "1.25", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_standard_deviation", - "title": "SSES standard deviation", - "units": "K", - "min": "-0.5", - "max": "2", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "wind_speed", - "title": "Wind Speed", - "units": "m s-1", - "min": "0", - "max": "30", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "dt_analysis", - "title": "deviation from sst reference climatology", - "units": "K", - "min": "-12.5", - "max": "12.5", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "quality_level", - "title": "quality_level", - "units": "", - "min": "1", - "max": "5", - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 4, - "res": 8 - } -} diff --git a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v1.0.cfg b/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v1.0.cfg deleted file mode 100644 index 378e46f..0000000 --- a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v1.0.cfg +++ /dev/null @@ -1,66 +0,0 @@ -{ - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": false, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "findValid": true, - "strategy": "polarsides", - "b": "*:*,0:*", - "s2": "0:*,*:*", - "t": "0:0,0:*", - "s1": "0:*,0:0" - }, - "imgVariables": [ - { - "id": "sea_surface_temperature", - "title": "sea surface sub-skin temperature", - "units": "K", - "min": "275", - "max": "305", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_bias", - "title": "SSES bias", - "units": "K", - "min": "-1.25", - "max": "1.25", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_standard_deviation", - "title": "SSES standard deviation", - "units": "K", - "min": "-0.5", - "max": "2", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "dt_analysis", - "title": "deviation from sst reference climatology", - "units": "K", - "min": "-12.5", - "max": "12.5", - "palette": "paletteMedspirationIndexed" - }, - { - "id": "quality_level", - "title": "quality_level", - "units": "", - "min": "1", - "max": "5", - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 4, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg b/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg deleted file mode 100644 index dda79be..0000000 --- a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg +++ /dev/null @@ -1,75 +0,0 @@ -{ - "shortName": "AVHRRMTA_G-NAVO-L2P-v2.0", - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "findValid": true, - "strategy": "polarsides", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" - }, - "imgVariables": [ - { - "id": "sea_surface_temperature", - "title": "sea water temperature at 1 meter depth", - "units": "kelvin", - "min": 275.0, - "max": 305.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_bias", - "title": "SSES bias error", - "units": "kelvin", - "min": -1.25, - "max": 1.25, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_standard_deviation", - "title": "SSES standard deviation error", - "units": "kelvin", - "min": -0.5, - "max": 2.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "dt_analysis", - "title": "deviation from sst reference climatology", - "units": "kelvin", - "min": -12.5, - "max": 12.5, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "l2p_flags", - "title": "L2P flags", - "units": "", - "min": 0.0, - "max": 512.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "quality_level", - "title": "quality level of SST pixel", - "units": "", - "min": 0.0, - "max": 5.0, - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 16, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg.1 b/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg.1 deleted file mode 100644 index dda79be..0000000 --- a/archive/old_algorithm/AVHRRMTA_G-NAVO-L2P-v2.0.cfg.1 +++ /dev/null @@ -1,75 +0,0 @@ -{ - "shortName": "AVHRRMTA_G-NAVO-L2P-v2.0", - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "findValid": true, - "strategy": "polarsides", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" - }, - "imgVariables": [ - { - "id": "sea_surface_temperature", - "title": "sea water temperature at 1 meter depth", - "units": "kelvin", - "min": 275.0, - "max": 305.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_bias", - "title": "SSES bias error", - "units": "kelvin", - "min": -1.25, - "max": 1.25, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_standard_deviation", - "title": "SSES standard deviation error", - "units": "kelvin", - "min": -0.5, - "max": 2.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "dt_analysis", - "title": "deviation from sst reference climatology", - "units": "kelvin", - "min": -12.5, - "max": 12.5, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "l2p_flags", - "title": "L2P flags", - "units": "", - "min": 0.0, - "max": 512.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "quality_level", - "title": "quality level of SST pixel", - "units": "", - "min": 0.0, - "max": 5.0, - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 16, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/AVHRRMTB_G-NAVO-L2P-v2.0.cfg b/archive/old_algorithm/AVHRRMTB_G-NAVO-L2P-v2.0.cfg deleted file mode 100644 index c312e63..0000000 --- a/archive/old_algorithm/AVHRRMTB_G-NAVO-L2P-v2.0.cfg +++ /dev/null @@ -1,78 +0,0 @@ -{ - "shortName": "AVHRRMTB_G-NAVO-L2P-v2.0", - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "findValid": true, - "strategy": "polarsides", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" - }, - "imgVariables": [ - { - "id": "sea_surface_temperature", - "title": "sea water temperature at 1 meter depth", - "units": "kelvin", - "min": 275.0, - "max": 305.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_bias", - "title": "SSES bias error", - "units": "kelvin", - "min": -1.25, - "max": 1.25, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "sses_standard_deviation", - "title": "SSES standard deviation error", - "units": "kelvin", - "min": -0.5, - "max": 2.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "dt_analysis", - "title": "deviation from sst reference climatology", - "units": "kelvin", - "min": -12.5, - "max": 12.5, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "l2p_flags", - "title": "L2P flags", - "units": "", - "min": 0.0, - "max": 512.0, - "palette": "paletteMedspirationIndexed", - "fill_missing": true, - "fill_value": 2048, - "ppd": 8 - }, - { - "id": "quality_level", - "title": "quality level of SST pixel", - "units": "", - "min": 0.0, - "max": 5.0, - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 16, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg b/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg deleted file mode 100644 index ed1288f..0000000 --- a/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg +++ /dev/null @@ -1,58 +0,0 @@ -{ - "shortName": "PRIM_SMAP_L2_V1", - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "strategy": "periodic", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" - }, - "imgVariables": [ - { - "id": "PRIM_S0", - "title": "Sea surface salinity estimated using PRIM", - "units": "1e-3", - "min": 30.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PRIM_S1m", - "title": "Salinity at 1 meter depth estimated using RIM", - "units": "1e-3", - "min": 0.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PRIM_S5m", - "title": "Salinity at 5 meters depth estimated using RIM", - "units": "1e-3", - "min": 0.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PSS", - "title": "Probability of Salinity Stratification", - "units": "1", - "min": 0.0, - "max": 1.0, - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 4, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg.1 b/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg.1 deleted file mode 100644 index ed1288f..0000000 --- a/archive/old_algorithm/PRIM_SMAP_L2_V1.cfg.1 +++ /dev/null @@ -1,58 +0,0 @@ -{ - "shortName": "PRIM_SMAP_L2_V1", - "latVar": "lat", - "lonVar": "lon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "strategy": "periodic", - "t": "0:0,0:*", - "s1": "0:*,0:0", - "b": "*:*,0:*", - "s2": "0:*,*:*" - }, - "imgVariables": [ - { - "id": "PRIM_S0", - "title": "Sea surface salinity estimated using PRIM", - "units": "1e-3", - "min": 30.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PRIM_S1m", - "title": "Salinity at 1 meter depth estimated using RIM", - "units": "1e-3", - "min": 0.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PRIM_S5m", - "title": "Salinity at 5 meters depth estimated using RIM", - "units": "1e-3", - "min": 0.0, - "max": 40.0, - "palette": "paletteMedspirationIndexed" - }, - { - "id": "PSS", - "title": "Probability of Salinity Stratification", - "units": "1", - "min": 0.0, - "max": 1.0, - "palette": "paletteMedspirationIndexed" - } - ], - "image": { - "ppd": 4, - "res": 8 - } -} \ No newline at end of file diff --git a/archive/old_algorithm/SMAP_RSS_L2_SSS_V6.cfg b/archive/old_algorithm/SMAP_RSS_L2_SSS_V6.cfg deleted file mode 100644 index dbd6945..0000000 --- a/archive/old_algorithm/SMAP_RSS_L2_SSS_V6.cfg +++ /dev/null @@ -1,44 +0,0 @@ -{ - "shortName": "SMAP_RSS_L2_SSS_V6", - "latVar": "cellat", - "lonVar": "cellon", - "timeVar": "time", - "is360": true, - "tiles": { - "steps": [ - 30, - 14 - ] - }, - "footprint": { - "strategy": "smap", - "t": "0:0,0:*,0:0", - "s1": "0:*,0:0,0:0", - "b": "*:*,0:*,0:0", - "s2": "0:*,*:*,0:0" - }, - "imgVariables": [ - { - "id": "sss_smap", - "title": "SMAP sea surface salinity smoothed to approx 70km resolution", - "units": "1e-3", - "min": 30.0, - "max": 40.0, - "palette": "palette_AQUARIUS_SSS", - "fill_missing": true - }, - { - "id": "sss_smap_unc", - "title": "total formal uncertainty estimate of SMAP sea surface salinity smoothed to approx 70km resolution", - "units": "1e-3", - "min": 0.0, - "max": 40.0, - "palette": "palette_AQUARIUS_SSS", - "fill_missing": true - } - ], - "image": { - "ppd": 4, - "res": 8 - } -} \ No newline at end of file