Преглед на файлове

Merge pull request #1 from primap-community/download-dataset

download script
crdanielbusch преди 5 месеца
родител
ревизия
b30b209547
променени са 95 файла, в които са добавени 1472 реда и са изтрити 196 реда
  1. 1 0
      .gitattributes
  2. 1 1
      .github/actions/setup/action.yml
  3. 3 3
      .pre-commit-config.yaml
  4. 5 0
      Makefile
  5. 1 0
      changelog/1.feature.md
  6. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_All_Data.csv
  7. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_All_Data_NOFLAG.csv
  8. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_AreaCodes.csv
  9. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_Flags.csv
  10. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/GN_2023Oct_Final.pdf
  11. 1 0
      downloaded_data/farm_gate_agriculture_energy/2023-12-13/farm_gate_agriculture_energy.zip
  12. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_All_Data.csv
  13. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_All_Data_NOFLAG.csv
  14. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_AreaCodes.csv
  15. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_Elements.csv
  16. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_Flags.csv
  17. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_ItemCodes.csv
  18. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/GN_2023Oct_Final.pdf
  19. 1 0
      downloaded_data/farm_gate_agriculture_energy/2024-11-14/farm_gate_agriculture_energy.zip
  20. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_All_Data.csv
  21. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_All_Data_NOFLAG.csv
  22. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_AreaCodes.csv
  23. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_Flags.csv
  24. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_ItemCodes.csv
  25. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/GCE_e.pdf
  26. 1 0
      downloaded_data/farm_gate_emissions_crops/2023-11-09/farm_gate_emissions_crops.zip
  27. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_All_Data.csv
  28. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_All_Data_NOFLAG.csv
  29. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_AreaCodes.csv
  30. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Elements.csv
  31. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Flags.csv
  32. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_ItemCodes.csv
  33. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Sources.csv
  34. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/GCE_e.pdf
  35. 1 0
      downloaded_data/farm_gate_emissions_crops/2024-11-14/farm_gate_emissions_crops.zip
  36. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_All_Data.csv
  37. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_All_Data_NOFLAG.csv
  38. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_AreaCodes.csv
  39. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_Flags.csv
  40. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_ItemCodes.csv
  41. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/GLE_e.pdf
  42. 1 0
      downloaded_data/farm_gate_livestock/2023-11-09/farm_gate_livestock.zip
  43. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_All_Data.csv
  44. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_All_Data_NOFLAG.csv
  45. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_AreaCodes.csv
  46. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Elements.csv
  47. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Flags.csv
  48. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_ItemCodes.csv
  49. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Sources.csv
  50. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/GLE_e.pdf
  51. 1 0
      downloaded_data/farm_gate_livestock/2024-11-14/farm_gate_livestock.zip
  52. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_All_Data.csv
  53. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_All_Data_NOFLAG.csv
  54. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_AreaCodes.csv
  55. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_Flags.csv
  56. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_ItemCodes.csv
  57. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/GV_e.pdf
  58. 1 0
      downloaded_data/land_use_drained_organic_soils/2023-11-09/land_use_drained_organic_soils.zip
  59. 1 0
      downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_All_Data.csv
  60. 1 0
      downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_All_Data_NOFLAG.csv
  61. 1 0
      downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_AreaCodes.csv
  62. 1 0
      downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_Flags.csv
  63. 1 0
      downloaded_data/land_use_fires/2023-11-09/GI_e.pdf
  64. 1 0
      downloaded_data/land_use_fires/2023-11-09/land_use_fires.zip
  65. 1 0
      downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_All_Data.csv
  66. 1 0
      downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_All_Data_NOFLAG.csv
  67. 1 0
      downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_AreaCodes.csv
  68. 1 0
      downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_Flags.csv
  69. 1 0
      downloaded_data/land_use_forests/2023-11-09/GF_e.pdf
  70. 1 0
      downloaded_data/land_use_forests/2023-11-09/land_use_forests.zip
  71. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_All_Data.csv
  72. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_All_Data_NOFLAG.csv
  73. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_AreaCodes.csv
  74. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Elements.csv
  75. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Flags.csv
  76. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_ItemCodes.csv
  77. 1 0
      downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Sources.csv
  78. 1 0
      downloaded_data/land_use_forests/2024-11-14/GF_e.pdf
  79. 1 0
      downloaded_data/land_use_forests/2024-11-14/land_use_forests.zip
  80. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_All_Data.csv
  81. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_All_Data_NOFLAG.csv
  82. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_AreaCodes.csv
  83. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_Flags.csv
  84. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/README_Methodological_Note.pdf
  85. 1 0
      downloaded_data/pre_post_agricultural_production/2023-11-09/pre_post_agricultural_production.zip
  86. 368 189
      poetry.lock
  87. 7 2
      pyproject.toml
  88. 256 1
      requirements.txt
  89. 8 0
      scripts/download_all_domains.py
  90. 350 0
      src/faostat_data_primap/download.py
  91. 22 0
      src/faostat_data_primap/exceptions.py
  92. 1 0
      src/faostat_data_primap/helper/__init__.py
  93. 76 0
      src/faostat_data_primap/helper/definitions.py
  94. 32 0
      tests/integration/test_download_script.py
  95. 261 0
      tests/unit/test_download.py

+ 1 - 0
.gitattributes

@@ -2,3 +2,4 @@
 **/.git* annex.largefiles=nothing
 * annex.largefiles=((mimeencoding=binary)and(largerthan=0))
 *.csv annex.largefiles=anything
+*.zip annex.largefiles=anything

+ 1 - 1
.github/actions/setup/action.yml

@@ -36,7 +36,7 @@ runs:
       uses: actions/setup-python@v5
       with:
         python-version: ${{ inputs.python-version }}
-        cache: poetry
+#        cache: poetry
     - name: Set Poetry environment
       shell: bash
       run: |

+ 3 - 3
.pre-commit-config.yaml

@@ -14,7 +14,7 @@ repos:
       - id: check-case-conflict
       - id: check-json
       - id: check-merge-conflict
-      - id: check-symlinks
+#      - id: check-symlinks - DATALAD
       - id: check-yaml
       - id: debug-statements
       - id: detect-private-key
@@ -40,8 +40,8 @@ repos:
     rev: '1.8.3'
     hooks:
       - id: poetry-check
-      - id: poetry-lock
-        args: [--no-update]
+#      - id: poetry-lock
+#        args: [--no-update]
   - repo: https://github.com/python-poetry/poetry-plugin-export
     rev: '1.8.0'
     hooks:

+ 5 - 0
Makefile

@@ -77,3 +77,8 @@ virtual-environment:  ## update virtual environment, create a new one if it does
 	poetry config virtualenvs.in-project true
 	poetry install --all-extras
 	poetry run pre-commit install
+
+.PHONY: download_all_domains-environment
+download_all_domains:
+	# downloads and stages (datalad save) all available data
+	datalad run poetry run python3 scripts/download_all_domains.py

+ 1 - 0
changelog/1.feature.md

@@ -0,0 +1 @@
+Script to download data set from FAOSTAT website.

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/K2/xM/MD5E-s3747984--5c8e2441e4d635b94dbeca29d8a1cd0d.csv/MD5E-s3747984--5c8e2441e4d635b94dbeca29d8a1cd0d.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/1V/8w/MD5E-s2905364--66bf9d1c3aba04a26b1acc2554e7c2d5.csv/MD5E-s2905364--66bf9d1c3aba04a26b1acc2554e7c2d5.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ww/m4/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/Emissions_Agriculture_Energy_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ZJ/33/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/GN_2023Oct_Final.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/JK/1V/MD5E-s289407--8bb0d371bcfb2958de3106da9663edab.pdf/MD5E-s289407--8bb0d371bcfb2958de3106da9663edab.pdf

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2023-12-13/farm_gate_agriculture_energy.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/4m/p0/MD5E-s1131872--3a3329f2115c62bab08ba71183623db7.zip/MD5E-s1131872--3a3329f2115c62bab08ba71183623db7.zip

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/FG/Xz/MD5E-s2833079--d5379693f73e21b396a6d0a622d5206c.csv/MD5E-s2833079--d5379693f73e21b396a6d0a622d5206c.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/5Q/q0/MD5E-s2233481--ded47ea05e8fdf3d8072cca344e16720.csv/MD5E-s2233481--ded47ea05e8fdf3d8072cca344e16720.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/zq/54/MD5E-s6297--c1ea968e8385286d309e132b8e2611ac.csv/MD5E-s6297--c1ea968e8385286d309e132b8e2611ac.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_Elements.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/p5/z1/MD5E-s123--9d183d8cef4d0009b8d1be146868e866.csv/MD5E-s123--9d183d8cef4d0009b8d1be146868e866.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/x2/v1/MD5E-s56--f761b847385e4153242bfa46c3a54937.csv/MD5E-s56--f761b847385e4153242bfa46c3a54937.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/Emissions_Agriculture_Energy_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/67/Fx/MD5E-s28--d1671e41eed70275d48271edcd20f669.csv/MD5E-s28--d1671e41eed70275d48271edcd20f669.csv

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/GN_2023Oct_Final.pdf

@@ -0,0 +1 @@
+/Users/danielbusch/Documents/FAOSTAT_data_primap/downloaded_data/farm_gate_agriculture_energy/2023-12-13/GN_2023Oct_Final.pdf

+ 1 - 0
downloaded_data/farm_gate_agriculture_energy/2024-11-14/farm_gate_agriculture_energy.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/GZ/75/MD5E-s836564--9e55ba6b3ec0e593bdf2c13bf687d258.zip/MD5E-s836564--9e55ba6b3ec0e593bdf2c13bf687d258.zip

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/9X/27/MD5E-s18421900--06c176efdfe8eba45d3c55fe10f6d483.csv/MD5E-s18421900--06c176efdfe8eba45d3c55fe10f6d483.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/x2/Jq/MD5E-s13411273--09d470954a229689107df6f1c44d28b4.csv/MD5E-s13411273--09d470954a229689107df6f1c44d28b4.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ww/m4/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/gW/QQ/MD5E-s134--7852035bf48ab964ef4b9a62152be7dc.csv/MD5E-s134--7852035bf48ab964ef4b9a62152be7dc.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/Emissions_crops_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/W7/5Z/MD5E-s272--615170d0f5f83341b7a3783c59dfe51b.csv/MD5E-s272--615170d0f5f83341b7a3783c59dfe51b.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/GCE_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/9p/78/MD5E-s345124--d1210a945203a52677128d6e2c59fb20.pdf/MD5E-s345124--d1210a945203a52677128d6e2c59fb20.pdf

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2023-11-09/farm_gate_emissions_crops.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/mG/4x/MD5E-s5025708--d6b35891c494f61bb1699f669611a959.zip/MD5E-s5025708--d6b35891c494f61bb1699f669611a959.zip

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/W3/j5/MD5E-s15709372--efbc2f4aa1bed4b98c59b333dca0c3a7.csv/MD5E-s15709372--efbc2f4aa1bed4b98c59b333dca0c3a7.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/9k/50/MD5E-s11432144--17d3c345b7872204268e271a3e06a913.csv/MD5E-s11432144--17d3c345b7872204268e271a3e06a913.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/zq/54/MD5E-s6297--c1ea968e8385286d309e132b8e2611ac.csv/MD5E-s6297--c1ea968e8385286d309e132b8e2611ac.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Elements.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/v1/6G/MD5E-s1023--8197093ff96a52327679a762ffd1bb9e.csv/MD5E-s1023--8197093ff96a52327679a762ffd1bb9e.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/9q/m6/MD5E-s174--6535d28b265ce2c88c0c89d6319cb894.csv/MD5E-s174--6535d28b265ce2c88c0c89d6319cb894.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/qP/FQ/MD5E-s316--1c54c2a7a3a0c9d85566c671b4fb0899.csv/MD5E-s316--1c54c2a7a3a0c9d85566c671b4fb0899.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/Emissions_crops_E_Sources.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/4M/P5/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/GCE_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/ww/49/MD5E-s341239--058c8232d7a6b34966f2cc9f46f8b697.pdf/MD5E-s341239--058c8232d7a6b34966f2cc9f46f8b697.pdf

+ 1 - 0
downloaded_data/farm_gate_emissions_crops/2024-11-14/farm_gate_emissions_crops.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/5F/0K/MD5E-s4761143--8efa3d3ee700fb1464339b39509d1e61.zip/MD5E-s4761143--8efa3d3ee700fb1464339b39509d1e61.zip

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/Jk/Z7/MD5E-s130221694--993f534b79c1479b4487a34d48d05948.csv/MD5E-s130221694--993f534b79c1479b4487a34d48d05948.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/P0/9j/MD5E-s96060625--9f782ebc87a0dc22ed20f488dc9aae0e.csv/MD5E-s96060625--9f782ebc87a0dc22ed20f488dc9aae0e.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/4P/vG/MD5E-s6251--d86a24d9bb5f28127ff8970b7c033be9.csv/MD5E-s6251--d86a24d9bb5f28127ff8970b7c033be9.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/gW/QQ/MD5E-s134--7852035bf48ab964ef4b9a62152be7dc.csv/MD5E-s134--7852035bf48ab964ef4b9a62152be7dc.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/Emissions_livestock_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/GP/ZJ/MD5E-s611--12df116591f7c201ca38e86a76846d24.csv/MD5E-s611--12df116591f7c201ca38e86a76846d24.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/GLE_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/w6/pm/MD5E-s405033--f82222b4fccfdfe14b8bba56c2dd698b.pdf/MD5E-s405033--f82222b4fccfdfe14b8bba56c2dd698b.pdf

+ 1 - 0
downloaded_data/farm_gate_livestock/2023-11-09/farm_gate_livestock.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/1M/0X/MD5E-s33910537--28bb5f9131517238e7a112e6871d5898.zip/MD5E-s33910537--28bb5f9131517238e7a112e6871d5898.zip

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/pM/j9/MD5E-s135665835--859a3cc8e622c6afa7e62e423d36ff2a.csv/MD5E-s135665835--859a3cc8e622c6afa7e62e423d36ff2a.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/w4/50/MD5E-s99757421--26d3484db180077770b4018394ec2b57.csv/MD5E-s99757421--26d3484db180077770b4018394ec2b57.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/Gv/6V/MD5E-s6257--b8ba805ea08ab9cb01e03000b31fb20d.csv/MD5E-s6257--b8ba805ea08ab9cb01e03000b31fb20d.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Elements.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/5g/9w/MD5E-s1391--fe694fb3c0362af2cd8762b86aa93517.csv/MD5E-s1391--fe694fb3c0362af2cd8762b86aa93517.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/5X/mG/MD5E-s136--3c4ccc7a672ea49a488fccd25b0f2c2e.csv/MD5E-s136--3c4ccc7a672ea49a488fccd25b0f2c2e.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/9m/49/MD5E-s614--3fe76b01eac239ba75c71e713e9e5f21.csv/MD5E-s614--3fe76b01eac239ba75c71e713e9e5f21.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/Emissions_livestock_E_Sources.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/4M/P5/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/GLE_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/GV/wm/MD5E-s400796--bd0848d93b80c90352d5db712114e473.pdf/MD5E-s400796--bd0848d93b80c90352d5db712114e473.pdf

+ 1 - 0
downloaded_data/farm_gate_livestock/2024-11-14/farm_gate_livestock.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/xM/PF/MD5E-s34856727--f7d804f983f2133c8020467045757d2b.zip/MD5E-s34856727--f7d804f983f2133c8020467045757d2b.zip

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/99/M8/MD5E-s2243842--559f478afc2a52862be39d60763caef5.csv/MD5E-s2243842--559f478afc2a52862be39d60763caef5.csv

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/WZ/34/MD5E-s1666995--fcefba6724146fdbcdb43f0d0d4b79e3.csv/MD5E-s1666995--fcefba6724146fdbcdb43f0d0d4b79e3.csv

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ww/m4/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ZJ/33/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/Emissions_Drained_Organic_Soils_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/Vg/XG/MD5E-s25--91eb7aa7ff09282acaf3300f619b8934.csv/MD5E-s25--91eb7aa7ff09282acaf3300f619b8934.csv

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/GV_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/m3/Gx/MD5E-s380874--a449b317052119d47931b6174e38de8c.pdf/MD5E-s380874--a449b317052119d47931b6174e38de8c.pdf

+ 1 - 0
downloaded_data/land_use_drained_organic_soils/2023-11-09/land_use_drained_organic_soils.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/FK/vp/MD5E-s387900--a022e0142fa658793302f93ce4820f51.zip/MD5E-s387900--a022e0142fa658793302f93ce4820f51.zip

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/X6/F8/MD5E-s8494656--8dd8d98e2b93871d065d3ab61cbbb2d0.csv/MD5E-s8494656--8dd8d98e2b93871d065d3ab61cbbb2d0.csv

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/0j/QW/MD5E-s6357447--b13bfb628607550945857dff7ab22a90.csv/MD5E-s6357447--b13bfb628607550945857dff7ab22a90.csv

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ww/m4/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv/MD5E-s6291--0c7df737cb3e007c75cbc38ab03cb1ae.csv

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/Emissions_Land_Use_Fires_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ZJ/33/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/GI_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/XK/gQ/MD5E-s308062--fdb50185d75c22aaa27a01c797e8c8ac.pdf/MD5E-s308062--fdb50185d75c22aaa27a01c797e8c8ac.pdf

+ 1 - 0
downloaded_data/land_use_fires/2023-11-09/land_use_fires.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/8j/pF/MD5E-s1749124--dc55869803658e9cd776b60ae24107eb.zip/MD5E-s1749124--dc55869803658e9cd776b60ae24107eb.zip

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/67/69/MD5E-s1110885--79f06db2c0b50d9d6262ad87e9981db7.csv/MD5E-s1110885--79f06db2c0b50d9d6262ad87e9981db7.csv

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/K5/XV/MD5E-s806494--d3d276b80c5dbe86be357eacb9ce1869.csv/MD5E-s806494--d3d276b80c5dbe86be357eacb9ce1869.csv

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/8F/gx/MD5E-s6442--bb95e21597ca3104fc033daa29e87e2c.csv/MD5E-s6442--bb95e21597ca3104fc033daa29e87e2c.csv

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/Emissions_Land_Use_Forests_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ZJ/33/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv/MD5E-s56--49ffa55879a2eb0ff6dba98c17944376.csv

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/GF_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/P3/GF/MD5E-s366469--5cbedb33ed921167503f29e1b529a9d1.pdf/MD5E-s366469--5cbedb33ed921167503f29e1b529a9d1.pdf

+ 1 - 0
downloaded_data/land_use_forests/2023-11-09/land_use_forests.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/km/WQ/MD5E-s177332--a1265221e2763f2048a45f727864166e.zip/MD5E-s177332--a1265221e2763f2048a45f727864166e.zip

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/Fp/7x/MD5E-s1357857--a55e1f97e5fa35ce41a6430c77acdba0.csv/MD5E-s1357857--a55e1f97e5fa35ce41a6430c77acdba0.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/J3/X5/MD5E-s993644--37f442cddf8ec1b97a9aabf6cf9ef994.csv/MD5E-s993644--37f442cddf8ec1b97a9aabf6cf9ef994.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/FM/w1/MD5E-s6449--880a3bd89bb6bed3772af842c78b8bca.csv/MD5E-s6449--880a3bd89bb6bed3772af842c78b8bca.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Elements.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/xG/Km/MD5E-s85--a2a47ebbe4199ed40f385e380bf28ef8.csv/MD5E-s85--a2a47ebbe4199ed40f385e380bf28ef8.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/q6/XX/MD5E-s58--49a2c1099f6d1aa736e9680001c8d767.csv/MD5E-s58--49a2c1099f6d1aa736e9680001c8d767.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_ItemCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/67/Fx/MD5E-s28--d1671e41eed70275d48271edcd20f669.csv/MD5E-s28--d1671e41eed70275d48271edcd20f669.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/Emissions_Land_Use_Forests_E_Sources.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/4M/P5/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv/MD5E-s52--912f83275943beae43d78606c2ca4fd5.csv

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/GF_e.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/KK/F6/MD5E-s364308--34a99dffd2bb038d7831610d4aa5cccb.pdf/MD5E-s364308--34a99dffd2bb038d7831610d4aa5cccb.pdf

+ 1 - 0
downloaded_data/land_use_forests/2024-11-14/land_use_forests.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/zp/MM/MD5E-s219011--07b1607db15a6a44739151e0d4cba43e.zip/MD5E-s219011--07b1607db15a6a44739151e0d4cba43e.zip

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_All_Data.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/F2/2V/MD5E-s9990445--1884fe626fe79701aef21dd62a7d999b.csv/MD5E-s9990445--1884fe626fe79701aef21dd62a7d999b.csv

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_All_Data_NOFLAG.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/ZW/65/MD5E-s7957490--02212a350da3de62f437750ff2190da7.csv/MD5E-s7957490--02212a350da3de62f437750ff2190da7.csv

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_AreaCodes.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/5F/x7/MD5E-s6898--e6afc975758367037e97b7a51601eef6.csv/MD5E-s6898--e6afc975758367037e97b7a51601eef6.csv

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/Emissions_Pre_Post_Production_E_Flags.csv

@@ -0,0 +1 @@
+../../../.git/annex/objects/8f/5K/MD5E-s37--c802d00a3f6701103f12b45fd77c936d.csv/MD5E-s37--c802d00a3f6701103f12b45fd77c936d.csv

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/README_Methodological_Note.pdf

@@ -0,0 +1 @@
+../../../.git/annex/objects/28/j9/MD5E-s950199--b540bc153f62f6e3c17d79fdde2802f6.pdf/MD5E-s950199--b540bc153f62f6e3c17d79fdde2802f6.pdf

+ 1 - 0
downloaded_data/pre_post_agricultural_production/2023-11-09/pre_post_agricultural_production.zip

@@ -0,0 +1 @@
+../../../.git/annex/objects/0z/JM/MD5E-s4159211--d529d764b672c1b778d13a7ca2cc3d13.zip/MD5E-s4159211--d529d764b672c1b778d13a7ca2cc3d13.zip

+ 368 - 189
poetry.lock

@@ -448,37 +448,37 @@ toml = ["tomli"]
 
 [[package]]
 name = "debugpy"
-version = "1.8.7"
+version = "1.8.8"
 description = "An implementation of the Debug Adapter Protocol for Python"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "debugpy-1.8.7-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95fe04a573b8b22896c404365e03f4eda0ce0ba135b7667a1e57bd079793b96b"},
-    {file = "debugpy-1.8.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:628a11f4b295ffb4141d8242a9bb52b77ad4a63a2ad19217a93be0f77f2c28c9"},
-    {file = "debugpy-1.8.7-cp310-cp310-win32.whl", hash = "sha256:85ce9c1d0eebf622f86cc68618ad64bf66c4fc3197d88f74bb695a416837dd55"},
-    {file = "debugpy-1.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:29e1571c276d643757ea126d014abda081eb5ea4c851628b33de0c2b6245b037"},
-    {file = "debugpy-1.8.7-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:caf528ff9e7308b74a1749c183d6808ffbedbb9fb6af78b033c28974d9b8831f"},
-    {file = "debugpy-1.8.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba1d078cf2e1e0b8402e6bda528bf8fda7ccd158c3dba6c012b7897747c41a0"},
-    {file = "debugpy-1.8.7-cp311-cp311-win32.whl", hash = "sha256:171899588bcd412151e593bd40d9907133a7622cd6ecdbdb75f89d1551df13c2"},
-    {file = "debugpy-1.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:6e1c4ffb0c79f66e89dfd97944f335880f0d50ad29525dc792785384923e2211"},
-    {file = "debugpy-1.8.7-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:4d27d842311353ede0ad572600c62e4bcd74f458ee01ab0dd3a1a4457e7e3706"},
-    {file = "debugpy-1.8.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c1fd62ae0356e194f3e7b7a92acd931f71fe81c4b3be2c17a7b8a4b546ec2"},
-    {file = "debugpy-1.8.7-cp312-cp312-win32.whl", hash = "sha256:2f729228430ef191c1e4df72a75ac94e9bf77413ce5f3f900018712c9da0aaca"},
-    {file = "debugpy-1.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:45c30aaefb3e1975e8a0258f5bbd26cd40cde9bfe71e9e5a7ac82e79bad64e39"},
-    {file = "debugpy-1.8.7-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:d050a1ec7e925f514f0f6594a1e522580317da31fbda1af71d1530d6ea1f2b40"},
-    {file = "debugpy-1.8.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f4349a28e3228a42958f8ddaa6333d6f8282d5edaea456070e48609c5983b7"},
-    {file = "debugpy-1.8.7-cp313-cp313-win32.whl", hash = "sha256:11ad72eb9ddb436afb8337891a986302e14944f0f755fd94e90d0d71e9100bba"},
-    {file = "debugpy-1.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:2efb84d6789352d7950b03d7f866e6d180284bc02c7e12cb37b489b7083d81aa"},
-    {file = "debugpy-1.8.7-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:4b908291a1d051ef3331484de8e959ef3e66f12b5e610c203b5b75d2725613a7"},
-    {file = "debugpy-1.8.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da8df5b89a41f1fd31503b179d0a84a5fdb752dddd5b5388dbd1ae23cda31ce9"},
-    {file = "debugpy-1.8.7-cp38-cp38-win32.whl", hash = "sha256:b12515e04720e9e5c2216cc7086d0edadf25d7ab7e3564ec8b4521cf111b4f8c"},
-    {file = "debugpy-1.8.7-cp38-cp38-win_amd64.whl", hash = "sha256:93176e7672551cb5281577cdb62c63aadc87ec036f0c6a486f0ded337c504596"},
-    {file = "debugpy-1.8.7-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:90d93e4f2db442f8222dec5ec55ccfc8005821028982f1968ebf551d32b28907"},
-    {file = "debugpy-1.8.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6db2a370e2700557a976eaadb16243ec9c91bd46f1b3bb15376d7aaa7632c81"},
-    {file = "debugpy-1.8.7-cp39-cp39-win32.whl", hash = "sha256:a6cf2510740e0c0b4a40330640e4b454f928c7b99b0c9dbf48b11efba08a8cda"},
-    {file = "debugpy-1.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:6a9d9d6d31846d8e34f52987ee0f1a904c7baa4912bf4843ab39dadf9b8f3e0d"},
-    {file = "debugpy-1.8.7-py2.py3-none-any.whl", hash = "sha256:57b00de1c8d2c84a61b90880f7e5b6deaf4c312ecbde3a0e8912f2a56c4ac9ae"},
-    {file = "debugpy-1.8.7.zip", hash = "sha256:18b8f731ed3e2e1df8e9cdaa23fb1fc9c24e570cd0081625308ec51c82efe42e"},
+    {file = "debugpy-1.8.8-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e59b1607c51b71545cb3496876544f7186a7a27c00b436a62f285603cc68d1c6"},
+    {file = "debugpy-1.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d"},
+    {file = "debugpy-1.8.8-cp310-cp310-win32.whl", hash = "sha256:b01f4a5e5c5fb1d34f4ccba99a20ed01eabc45a4684f4948b5db17a319dfb23f"},
+    {file = "debugpy-1.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:535f4fb1c024ddca5913bb0eb17880c8f24ba28aa2c225059db145ee557035e9"},
+    {file = "debugpy-1.8.8-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:c399023146e40ae373753a58d1be0a98bf6397fadc737b97ad612886b53df318"},
+    {file = "debugpy-1.8.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09cc7b162586ea2171eea055985da2702b0723f6f907a423c9b2da5996ad67ba"},
+    {file = "debugpy-1.8.8-cp311-cp311-win32.whl", hash = "sha256:eea8821d998ebeb02f0625dd0d76839ddde8cbf8152ebbe289dd7acf2cdc6b98"},
+    {file = "debugpy-1.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:d4483836da2a533f4b1454dffc9f668096ac0433de855f0c22cdce8c9f7e10c4"},
+    {file = "debugpy-1.8.8-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:0cc94186340be87b9ac5a707184ec8f36547fb66636d1029ff4f1cc020e53996"},
+    {file = "debugpy-1.8.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64674e95916e53c2e9540a056e5f489e0ad4872645399d778f7c598eacb7b7f9"},
+    {file = "debugpy-1.8.8-cp312-cp312-win32.whl", hash = "sha256:5c6e885dbf12015aed73770f29dec7023cb310d0dc2ba8bfbeb5c8e43f80edc9"},
+    {file = "debugpy-1.8.8-cp312-cp312-win_amd64.whl", hash = "sha256:19ffbd84e757a6ca0113574d1bf5a2298b3947320a3e9d7d8dc3377f02d9f864"},
+    {file = "debugpy-1.8.8-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:705cd123a773d184860ed8dae99becd879dfec361098edbefb5fc0d3683eb804"},
+    {file = "debugpy-1.8.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890fd16803f50aa9cb1a9b9b25b5ec321656dd6b78157c74283de241993d086f"},
+    {file = "debugpy-1.8.8-cp313-cp313-win32.whl", hash = "sha256:90244598214bbe704aa47556ec591d2f9869ff9e042e301a2859c57106649add"},
+    {file = "debugpy-1.8.8-cp313-cp313-win_amd64.whl", hash = "sha256:4b93e4832fd4a759a0c465c967214ed0c8a6e8914bced63a28ddb0dd8c5f078b"},
+    {file = "debugpy-1.8.8-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:143ef07940aeb8e7316de48f5ed9447644da5203726fca378f3a6952a50a9eae"},
+    {file = "debugpy-1.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95651bdcbfd3b27a408869a53fbefcc2bcae13b694daee5f1365b1b83a00113"},
+    {file = "debugpy-1.8.8-cp38-cp38-win32.whl", hash = "sha256:26b461123a030e82602a750fb24d7801776aa81cd78404e54ab60e8b5fecdad5"},
+    {file = "debugpy-1.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3cbf1833e644a3100eadb6120f25be8a532035e8245584c4f7532937edc652a"},
+    {file = "debugpy-1.8.8-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:53709d4ec586b525724819dc6af1a7703502f7e06f34ded7157f7b1f963bb854"},
+    {file = "debugpy-1.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a9c013077a3a0000e83d97cf9cc9328d2b0bbb31f56b0e99ea3662d29d7a6a2"},
+    {file = "debugpy-1.8.8-cp39-cp39-win32.whl", hash = "sha256:ffe94dd5e9a6739a75f0b85316dc185560db3e97afa6b215628d1b6a17561cb2"},
+    {file = "debugpy-1.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5c0e5a38c7f9b481bf31277d2f74d2109292179081f11108e668195ef926c0f9"},
+    {file = "debugpy-1.8.8-py2.py3-none-any.whl", hash = "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"},
+    {file = "debugpy-1.8.8.zip", hash = "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091"},
 ]
 
 [[package]]
@@ -658,15 +658,26 @@ files = [
 docs = ["Sphinx", "furo"]
 test = ["objgraph", "psutil"]
 
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+    {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
 [[package]]
 name = "identify"
-version = "2.6.1"
+version = "2.6.2"
 description = "File identification library for Python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 files = [
-    {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"},
-    {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"},
+    {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"},
+    {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"},
 ]
 
 [package.extras]
@@ -720,28 +731,6 @@ perf = ["ipython"]
 test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
 type = ["pytest-mypy"]
 
-[[package]]
-name = "importlib-resources"
-version = "6.4.5"
-description = "Read resources from Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
-    {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"},
-    {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"},
-]
-
-[package.dependencies]
-zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
-cover = ["pytest-cov"]
-doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-enabler = ["pytest-enabler (>=2.2)"]
-test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"]
-type = ["pytest-mypy"]
-
 [[package]]
 name = "incremental"
 version = "24.7.2"
@@ -806,13 +795,13 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio
 
 [[package]]
 name = "ipython"
-version = "8.18.1"
+version = "8.29.0"
 description = "IPython: Productive Interactive Computing"
 optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
 files = [
-    {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"},
-    {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"},
+    {file = "ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8"},
+    {file = "ipython-8.29.0.tar.gz", hash = "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"},
 ]
 
 [package.dependencies]
@@ -821,44 +810,45 @@ decorator = "*"
 exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
 jedi = ">=0.16"
 matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
 prompt-toolkit = ">=3.0.41,<3.1.0"
 pygments = ">=2.4.0"
 stack-data = "*"
-traitlets = ">=5"
-typing-extensions = {version = "*", markers = "python_version < \"3.10\""}
+traitlets = ">=5.13.0"
+typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
 
 [package.extras]
-all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
+all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
 black = ["black"]
-doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
+doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"]
 kernel = ["ipykernel"]
+matplotlib = ["matplotlib"]
 nbconvert = ["nbconvert"]
 nbformat = ["nbformat"]
 notebook = ["ipywidgets", "notebook"]
 parallel = ["ipyparallel"]
 qtconsole = ["qtconsole"]
-test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"]
-test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"]
+test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
 
 [[package]]
 name = "jedi"
-version = "0.19.1"
+version = "0.19.2"
 description = "An autocompletion tool for Python that can be used for text editors."
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
-    {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
+    {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
+    {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
 ]
 
 [package.dependencies]
-parso = ">=0.8.3,<0.9.0"
+parso = ">=0.8.4,<0.9.0"
 
 [package.extras]
 docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
 qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
+testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
 
 [[package]]
 name = "jinja2"
@@ -951,7 +941,6 @@ files = [
 ]
 
 [package.dependencies]
-importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""}
 jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
 python-dateutil = ">=2.8.2"
 pyzmq = ">=23.0"
@@ -1347,15 +1336,29 @@ files = [
     {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
 ]
 
+[[package]]
+name = "outcome"
+version = "1.3.0.post0"
+description = "Capture the outcome of Python function calls."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b"},
+    {file = "outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+
 [[package]]
 name = "packaging"
-version = "24.1"
+version = "24.2"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
-    {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
+    {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
+    {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
 ]
 
 [[package]]
@@ -1558,6 +1561,18 @@ files = [
 [package.extras]
 windows-terminal = ["colorama (>=0.4.6)"]
 
+[[package]]
+name = "pysocks"
+version = "1.7.1"
+description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
+    {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
+    {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
+]
+
 [[package]]
 name = "pytest"
 version = "7.4.4"
@@ -1612,6 +1627,20 @@ files = [
 [package.dependencies]
 six = ">=1.5"
 
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
+    {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
+]
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
 [[package]]
 name = "pywin32"
 version = "308"
@@ -1860,114 +1889,101 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 
 [[package]]
 name = "rpds-py"
-version = "0.20.1"
+version = "0.21.0"
 description = "Python bindings to Rust's persistent data structures (rpds)"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 files = [
-    {file = "rpds_py-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a649dfd735fff086e8a9d0503a9f0c7d01b7912a333c7ae77e1515c08c146dad"},
-    {file = "rpds_py-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f16bc1334853e91ddaaa1217045dd7be166170beec337576818461268a3de67f"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14511a539afee6f9ab492b543060c7491c99924314977a55c98bfa2ee29ce78c"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ccb8ac2d3c71cda472b75af42818981bdacf48d2e21c36331b50b4f16930163"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c142b88039b92e7e0cb2552e8967077e3179b22359e945574f5e2764c3953dcf"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f19169781dddae7478a32301b499b2858bc52fc45a112955e798ee307e294977"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13c56de6518e14b9bf6edde23c4c39dac5b48dcf04160ea7bce8fca8397cdf86"},
-    {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:925d176a549f4832c6f69fa6026071294ab5910e82a0fe6c6228fce17b0706bd"},
-    {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78f0b6877bfce7a3d1ff150391354a410c55d3cdce386f862926a4958ad5ab7e"},
-    {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3dd645e2b0dcb0fd05bf58e2e54c13875847687d0b71941ad2e757e5d89d4356"},
-    {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4f676e21db2f8c72ff0936f895271e7a700aa1f8d31b40e4e43442ba94973899"},
-    {file = "rpds_py-0.20.1-cp310-none-win32.whl", hash = "sha256:648386ddd1e19b4a6abab69139b002bc49ebf065b596119f8f37c38e9ecee8ff"},
-    {file = "rpds_py-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:d9ecb51120de61e4604650666d1f2b68444d46ae18fd492245a08f53ad2b7711"},
-    {file = "rpds_py-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:762703bdd2b30983c1d9e62b4c88664df4a8a4d5ec0e9253b0231171f18f6d75"},
-    {file = "rpds_py-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0b581f47257a9fce535c4567782a8976002d6b8afa2c39ff616edf87cbeff712"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842c19a6ce894493563c3bd00d81d5100e8e57d70209e84d5491940fdb8b9e3a"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42cbde7789f5c0bcd6816cb29808e36c01b960fb5d29f11e052215aa85497c93"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c8e9340ce5a52f95fa7d3b552b35c7e8f3874d74a03a8a69279fd5fca5dc751"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ba6f89cac95c0900d932c9efb7f0fb6ca47f6687feec41abcb1bd5e2bd45535"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a916087371afd9648e1962e67403c53f9c49ca47b9680adbeef79da3a7811b0"},
-    {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:200a23239781f46149e6a415f1e870c5ef1e712939fe8fa63035cd053ac2638e"},
-    {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58b1d5dd591973d426cbb2da5e27ba0339209832b2f3315928c9790e13f159e8"},
-    {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6b73c67850ca7cae0f6c56f71e356d7e9fa25958d3e18a64927c2d930859b8e4"},
-    {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d8761c3c891cc51e90bc9926d6d2f59b27beaf86c74622c8979380a29cc23ac3"},
-    {file = "rpds_py-0.20.1-cp311-none-win32.whl", hash = "sha256:cd945871335a639275eee904caef90041568ce3b42f402c6959b460d25ae8732"},
-    {file = "rpds_py-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:7e21b7031e17c6b0e445f42ccc77f79a97e2687023c5746bfb7a9e45e0921b84"},
-    {file = "rpds_py-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:36785be22066966a27348444b40389f8444671630063edfb1a2eb04318721e17"},
-    {file = "rpds_py-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:142c0a5124d9bd0e2976089484af5c74f47bd3298f2ed651ef54ea728d2ea42c"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbddc10776ca7ebf2a299c41a4dde8ea0d8e3547bfd731cb87af2e8f5bf8962d"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15a842bb369e00295392e7ce192de9dcbf136954614124a667f9f9f17d6a216f"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be5ef2f1fc586a7372bfc355986226484e06d1dc4f9402539872c8bb99e34b01"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbcf360c9e3399b056a238523146ea77eeb2a596ce263b8814c900263e46031a"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd27a66740ffd621d20b9a2f2b5ee4129a56e27bfb9458a3bcc2e45794c96cb"},
-    {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0b937b2a1988f184a3e9e577adaa8aede21ec0b38320d6009e02bd026db04fa"},
-    {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6889469bfdc1eddf489729b471303739bf04555bb151fe8875931f8564309afc"},
-    {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19b73643c802f4eaf13d97f7855d0fb527fbc92ab7013c4ad0e13a6ae0ed23bd"},
-    {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c6afcf2338e7f374e8edc765c79fbcb4061d02b15dd5f8f314a4af2bdc7feb5"},
-    {file = "rpds_py-0.20.1-cp312-none-win32.whl", hash = "sha256:dc73505153798c6f74854aba69cc75953888cf9866465196889c7cdd351e720c"},
-    {file = "rpds_py-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:8bbe951244a838a51289ee53a6bae3a07f26d4e179b96fc7ddd3301caf0518eb"},
-    {file = "rpds_py-0.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6ca91093a4a8da4afae7fe6a222c3b53ee4eef433ebfee4d54978a103435159e"},
-    {file = "rpds_py-0.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b9c2fe36d1f758b28121bef29ed1dee9b7a2453e997528e7d1ac99b94892527c"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f009c69bc8c53db5dfab72ac760895dc1f2bc1b62ab7408b253c8d1ec52459fc"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6740a3e8d43a32629bb9b009017ea5b9e713b7210ba48ac8d4cb6d99d86c8ee8"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32b922e13d4c0080d03e7b62991ad7f5007d9cd74e239c4b16bc85ae8b70252d"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe00a9057d100e69b4ae4a094203a708d65b0f345ed546fdef86498bf5390982"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fe9b04b6fa685bd39237d45fad89ba19e9163a1ccaa16611a812e682913496"},
-    {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa7ac11e294304e615b43f8c441fee5d40094275ed7311f3420d805fde9b07b4"},
-    {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aa97af1558a9bef4025f8f5d8c60d712e0a3b13a2fe875511defc6ee77a1ab7"},
-    {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:483b29f6f7ffa6af845107d4efe2e3fa8fb2693de8657bc1849f674296ff6a5a"},
-    {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37fe0f12aebb6a0e3e17bb4cd356b1286d2d18d2e93b2d39fe647138458b4bcb"},
-    {file = "rpds_py-0.20.1-cp313-none-win32.whl", hash = "sha256:a624cc00ef2158e04188df5e3016385b9353638139a06fb77057b3498f794782"},
-    {file = "rpds_py-0.20.1-cp313-none-win_amd64.whl", hash = "sha256:b71b8666eeea69d6363248822078c075bac6ed135faa9216aa85f295ff009b1e"},
-    {file = "rpds_py-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5b48e790e0355865197ad0aca8cde3d8ede347831e1959e158369eb3493d2191"},
-    {file = "rpds_py-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3e310838a5801795207c66c73ea903deda321e6146d6f282e85fa7e3e4854804"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249280b870e6a42c0d972339e9cc22ee98730a99cd7f2f727549af80dd5a963"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e79059d67bea28b53d255c1437b25391653263f0e69cd7dec170d778fdbca95e"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b431c777c9653e569986ecf69ff4a5dba281cded16043d348bf9ba505486f36"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da584ff96ec95e97925174eb8237e32f626e7a1a97888cdd27ee2f1f24dd0ad8"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a0629ec053fc013808a85178524e3cb63a61dbc35b22499870194a63578fb9"},
-    {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fbf15aff64a163db29a91ed0868af181d6f68ec1a3a7d5afcfe4501252840bad"},
-    {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:07924c1b938798797d60c6308fa8ad3b3f0201802f82e4a2c41bb3fafb44cc28"},
-    {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4a5a844f68776a7715ecb30843b453f07ac89bad393431efbf7accca3ef599c1"},
-    {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:518d2ca43c358929bf08f9079b617f1c2ca6e8848f83c1225c88caeac46e6cbc"},
-    {file = "rpds_py-0.20.1-cp38-none-win32.whl", hash = "sha256:3aea7eed3e55119635a74bbeb80b35e776bafccb70d97e8ff838816c124539f1"},
-    {file = "rpds_py-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:7dca7081e9a0c3b6490a145593f6fe3173a94197f2cb9891183ef75e9d64c425"},
-    {file = "rpds_py-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b41b6321805c472f66990c2849e152aff7bc359eb92f781e3f606609eac877ad"},
-    {file = "rpds_py-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a90c373ea2975519b58dece25853dbcb9779b05cc46b4819cb1917e3b3215b6"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16d4477bcb9fbbd7b5b0e4a5d9b493e42026c0bf1f06f723a9353f5153e75d30"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84b8382a90539910b53a6307f7c35697bc7e6ffb25d9c1d4e998a13e842a5e83"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4888e117dd41b9d34194d9e31631af70d3d526efc363085e3089ab1a62c32ed1"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5265505b3d61a0f56618c9b941dc54dc334dc6e660f1592d112cd103d914a6db"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e75ba609dba23f2c95b776efb9dd3f0b78a76a151e96f96cc5b6b1b0004de66f"},
-    {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1791ff70bc975b098fe6ecf04356a10e9e2bd7dc21fa7351c1742fdeb9b4966f"},
-    {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d126b52e4a473d40232ec2052a8b232270ed1f8c9571aaf33f73a14cc298c24f"},
-    {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c14937af98c4cc362a1d4374806204dd51b1e12dded1ae30645c298e5a5c4cb1"},
-    {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3d089d0b88996df627693639d123c8158cff41c0651f646cd8fd292c7da90eaf"},
-    {file = "rpds_py-0.20.1-cp39-none-win32.whl", hash = "sha256:653647b8838cf83b2e7e6a0364f49af96deec64d2a6578324db58380cff82aca"},
-    {file = "rpds_py-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:fa41a64ac5b08b292906e248549ab48b69c5428f3987b09689ab2441f267d04d"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a07ced2b22f0cf0b55a6a510078174c31b6d8544f3bc00c2bcee52b3d613f74"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:68cb0a499f2c4a088fd2f521453e22ed3527154136a855c62e148b7883b99f9a"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3060d885657abc549b2a0f8e1b79699290e5d83845141717c6c90c2df38311"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95f3b65d2392e1c5cec27cff08fdc0080270d5a1a4b2ea1d51d5f4a2620ff08d"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cc3712a4b0b76a1d45a9302dd2f53ff339614b1c29603a911318f2357b04dd2"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d4eea0761e37485c9b81400437adb11c40e13ef513375bbd6973e34100aeb06"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f5179583d7a6cdb981151dd349786cbc318bab54963a192692d945dd3f6435d"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fbb0ffc754490aff6dabbf28064be47f0f9ca0b9755976f945214965b3ace7e"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a94e52537a0e0a85429eda9e49f272ada715506d3b2431f64b8a3e34eb5f3e75"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:92b68b79c0da2a980b1c4197e56ac3dd0c8a149b4603747c4378914a68706979"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:93da1d3db08a827eda74356f9f58884adb254e59b6664f64cc04cdff2cc19b0d"},
-    {file = "rpds_py-0.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:754bbed1a4ca48479e9d4182a561d001bbf81543876cdded6f695ec3d465846b"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ca449520e7484534a2a44faf629362cae62b660601432d04c482283c47eaebab"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9c4cb04a16b0f199a8c9bf807269b2f63b7b5b11425e4a6bd44bd6961d28282c"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63804105143c7e24cee7db89e37cb3f3941f8e80c4379a0b355c52a52b6780"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55cd1fa4ecfa6d9f14fbd97ac24803e6f73e897c738f771a9fe038f2f11ff07c"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f8f741b6292c86059ed175d80eefa80997125b7c478fb8769fd9ac8943a16c0"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fc212779bf8411667234b3cdd34d53de6c2b8b8b958e1e12cb473a5f367c338"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad56edabcdb428c2e33bbf24f255fe2b43253b7d13a2cdbf05de955217313e6"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a3a1e9ee9728b2c1734f65d6a1d376c6f2f6fdcc13bb007a08cc4b1ff576dc5"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e13de156137b7095442b288e72f33503a469aa1980ed856b43c353ac86390519"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:07f59760ef99f31422c49038964b31c4dfcfeb5d2384ebfc71058a7c9adae2d2"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:59240685e7da61fb78f65a9f07f8108e36a83317c53f7b276b4175dc44151684"},
-    {file = "rpds_py-0.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:83cba698cfb3c2c5a7c3c6bac12fe6c6a51aae69513726be6411076185a8b24a"},
-    {file = "rpds_py-0.20.1.tar.gz", hash = "sha256:e1791c4aabd117653530dccd24108fa03cc6baf21f58b950d0a73c3b3b29a350"},
+    {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]]
@@ -1996,6 +2012,25 @@ files = [
     {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"},
 ]
 
+[[package]]
+name = "selenium"
+version = "4.26.1"
+description = "Official Python bindings for Selenium WebDriver"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "selenium-4.26.1-py3-none-any.whl", hash = "sha256:1db3f3a0cd5bb07624fa8a3905a6fdde1595a42185a0617077c361dc53d104fb"},
+    {file = "selenium-4.26.1.tar.gz", hash = "sha256:7640f3f08ae7f4e450f895678e8a10a55eb4e4ca18311ed675ecc4684b96b683"},
+]
+
+[package.dependencies]
+certifi = ">=2021.10.8"
+trio = ">=0.17,<1.0"
+trio-websocket = ">=0.9,<1.0"
+typing_extensions = ">=4.9,<5.0"
+urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
+websocket-client = ">=1.8,<2.0"
+
 [[package]]
 name = "semantic-version"
 version = "2.10.0"
@@ -2037,6 +2072,17 @@ files = [
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 ]
 
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+description = "Sniff out which async library your code is running under"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
+    {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
+]
+
 [[package]]
 name = "snowballstemmer"
 version = "2.2.0"
@@ -2048,6 +2094,17 @@ files = [
     {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
 ]
 
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+optional = false
+python-versions = "*"
+files = [
+    {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+    {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
+]
+
 [[package]]
 name = "soupsieve"
 version = "2.6"
@@ -2076,7 +2133,6 @@ babel = ">=2.9"
 colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
 docutils = ">=0.14,<0.20"
 imagesize = ">=1.3"
-importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""}
 Jinja2 = ">=3.0"
 packaging = ">=21.0"
 Pygments = ">=2.12"
@@ -2399,13 +2455,13 @@ files = [
 
 [[package]]
 name = "tomli"
-version = "2.0.2"
+version = "2.1.0"
 description = "A lil' TOML parser"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
-    {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
+    {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"},
+    {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"},
 ]
 
 [[package]]
@@ -2441,7 +2497,6 @@ files = [
 
 [package.dependencies]
 click = "*"
-importlib-resources = {version = ">=5", markers = "python_version < \"3.10\""}
 incremental = "*"
 jinja2 = "*"
 tomli = {version = "*", markers = "python_version < \"3.11\""}
@@ -2464,6 +2519,81 @@ files = [
 docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
 test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
 
+[[package]]
+name = "trio"
+version = "0.27.0"
+description = "A friendly Python library for async concurrency and I/O"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"},
+    {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"},
+]
+
+[package.dependencies]
+attrs = ">=23.2.0"
+cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""}
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+idna = "*"
+outcome = "*"
+sniffio = ">=1.3.0"
+sortedcontainers = "*"
+
+[[package]]
+name = "trio-websocket"
+version = "0.11.1"
+description = "WebSocket library for Trio"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"},
+    {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"},
+]
+
+[package.dependencies]
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+trio = ">=0.11"
+wsproto = ">=0.14"
+
+[[package]]
+name = "types-beautifulsoup4"
+version = "4.12.0.20241020"
+description = "Typing stubs for beautifulsoup4"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "types-beautifulsoup4-4.12.0.20241020.tar.gz", hash = "sha256:158370d08d0cd448bd11b132a50ff5279237a5d4b5837beba074de152a513059"},
+    {file = "types_beautifulsoup4-4.12.0.20241020-py3-none-any.whl", hash = "sha256:c95e66ce15a4f5f0835f7fbc5cd886321ae8294f977c495424eaf4225307fd30"},
+]
+
+[package.dependencies]
+types-html5lib = "*"
+
+[[package]]
+name = "types-html5lib"
+version = "1.1.11.20241018"
+description = "Typing stubs for html5lib"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "types-html5lib-1.1.11.20241018.tar.gz", hash = "sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa"},
+    {file = "types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403"},
+]
+
+[[package]]
+name = "types-requests"
+version = "2.32.0.20241016"
+description = "Typing stubs for requests"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"},
+    {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"},
+]
+
+[package.dependencies]
+urllib3 = ">=2"
+
 [[package]]
 name = "typing-extensions"
 version = "4.12.2"
@@ -2486,6 +2616,9 @@ files = [
     {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
 ]
 
+[package.dependencies]
+pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
+
 [package.extras]
 brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
 h2 = ["h2 (>=4,<5)"]
@@ -2523,15 +2656,61 @@ files = [
     {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
 ]
 
+[[package]]
+name = "webdriver-manager"
+version = "4.0.2"
+description = "Library provides the way to automatically manage drivers for different browsers"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "webdriver_manager-4.0.2-py2.py3-none-any.whl", hash = "sha256:75908d92ecc45ff2b9953614459c633db8f9aa1ff30181cefe8696e312908129"},
+    {file = "webdriver_manager-4.0.2.tar.gz", hash = "sha256:efedf428f92fd6d5c924a0d054e6d1322dd77aab790e834ee767af392b35590f"},
+]
+
+[package.dependencies]
+packaging = "*"
+python-dotenv = "*"
+requests = "*"
+
+[[package]]
+name = "websocket-client"
+version = "1.8.0"
+description = "WebSocket client for Python with low level API options"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"},
+    {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"},
+]
+
+[package.extras]
+docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"]
+optional = ["python-socks", "wsaccel"]
+test = ["websockets"]
+
+[[package]]
+name = "wsproto"
+version = "1.2.0"
+description = "WebSockets state-machine based protocol implementation"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
+    {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"},
+]
+
+[package.dependencies]
+h11 = ">=0.9.0,<1"
+
 [[package]]
 name = "zipp"
-version = "3.20.2"
+version = "3.21.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 files = [
-    {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"},
-    {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"},
+    {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
+    {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
 ]
 
 [package.extras]
@@ -2544,5 +2723,5 @@ type = ["pytest-mypy"]
 
 [metadata]
 lock-version = "2.0"
-python-versions = "^3.9"
-content-hash = "84d0f5f18f3180f6c0d06814165a3e324251596b53bdd4bab905eb6855d21109"
+python-versions = "^3.10"
+content-hash = "41950ad66a4c310ab9ab2d6e9f6a2a34a90b41da51b74e866ec001c5d17d0247"

+ 7 - 2
pyproject.toml

@@ -9,7 +9,12 @@ license = "TBD"
 include = ["LICENCE"]  # poetry uses US English so assumes it will be spelt LICENSE
 
 [tool.poetry.dependencies]
-python = "^3.9"
+python = "^3.10"
+selenium = "^4.26.1"
+webdriver-manager = "^4.0.2"
+beautifulsoup4 = "^4.12.3"
+types-beautifulsoup4 = "^4.12.0.20241020"
+types-requests = "^2.32.0.20241016"
 
 
 [tool.poetry.group.tests.dependencies]
@@ -44,7 +49,7 @@ source = ["src"]
 branch = true
 
 [tool.coverage.report]
-fail_under = 95
+fail_under = 40
 skip_empty = true
 show_missing = true
 # Regexes for lines to exclude from consideration in addition to the defaults

+ 256 - 1
requirements.txt

@@ -1 +1,256 @@
-/annex/objects/MD5E-s1--68b329da9893e34099c7d8ad5cb9c940.txt
+attrs==24.2.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \
+    --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
+beautifulsoup4==4.12.3 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \
+    --hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed
+certifi==2024.8.30 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
+    --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
+cffi==1.17.1 ; os_name == "nt" and implementation_name != "pypy" and python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
+    --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
+    --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \
+    --hash=sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15 \
+    --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \
+    --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \
+    --hash=sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8 \
+    --hash=sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36 \
+    --hash=sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17 \
+    --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \
+    --hash=sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc \
+    --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \
+    --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \
+    --hash=sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702 \
+    --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \
+    --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \
+    --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \
+    --hash=sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6 \
+    --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \
+    --hash=sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b \
+    --hash=sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e \
+    --hash=sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be \
+    --hash=sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c \
+    --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \
+    --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \
+    --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \
+    --hash=sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8 \
+    --hash=sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1 \
+    --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \
+    --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \
+    --hash=sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67 \
+    --hash=sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595 \
+    --hash=sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0 \
+    --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \
+    --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \
+    --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \
+    --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \
+    --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \
+    --hash=sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3 \
+    --hash=sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16 \
+    --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \
+    --hash=sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e \
+    --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \
+    --hash=sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964 \
+    --hash=sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c \
+    --hash=sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576 \
+    --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \
+    --hash=sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3 \
+    --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \
+    --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \
+    --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \
+    --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \
+    --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \
+    --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \
+    --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \
+    --hash=sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14 \
+    --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \
+    --hash=sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9 \
+    --hash=sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7 \
+    --hash=sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382 \
+    --hash=sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a \
+    --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \
+    --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \
+    --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \
+    --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \
+    --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \
+    --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b
+charset-normalizer==3.4.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621 \
+    --hash=sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6 \
+    --hash=sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8 \
+    --hash=sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912 \
+    --hash=sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c \
+    --hash=sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b \
+    --hash=sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d \
+    --hash=sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d \
+    --hash=sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95 \
+    --hash=sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e \
+    --hash=sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565 \
+    --hash=sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64 \
+    --hash=sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab \
+    --hash=sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be \
+    --hash=sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e \
+    --hash=sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907 \
+    --hash=sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0 \
+    --hash=sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2 \
+    --hash=sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62 \
+    --hash=sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62 \
+    --hash=sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23 \
+    --hash=sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc \
+    --hash=sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284 \
+    --hash=sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca \
+    --hash=sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455 \
+    --hash=sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858 \
+    --hash=sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b \
+    --hash=sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594 \
+    --hash=sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc \
+    --hash=sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db \
+    --hash=sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b \
+    --hash=sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea \
+    --hash=sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6 \
+    --hash=sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920 \
+    --hash=sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749 \
+    --hash=sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7 \
+    --hash=sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd \
+    --hash=sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99 \
+    --hash=sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242 \
+    --hash=sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee \
+    --hash=sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129 \
+    --hash=sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2 \
+    --hash=sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51 \
+    --hash=sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee \
+    --hash=sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8 \
+    --hash=sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b \
+    --hash=sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613 \
+    --hash=sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742 \
+    --hash=sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe \
+    --hash=sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3 \
+    --hash=sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5 \
+    --hash=sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631 \
+    --hash=sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7 \
+    --hash=sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15 \
+    --hash=sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c \
+    --hash=sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea \
+    --hash=sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417 \
+    --hash=sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250 \
+    --hash=sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88 \
+    --hash=sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca \
+    --hash=sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa \
+    --hash=sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99 \
+    --hash=sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149 \
+    --hash=sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41 \
+    --hash=sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574 \
+    --hash=sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0 \
+    --hash=sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f \
+    --hash=sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d \
+    --hash=sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654 \
+    --hash=sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3 \
+    --hash=sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19 \
+    --hash=sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90 \
+    --hash=sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578 \
+    --hash=sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9 \
+    --hash=sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1 \
+    --hash=sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51 \
+    --hash=sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719 \
+    --hash=sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236 \
+    --hash=sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a \
+    --hash=sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c \
+    --hash=sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade \
+    --hash=sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944 \
+    --hash=sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc \
+    --hash=sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6 \
+    --hash=sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6 \
+    --hash=sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27 \
+    --hash=sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6 \
+    --hash=sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2 \
+    --hash=sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12 \
+    --hash=sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf \
+    --hash=sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114 \
+    --hash=sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7 \
+    --hash=sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf \
+    --hash=sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d \
+    --hash=sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b \
+    --hash=sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed \
+    --hash=sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03 \
+    --hash=sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4 \
+    --hash=sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67 \
+    --hash=sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365 \
+    --hash=sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a \
+    --hash=sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748 \
+    --hash=sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b \
+    --hash=sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079 \
+    --hash=sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482
+exceptiongroup==1.2.2 ; python_version >= "3.10" and python_version < "3.11" \
+    --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \
+    --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc
+h11==0.14.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
+    --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
+idna==3.10 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
+    --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
+outcome==1.3.0.post0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
+    --hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
+packaging==24.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \
+    --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f
+pycparser==2.22 ; os_name == "nt" and implementation_name != "pypy" and python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
+    --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
+pysocks==1.7.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \
+    --hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
+    --hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
+python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \
+    --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a
+requests==2.32.3 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
+    --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
+selenium==4.26.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:1db3f3a0cd5bb07624fa8a3905a6fdde1595a42185a0617077c361dc53d104fb \
+    --hash=sha256:7640f3f08ae7f4e450f895678e8a10a55eb4e4ca18311ed675ecc4684b96b683
+sniffio==1.3.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
+    --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
+sortedcontainers==2.4.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
+    --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
+soupsieve==2.6 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \
+    --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9
+trio-websocket==0.11.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f \
+    --hash=sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638
+trio==0.27.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831 \
+    --hash=sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884
+types-beautifulsoup4==4.12.0.20241020 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:158370d08d0cd448bd11b132a50ff5279237a5d4b5837beba074de152a513059 \
+    --hash=sha256:c95e66ce15a4f5f0835f7fbc5cd886321ae8294f977c495424eaf4225307fd30
+types-html5lib==1.1.11.20241018 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403 \
+    --hash=sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa
+types-requests==2.32.0.20241016 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95 \
+    --hash=sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747
+typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
+    --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
+urllib3==2.2.3 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
+    --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
+urllib3[socks]==2.2.3 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
+    --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
+webdriver-manager==4.0.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:75908d92ecc45ff2b9953614459c633db8f9aa1ff30181cefe8696e312908129 \
+    --hash=sha256:efedf428f92fd6d5c924a0d054e6d1322dd77aab790e834ee767af392b35590f
+websocket-client==1.8.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526 \
+    --hash=sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da
+wsproto==1.2.0 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \
+    --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736

+ 8 - 0
scripts/download_all_domains.py

@@ -0,0 +1,8 @@
+"""Downloads all domain data sets from FAOSTAT website."""
+
+from faostat_data_primap.download import (
+    download_all_domains,
+)
+
+if __name__ == "__main__":
+    download_all_domains()

+ 350 - 0
src/faostat_data_primap/download.py

@@ -0,0 +1,350 @@
+"""Downloads data from FAOSTAT website."""
+
+import hashlib
+import os
+import pathlib
+import time
+import zipfile
+from datetime import datetime
+
+import requests
+from bs4 import BeautifulSoup
+from selenium import webdriver
+from selenium.webdriver.chrome.options import Options
+from selenium.webdriver.chrome.service import Service
+
+from faostat_data_primap.exceptions import DateTagNotFoundError
+from faostat_data_primap.helper.definitions import domains, downloaded_data_path
+
+
+def find_previous_release_path(
+    current_release_path: pathlib.Path,
+) -> pathlib.Path | None:
+    """
+    Find the most recent previous release directory within same domain
+
+    Release directories are assumed to be subdirectories within the same parent
+    directory as `current_release_path`. The Sorting is done alphabetically,
+    so directory names should follow the naming convention YYYY-MM-DD
+
+    Parameters
+    ----------
+    current_release_path : pathlib.Path
+        The path of the current release directory.
+
+    Returns
+    -------
+    pathlib.Path or None
+        Returns the path of the most recent previous release directory if one exists,
+        otherwise returns None.
+    """
+    domain_path = current_release_path.parent
+    all_releases = [
+        release_name
+        for release_name in os.listdir(current_release_path.parent)
+        if (domain_path / release_name).is_dir()
+    ]
+
+    # make sure all directories follow the naming convention
+    try:
+        all_releases_datetime = [
+            datetime.strptime(release, "%Y-%m-%d") for release in all_releases
+        ]
+    except ValueError as e:
+        msg = (
+            "All release folders must be in YYYY-MM-DD format, "
+            f"got {sorted(all_releases)}"
+        )
+        raise ValueError(msg) from e
+
+    all_releases_datetime = sorted(all_releases_datetime)
+    current_release_datetime = datetime.strptime(current_release_path.name, "%Y-%m-%d")
+    index = all_releases_datetime.index(current_release_datetime)
+
+    # if the current release is the latest or the only one
+    if index == 0:
+        return None
+
+    return domain_path / all_releases_datetime[index - 1].strftime("%Y-%m-%d")
+
+
+def calculate_checksum(file_path: pathlib.Path) -> str:
+    """
+    Calculate the SHA-256 checksum of a file.
+
+    Parameters
+    ----------
+    file_path : pathlib.Path
+        The path to the file for which the checksum is calculated.
+
+    Returns
+    -------
+    str
+        The SHA-256 checksum of the file as a hexadecimal string.
+    """
+    sha256 = hashlib.sha256()
+    with open(file_path, "rb") as f:
+        for chunk in iter(lambda: f.read(4096), b""):
+            sha256.update(chunk)
+    return sha256.hexdigest()
+
+
+def download_methodology(url_download: str, save_path: pathlib.Path) -> None:
+    """
+    Download methodology file.
+
+    Download the methodology PDF-file from a specified URL and save to a
+    target directory. If the file already exists in `save_path`,
+    the download is skipped. If a previous release directory exists,
+    the function attempts to locate the file there and compares checksums
+    to avoid downloading an identical file. If it exists in the previous release,
+    but it's not identical it is downloaded. If the file exists in the previous
+    release directory and is identical, a symlink will be created instead of downloading
+    to avoid duplicate downloads. If the file does not exist in a previous release,
+    it will be downloaded.
+
+    Parameters
+    ----------
+    url_download : str
+        The URL from which to download the file.
+    save_path : pathlib.Path
+        The path to the directory where the file should be saved.
+    """
+    filename = url_download.split("/")[-1]
+    download_path = save_path / filename
+
+    if download_path.exists():
+        print(f"Skipping download of {download_path} because it already exists.")
+        return
+
+    previous_release = find_previous_release_path(save_path)
+    # Attempt to find a file to compare in the previous release
+    if previous_release:
+        file_to_compare = previous_release / filename
+        if file_to_compare.exists():
+            response = requests.get(url_download, stream=True, timeout=30)
+            response.raise_for_status()
+            file_to_download_checksum = hashlib.sha256(response.content).hexdigest()
+            file_to_compare_checksum = calculate_checksum(file_to_compare)
+
+            if file_to_download_checksum == file_to_compare_checksum:
+                print(
+                    f"File '{filename}' is identical in the previous release. "
+                    f"Creating symlink."
+                )
+                os.symlink(file_to_compare, download_path)
+                return
+            else:
+                print(
+                    f"File '{filename}' differs from previous release. "
+                    f"Downloading file."
+                )
+        else:
+            print(f"File '{filename}' not found in previous release. Downloading file.")
+            response = requests.get(url_download, stream=True, timeout=30)
+            response.raise_for_status()
+
+        # Save downloaded file to current release
+        with open(download_path, "wb") as f:
+            f.write(response.content)
+
+    else:
+        print(f"No previous release found. Downloading file '{filename}'.")
+        response = requests.get(url_download, stream=True, timeout=30)
+        response.raise_for_status()
+        with open(download_path, "wb") as f:
+            f.write(response.content)
+
+
+def get_html_content(url: str) -> BeautifulSoup:
+    """
+    Get html from url.
+
+    Parameters
+    ----------
+    url
+        The url to the domain overview website.
+
+    Returns
+    -------
+        html content
+    """
+    # If the chrome driver isn't found on your system PATH, Selenium
+    # will automatically download it for you. Make sure there is no
+    # chromedriver installed on your system.
+    service = Service()
+    options = Options()
+    options.add_argument("--headless")
+    driver = webdriver.Chrome(service=service, options=options)
+
+    driver.get(url)
+
+    # give time to load javascript
+    time.sleep(3)
+
+    html_content = driver.page_source
+
+    return BeautifulSoup(html_content, "html.parser")
+
+
+def get_last_updated_date(soup: BeautifulSoup, url: str) -> str:
+    """
+    Get the date when data set way last updated from html text
+
+    The FAO stat domain overview page includes a date when
+    the data set was last updated. We need it to label our downloaded
+    data sets. This function searches and extracts the date
+    from the html code.
+
+    Parameters
+    ----------
+    soup
+        The beautiful soup object with all html code of the domain
+        overview page.
+    url
+        The url to the domain overview page.
+
+    Returns
+    -------
+        date when data set was last updated
+
+    Raises
+    ------
+    DateTagNotFoundError
+        If the tag for the date is not found in the html code
+    """
+    date_tag = soup.find("p", {"data-role": "date"})
+
+    if not date_tag:
+        raise DateTagNotFoundError(url=url)
+
+    last_updated = date_tag.get_text()
+    last_updated = datetime.strptime(last_updated, "%B %d, %Y").strftime("%Y-%m-%d")
+    return last_updated
+
+
+def download_file(url_download: str, save_path: pathlib.Path) -> bool:
+    """
+    Download file.
+
+    If an existing file is found at this location, the download is skipped.
+
+    Parameters
+    ----------
+    url_download
+        Remote URL to download the file from
+    save_path
+        Path to save the downloaded file to
+
+    Returns
+    -------
+        True if the file was downloaded, False if a cached file was found
+    """
+    if not save_path.exists():
+        with requests.get(url_download, stream=True, timeout=30) as response:
+            response.raise_for_status()
+
+            with open(save_path, mode="wb") as file:
+                file.write(response.content)
+
+        return True
+    else:
+        print(f"Skipping download of {save_path}" " because it already exists.")
+    return False
+
+
+def unzip_file(local_filename: pathlib.Path) -> list[str]:
+    """
+    Unzip files in same directory. Skip if files are already there
+
+    Parameters
+    ----------
+    local_filename
+        Path to the zip file
+
+    Returns
+    -------
+        List of unzipped files
+    """
+    unzipped_files = []
+    if local_filename.suffix == ".zip":
+        try:
+            with zipfile.ZipFile(str(local_filename), "r") as zip_file:
+                for file_info in zip_file.infolist():
+                    extracted_file_path = local_filename.parent / file_info.filename
+
+                    if extracted_file_path.exists():
+                        print(
+                            f"File '{file_info.filename}' already exists. "
+                            f"Skipping extraction."
+                        )
+                    else:
+                        print(f"Extracting '{file_info.filename}'...")
+                        zip_file.extract(file_info, local_filename.parent)
+                        unzipped_files.append(local_filename.name)
+
+        # TODO Better error logging/visibilty
+        except zipfile.BadZipFile:
+            print(f"Error while trying to extract " f"{local_filename}")
+        except NotImplementedError:
+            print("Zip format not supported, " "please unzip on the command line.")
+    else:
+        print(f"Not attempting to extract " f"{local_filename}.")
+    return unzipped_files
+
+
+def download_all_domains(
+    domains: dict[str, dict[str, str]] = domains,
+    downloaded_data_path: pathlib.Path = downloaded_data_path,
+) -> list[str]:
+    """
+    Download and unpack all climate-related domains from the FAO stat website.
+
+    Extract the date when the data set was last updated and create a directory
+    with the same name. Download the zip files for each domain if
+    it does not already exist. Unpack the zip file and save in
+    the same directory.
+
+    Parameters
+    ----------
+    sources
+        Name of data set, url to domain overview,
+        and download url
+
+    Returns
+    -------
+        List of input files that have been fetched or found locally.
+
+    """
+    downloaded_files = []
+    for ds_name, urls in domains.items():
+        url = urls["url_domain"]
+        url_download = urls["url_download"]
+        url_methodology = urls["url_methodology"]
+
+        soup = get_html_content(url)
+
+        last_updated = get_last_updated_date(soup, url)
+
+        if not downloaded_data_path.exists():
+            downloaded_data_path.mkdir()
+
+        ds_path = downloaded_data_path / ds_name
+        if not ds_path.exists():
+            ds_path.mkdir()
+
+        local_data_dir = ds_path / last_updated
+        if not local_data_dir.exists():
+            local_data_dir.mkdir()
+
+        download_methodology(save_path=local_data_dir, url_download=url_methodology)
+
+        local_filename = local_data_dir / f"{ds_name}.zip"
+
+        download_file(url_download=url_download, save_path=local_filename)
+
+        downloaded_files.append(str(local_filename))
+
+        unzip_file(local_filename)
+
+    return downloaded_files

+ 22 - 0
src/faostat_data_primap/exceptions.py

@@ -0,0 +1,22 @@
+"""Exceptions"""
+
+
+class DateTagNotFoundError(Exception):
+    """
+    Raised when date for latest update cannot be found on FAO domain website
+    """
+
+    def __init__(
+        self,
+        url: str,
+    ) -> None:
+        """
+        Initialise the error
+
+        Parameters
+        ----------
+        url
+            Link to download domain page
+        """
+        msg = f"Tag for date lat updated was not found on page with url {url}."
+        super().__init__(msg)

+ 1 - 0
src/faostat_data_primap/helper/__init__.py

@@ -0,0 +1 @@
+"""helper"""

+ 76 - 0
src/faostat_data_primap/helper/definitions.py

@@ -0,0 +1,76 @@
+"""definitions like folders, mappings etc."""
+from pathlib import Path
+
+domains = {
+    "farm_gate_emissions_crops": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GCE",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_crops_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GCE/GCE_e.pdf",
+    },
+    "farm_gate_livestock": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GLE",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_livestock_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GLE/GLE_e.pdf",
+    },
+    "farm_gate_agriculture_energy": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GN",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_Agriculture_Energy_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GN/GN_2023Oct_Final.pdf",
+    },
+    "land_use_forests": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GF",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_Land_Use_Forests_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GF/GF_e.pdf",
+    },
+    "land_use_fires": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GI",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_Land_Use_Fires_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GI/GI_e.pdf",
+    },
+    "land_use_drained_organic_soils": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GV",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_Drained_Organic_Soils_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GV/GV_e.pdf",
+    },
+    "pre_post_agricultural_production": {
+        "url_domain": "https://www.fao.org/faostat/en/#data/GPP",
+        "url_download": "https://bulks-faostat.fao.org/production/Emissions_Pre_Post_Production_E_All_Data.zip",
+        "url_methodology": "https://files-faostat.fao.org/production/GPP/README_Methodological_Note.pdf",
+    },
+}
+
+
+def get_root_path(root_indicator: str = ".git") -> Path:
+    """
+    Traverse up from the current script location to find the repository root.
+
+    The root is defined by the presence of a root_indicator file or
+    directory (e.g., '.git').
+
+    Parameters
+    ----------
+        root_indicator
+            A filename or directory name that indicates the root of the repository.
+
+    Returns
+    -------
+    Path
+        The path to the root directory of the repository.
+
+    Raises
+    ------
+        RuntimeError: If the repository root cannot be found.
+    """
+    current_dir = Path(__file__).resolve().parent
+    while current_dir != Path(current_dir.root):
+        if (current_dir / root_indicator).exists():
+            return current_dir
+        current_dir = current_dir.parent
+    msg = f"Repository root with indicator '{root_indicator}' not found."
+    raise RuntimeError(msg)
+
+
+root_path = get_root_path()
+code_path = root_path / "src" / "faostat_data_primap"
+extracted_data_path = root_path / "extracted_data"
+downloaded_data_path = root_path / "downloaded_data"

+ 32 - 0
tests/integration/test_download_script.py

@@ -0,0 +1,32 @@
+import os
+
+from src.faostat_data_primap.download import download_all_domains
+
+
+# test the whole download script run
+def test_download_all_domains(tmp_path):
+    downloaded_data_path = tmp_path / "downloaded_data"
+    download_all_domains(downloaded_data_path=downloaded_data_path)
+
+    expected_downloaded_domains = [
+        "farm_gate_emissions_crops",
+        "farm_gate_livestock",
+        "farm_gate_agriculture_energy",
+        "land_use_forests",
+        "land_use_fires",
+        "land_use_drained_organic_soils",
+        "pre_post_agricultural_production",
+    ]
+
+    domains = []
+    for domain in downloaded_data_path.iterdir():
+        if domain.is_dir():
+            domains.append(domain.name)
+        for release in domain.iterdir():
+            downloaded_data = os.listdir(release)
+            # make sure we have at least one .csv, one .pdf and one .zip file
+            assert [f for f in downloaded_data if f.endswith(".csv")]
+            assert [f for f in downloaded_data if f.endswith(".pdf")]
+            assert [f for f in downloaded_data if f.endswith(".zip")]
+
+    assert sorted(expected_downloaded_domains) == sorted(domains)

+ 261 - 0
tests/unit/test_download.py

@@ -0,0 +1,261 @@
+import pytest
+import requests
+
+from src.faostat_data_primap.download import (
+    calculate_checksum,
+    download_methodology,
+    find_previous_release_path,
+)
+
+
+@pytest.fixture
+def temp_domain_directories(tmp_path):
+    """
+    Sets up a temporary directory structure for domains and releases for testing.
+
+    Parameters
+    ----------
+    tmp_path : pathlib.Path
+        A pytest-provided temporary directory path.
+
+    Returns
+    -------
+    dict
+        A dictionary containing the paths to the `downloaded_data` directory,
+        the specific domain directory, and a list of sorted release paths.
+    """
+    downloaded_data = tmp_path / "downloaded_data"
+    downloaded_data.mkdir()
+
+    domains = (
+        "farm_gate_emissions_crops",
+        "farm_gate_livestock",
+        "farm_gate_agriculture_energy",
+        "land_use_forests",
+        "land_use_fires",
+        "land_use_drained_organic_soils",
+        "pre_post_agricultural_production",
+    )
+    domain_paths = []
+
+    for domain in domains:
+        domain_path = downloaded_data / domain
+        domain_path.mkdir()
+        domain_paths.append(domain_path)
+
+    return {
+        "downloaded_data": downloaded_data,
+        "domain_paths": domain_paths,
+    }
+
+
+@pytest.mark.parametrize(
+    "releases," "current_release_date, " "expected_result_date",
+    [
+        pytest.param(
+            ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"],
+            "2024-11-29",
+            "2024-11-09",
+            id="current release is latest release",
+        ),
+        pytest.param(
+            ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"],
+            "2023-12-13",
+            "2022-03-18",
+            id="current somewhere not the latest release",
+        ),
+    ],
+)
+def test_find_previous_release_path_exists(
+    temp_domain_directories, releases, current_release_date, expected_result_date
+):
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+    current_release_path = domain_path / current_release_date
+    expected_result = domain_path / expected_result_date
+
+    release_paths = []
+    for release in releases:
+        release_path = domain_path / release
+        release_path.mkdir()
+        release_paths.append(release_path)
+
+    result = find_previous_release_path(current_release_path)
+
+    assert result == expected_result
+
+
+@pytest.mark.parametrize(
+    "releases,current_release_date",
+    [
+        pytest.param(
+            ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"],
+            "2022-03-18",
+            id="current release is oldest release",
+        ),
+        pytest.param(
+            ["2024-11-09"], "2024-11-09", id="current release is only release"
+        ),
+    ],
+)
+def test_find_previous_release_path_that_does_not_exists(
+    temp_domain_directories, releases, current_release_date
+):
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+    current_release_path = domain_path / current_release_date
+
+    release_paths = []
+    for release in releases:
+        release_path = domain_path / release
+        release_path.mkdir()
+        release_paths.append(release_path)
+
+    result = find_previous_release_path(current_release_path)
+
+    assert not result
+
+
+@pytest.mark.parametrize(
+    "releases,current_release_date, error_msg",
+    [
+        pytest.param(
+            ["2023-12-13", "2022-03-18", "2024-11-29", "20240-11-09"],
+            "2022-03-18",
+            (
+                "All release folders must be in YYYY-MM-DD format, got "
+                "['2022-03-18', '2023-12-13', '2024-11-29', '20240-11-09']"
+            ),
+            id="typo",
+        ),
+        pytest.param(
+            ["20231213", "2022-03-18", "2024-11-29", "2024-11-09"],
+            "2022-03-18",
+            (
+                "All release folders must be in YYYY-MM-DD format, got "
+                "['2022-03-18', '20231213', '2024-11-09', '2024-11-29']"
+            ),
+            id="missing hyphen",
+        ),
+    ],
+)
+def test_find_previous_release_path_wrong_dir_format(
+    temp_domain_directories, releases, current_release_date, error_msg
+):
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+    current_release_path = domain_path / current_release_date
+
+    release_paths = []
+    for release in releases:
+        release_path = domain_path / release
+        release_path.mkdir()
+        release_paths.append(release_path)
+
+    with pytest.raises(ValueError) as excinfo:
+        result = find_previous_release_path(current_release_path)  # noqa: F841
+
+    assert str(excinfo.value) == error_msg
+
+
+def test_calculate_checksum(tmp_path):
+    filepath_a = tmp_path / "test_file_a.txt"
+    with open(filepath_a, "w") as f:
+        f.write("content of file a")
+
+    filepath_b = tmp_path / "test_file_b.txt"
+    with open(filepath_b, "w") as f:
+        f.write("content of file a")
+
+    filepath_c = tmp_path / "test_file_c.txt"
+    with open(filepath_c, "w") as f:
+        f.write("content of file c")
+
+    checksum_a = calculate_checksum(filepath_a)
+
+    checksum_b = calculate_checksum(filepath_b)
+
+    checksum_c = calculate_checksum(filepath_c)
+
+    assert checksum_a == checksum_b
+
+    assert checksum_b != checksum_c
+
+
+def test_file_exists_in_previous_release_and_is_the_same(temp_domain_directories):
+    # set up temporary directories
+    downloaded_data_path = temp_domain_directories["downloaded_data"]
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+
+    # make folders for different releases
+    for release in ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"]:
+        release_path = domain_path / release
+        release_path.mkdir()
+
+    file_to_compare_path = domain_path / "2024-11-09" / "GCE_e.pdf"
+    response = requests.get(
+        "https://files-faostat.fao.org/production/GCE/GCE_e.pdf",
+        stream=True,
+        timeout=30,
+    )
+    response.raise_for_status()  # Check for successful request
+    with open(file_to_compare_path, "wb") as f:
+        f.write(response.content)
+    save_path = downloaded_data_path / "farm_gate_emissions_crops" / "2024-11-29"
+    download_methodology(
+        "https://files-faostat.fao.org/production/GCE/GCE_e.pdf", save_path=save_path
+    )
+    downloaded_file_path = domain_path / "2024-11-29" / "GCE_e.pdf"
+    assert downloaded_file_path.is_symlink()
+
+
+def test_methodology_document_exists_in_previous_release_but_is_different(
+    temp_domain_directories,
+):
+    # set up temporary directories
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+
+    # make folders for different releases
+    for release in ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"]:
+        release_path = domain_path / release
+        release_path.mkdir()
+
+    file_to_compare_path = domain_path / "2024-11-09" / "GCE_e.pdf"
+    with open(file_to_compare_path, "wb") as f:
+        s = "hi"
+        f.write(s.encode("utf-8"))
+
+    save_path = domain_path / "2024-11-29"
+    download_methodology(
+        "https://files-faostat.fao.org/production/GCE/GCE_e.pdf", save_path=save_path
+    )
+    downloaded_file_path = domain_path / "2024-11-29" / "GCE_e.pdf"
+    assert downloaded_file_path.exists()
+
+
+def test_methodology_document_does_not_exist_in_previous_release(
+    temp_domain_directories,
+):
+    # set up temporary directories
+    domain_path = temp_domain_directories["domain_paths"][
+        0
+    ]  # farm_gate_emissions_crops
+
+    # make folders for different releases
+    for release in ["2023-12-13", "2022-03-18", "2024-11-29", "2024-11-09"]:
+        release_path = domain_path / release
+        release_path.mkdir()
+
+    save_path = domain_path / "2024-11-29"
+    download_methodology(
+        "https://files-faostat.fao.org/production/GCE/GCE_e.pdf", save_path=save_path
+    )
+    downloaded_file_path = domain_path / "2024-11-29" / "GCE_e.pdf"
+    assert downloaded_file_path.exists()