eotdl 2025.4.22__tar.gz → 2025.4.22.post2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/PKG-INFO +1 -1
  2. eotdl-2025.4.22.post2/eotdl/__init__.py +1 -0
  3. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/commands/datasets.py +11 -0
  4. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/commands/models.py +12 -0
  5. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/commands/stac.py +2 -2
  6. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/curation/stac/api.py +2 -2
  7. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/datasets/__init__.py +1 -0
  8. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/datasets/update.py +8 -0
  9. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/__init__.py +1 -0
  10. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/update.py +7 -0
  11. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/APIRepo.py +2 -2
  12. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/DatasetsAPIRepo.py +6 -0
  13. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/ModelsAPIRepo.py +5 -1
  14. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/STACAPIRepo.py +2 -2
  15. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/pyproject.toml +1 -1
  16. eotdl-2025.4.22.post2/tests/load/conftest.py +55 -0
  17. eotdl-2025.4.22.post2/tests/load/eotdl.tiers.json +34 -0
  18. eotdl-2025.4.22.post2/tests/load/test_upload_size.py +194 -0
  19. eotdl-2025.4.22.post2/tests/load/upload_times.log +6 -0
  20. eotdl-2025.4.22.post2/tests/load/upload_times_tifs.log +5 -0
  21. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/uv.lock +1 -1
  22. eotdl-2025.4.22/eotdl/__init__.py +0 -1
  23. eotdl-2025.4.22/pyproject.toml.bck +0 -35
  24. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/.gitignore +0 -0
  25. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/README.md +0 -0
  26. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/__init__.py +0 -0
  27. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/airbus/__init__.py +0 -0
  28. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/airbus/client.py +0 -0
  29. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/airbus/parameters.py +0 -0
  30. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/airbus/utils.py +0 -0
  31. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/download.py +0 -0
  32. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/search.py +0 -0
  33. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/sentinelhub/__init__.py +0 -0
  34. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/sentinelhub/client.py +0 -0
  35. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/sentinelhub/evalscripts.py +0 -0
  36. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/sentinelhub/parameters.py +0 -0
  37. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/access/sentinelhub/utils.py +0 -0
  38. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/auth/__init__.py +0 -0
  39. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/auth/auth.py +0 -0
  40. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/auth/errors.py +0 -0
  41. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/auth/is_logged.py +0 -0
  42. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/auth/logout.py +0 -0
  43. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/cli.py +0 -0
  44. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/commands/__init__.py +0 -0
  45. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/commands/auth.py +0 -0
  46. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/curation/__init__.py +0 -0
  47. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/curation/stac/__init__.py +0 -0
  48. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/curation/stac/stac.py +0 -0
  49. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/datasets/ingest.py +0 -0
  50. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/datasets/retrieve.py +0 -0
  51. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/datasets/stage.py +0 -0
  52. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/files/__init__.py +0 -0
  53. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/files/ingest.bck +0 -0
  54. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/files/ingest.py +0 -0
  55. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/files/metadata.py +0 -0
  56. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/download.py +0 -0
  57. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/ingest.py +0 -0
  58. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/retrieve.py +0 -0
  59. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/models/stage.py +0 -0
  60. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/AuthAPIRepo.py +0 -0
  61. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/AuthRepo.py +0 -0
  62. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/FilesAPIRepo.py +0 -0
  63. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/repos/__init__.py +0 -0
  64. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/shared/__init__.py +0 -0
  65. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/shared/checksum.py +0 -0
  66. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/__init__.py +0 -0
  67. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/geo_utils.py +0 -0
  68. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/metadata.py +0 -0
  69. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/paths.py +0 -0
  70. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/stac.py +0 -0
  71. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/time_utils.py +0 -0
  72. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/tools/tools.py +0 -0
  73. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/wrappers/__init__.py +0 -0
  74. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl/wrappers/models.py +0 -0
  75. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/eotdl.png +0 -0
  76. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/main.py +0 -0
  77. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/__init__.py +0 -0
  78. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/_test_access.py +0 -0
  79. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/_test_hello.py +0 -0
  80. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/__init__.py +0 -0
  81. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/_test_auth.py +0 -0
  82. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/access/__init__.py +0 -0
  83. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/__init__.py +0 -0
  84. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/__init__.py +0 -0
  85. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/fixtures.py +0 -0
  86. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/catalog.json +0 -0
  87. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_1/Jaca_1.json +0 -0
  88. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_2/Jaca_2.json +0 -0
  89. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_3/Jaca_3.json +0 -0
  90. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_4/Jaca_4.json +0 -0
  91. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/collection.json +0 -0
  92. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/catalog.json +0 -0
  93. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_1/Jaca_1.json +0 -0
  94. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_2/Jaca_2.json +0 -0
  95. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_3/Jaca_3.json +0 -0
  96. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_4/Jaca_4.json +0 -0
  97. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/collection.json +0 -0
  98. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_1/Jaca_1.json +0 -0
  99. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_2/Jaca_2.json +0 -0
  100. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_3/Jaca_3.json +0 -0
  101. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_4/Jaca_4.json +0 -0
  102. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/collection.json +0 -0
  103. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_1_labels.geojson +0 -0
  104. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_2_labels.geojson +0 -0
  105. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_3_labels.geojson +0 -0
  106. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_4_labels.geojson +0 -0
  107. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/labels_scaneo/labels.json +0 -0
  108. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-13.tif +0 -0
  109. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-13_labels.geojson +0 -0
  110. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-28.tif +0 -0
  111. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-02-02_labels.geojson +0 -0
  112. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_dataframe.py +0 -0
  113. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_extent.py +0 -0
  114. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_labels.py +0 -0
  115. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_ml_dataset.py +0 -0
  116. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/curation/stac/test_stac.py +0 -0
  117. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/datasets/__init__.py +0 -0
  118. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/datasets/_test_datasets.py +0 -0
  119. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/datasets/_test_download_dataset.py +0 -0
  120. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/datasets/_test_retrieve_datasets.py +0 -0
  121. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/__init__.py +0 -0
  122. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/test_geo_utils.py +0 -0
  123. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/test_metadata.py +0 -0
  124. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/test_paths.py +0 -0
  125. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/test_time_utils.py +0 -0
  126. {eotdl-2025.4.22 → eotdl-2025.4.22.post2}/tests/unit/tools/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eotdl
3
- Version: 2025.4.22
3
+ Version: 2025.4.22.post2
4
4
  Summary: Earth Observation Training Data Lab
5
5
  Author-email: earthpulse <it@earthpulse.es>
6
6
  License-Expression: MIT
@@ -0,0 +1 @@
1
+ __version__ = "2025.04.22-2"
@@ -5,6 +5,7 @@ from ..datasets import (
5
5
  retrieve_datasets,
6
6
  ingest_dataset,
7
7
  stage_dataset,
8
+ deactivate_dataset,
8
9
  )
9
10
 
10
11
  app = typer.Typer(help="Explore, ingest and download training datasets.")
@@ -141,5 +142,15 @@ def get(
141
142
  typer.echo(e)
142
143
 
143
144
 
145
+ @app.command()
146
+ def deactivate(
147
+ dataset_id: str = typer.Argument(None, help="ID of the dataset to deactivate")
148
+ ):
149
+ try:
150
+ deactivate_dataset(dataset_id)
151
+ typer.echo(f"Dataset {dataset_id} deactivated")
152
+ except Exception as e:
153
+ typer.echo(e)
154
+
144
155
  if __name__ == "__main__":
145
156
  app()
@@ -5,6 +5,7 @@ from ..models import (
5
5
  retrieve_models,
6
6
  ingest_model,
7
7
  stage_model,
8
+ deactivate_model
8
9
  )
9
10
 
10
11
  app = typer.Typer(help="Explore, ingest and download ML models.")
@@ -132,5 +133,16 @@ def get(
132
133
  typer.echo(e)
133
134
 
134
135
 
136
+ @app.command()
137
+ def deactivate(
138
+ model_id: str = typer.Argument(None, help="ID of the model to deactivate")
139
+ ):
140
+ try:
141
+ deactivate_model(model_id)
142
+ typer.echo(f"Model {model_id} deactivated")
143
+ except Exception as e:
144
+ typer.echo(e)
145
+
146
+
135
147
  if __name__ == "__main__":
136
148
  app()
@@ -24,9 +24,9 @@ def collections():
24
24
  raise typer.Abort()
25
25
 
26
26
  @app.command()
27
- def collection(collection_id: str):
27
+ def collection(collection_name: str):
28
28
  try:
29
- data = retrieve_stac_collection(collection_id)
29
+ data = retrieve_stac_collection(collection_name)
30
30
  typer.echo(data)
31
31
  except Exception as e:
32
32
  typer.echo(e)
@@ -16,9 +16,9 @@ def retrieve_stac_collections():
16
16
  raise Exception(error)
17
17
  return data
18
18
 
19
- def retrieve_stac_collection(collection_id):
19
+ def retrieve_stac_collection(collection_name):
20
20
  repo = STACAPIRepo()
21
- data, error = repo.collection(collection_id)
21
+ data, error = repo.collection(collection_name)
22
22
  if error:
23
23
  raise Exception(error)
24
24
  return data
@@ -1,3 +1,4 @@
1
1
  from .retrieve import retrieve_datasets, retrieve_dataset, retrieve_dataset_files
2
2
  from .ingest import ingest_dataset, ingest_virtual_dataset, ingest_dataset_catalog
3
3
  from .stage import stage_dataset, stage_dataset_file
4
+ from .update import deactivate_dataset
@@ -15,3 +15,11 @@ def update_dataset(dataset_id, metadata, content, user):
15
15
  if error:
16
16
  raise Exception(error)
17
17
  return data
18
+
19
+
20
+ def deactivate_dataset(dataset_id):
21
+ repo = DatasetsAPIRepo()
22
+ data, error = repo.deactivate_dataset(dataset_id)
23
+ if error:
24
+ raise Exception(error)
25
+ return data
@@ -1,3 +1,4 @@
1
1
  from .retrieve import retrieve_models, retrieve_model, retrieve_model_files
2
2
  from .ingest import ingest_model, ingest_virtual_model, ingest_model_catalog
3
3
  from .stage import stage_model, stage_model_file
4
+ from .update import deactivate_model
@@ -15,3 +15,10 @@ def update_model(model_id, metadata, content, user):
15
15
  if error:
16
16
  raise Exception(error)
17
17
  return data
18
+
19
+ def deactivate_model(model_id):
20
+ repo = ModelsAPIRepo()
21
+ data, error = repo.deactivate_model(model_id)
22
+ if error:
23
+ raise Exception(error)
24
+ return data
@@ -4,8 +4,8 @@ import requests
4
4
 
5
5
  class APIRepo:
6
6
  def __init__(self, url=None):
7
- default_url = "https://api.eotdl.com/"
8
- # default_url = "http://localhost:8000/"
7
+ # default_url = "https://api.eotdl.com/"
8
+ default_url = "http://localhost:8000/"
9
9
  self.url = url if url else os.getenv("EOTDL_API_URL", default_url)
10
10
 
11
11
  def format_response(self, response):
@@ -41,3 +41,9 @@ class DatasetsAPIRepo(APIRepo):
41
41
  headers=self.generate_headers(user),
42
42
  )
43
43
  return self.format_response(response)
44
+
45
+ def deactivate_dataset(self, dataset_name):
46
+ response = requests.patch(
47
+ self.url + "datasets/deactivate/" + dataset_name
48
+ )
49
+ return self.format_response(response)
@@ -38,4 +38,8 @@ class ModelsAPIRepo(APIRepo):
38
38
  )
39
39
  return self.format_response(response)
40
40
 
41
-
41
+ def deactivate_model(self, model_name):
42
+ response = requests.patch(
43
+ self.url + "models/deactivate/" + model_name
44
+ )
45
+ return self.format_response(response)
@@ -15,8 +15,8 @@ class STACAPIRepo(APIRepo):
15
15
  response = requests.get(self.url + "stac/collections")
16
16
  return self.format_response(response)
17
17
 
18
- def collection(self, collection_id):
19
- response = requests.get(self.url + f"stac/collections/{collection_id}")
18
+ def collection(self, collection_name):
19
+ response = requests.get(self.url + f"stac/collections/{collection_name}")
20
20
  return self.format_response(response)
21
21
 
22
22
  def items(self, collection_id):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "eotdl"
3
- version = "2025.04.22"
3
+ version = "2025.04.22-2"
4
4
  description = "Earth Observation Training Data Lab"
5
5
  authors = [
6
6
  {name = "earthpulse", email = "it@earthpulse.es"},
@@ -0,0 +1,55 @@
1
+ import json
2
+ from bson import ObjectId
3
+ from minio import Minio
4
+ from pymongo import MongoClient
5
+ import pytest
6
+ import boto3
7
+
8
+
9
+ @pytest.fixture
10
+ def setup_mongo():
11
+ client = MongoClient("mongodb://localhost:27017/")
12
+ db = client["eotdl-test"]
13
+
14
+ client.drop_database("eotdl-test")
15
+
16
+ db = client["eotdl-test"]
17
+ tiers_collection = db["tiers"]
18
+
19
+ with open("eotdl/tests/load/eotdl.tiers.json", "r") as file:
20
+ json_data = json.load(file)
21
+
22
+ for item in json_data:
23
+ if "_id" in item:
24
+ item["_id"] = ObjectId(item["_id"])
25
+
26
+ tiers_collection.insert_many(json_data)
27
+
28
+ yield tiers_collection
29
+
30
+ client.drop_database("eotdl-test")
31
+
32
+
33
+ @pytest.fixture
34
+ def setup_minio():
35
+ minio_client = Minio(
36
+ "localhost:9000",
37
+ access_key="eotdl",
38
+ secret_key="12345678",
39
+ secure=False,
40
+ )
41
+
42
+ bucket_name = "eotdl-test"
43
+
44
+ if not minio_client.bucket_exists(bucket_name):
45
+ minio_client.make_bucket(bucket_name)
46
+
47
+ objects = minio_client.list_objects(bucket_name, recursive=True)
48
+ for obj in objects:
49
+ minio_client.remove_object(bucket_name, obj.object_name)
50
+
51
+ yield minio_client
52
+
53
+ objects = minio_client.list_objects(bucket_name, recursive=True)
54
+ for obj in objects:
55
+ minio_client.remove_object(bucket_name, obj.object_name)
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "_id": "645242248456b2cc058e43bf",
4
+ "name": "free",
5
+ "limits": {
6
+ "datasets": {
7
+ "upload": 10,
8
+ "download": 3,
9
+ "count": 10
10
+ },
11
+ "models": {
12
+ "upload": 10,
13
+ "download": 10,
14
+ "count": 10
15
+ }
16
+ }
17
+ },
18
+ {
19
+ "_id": "645242248456b2cc058e43c0",
20
+ "name": "dev",
21
+ "limits": {
22
+ "datasets": {
23
+ "upload": 100000,
24
+ "download": 100000,
25
+ "count": 100
26
+ },
27
+ "models": {
28
+ "upload": 100000,
29
+ "download": 100000,
30
+ "count": 100
31
+ }
32
+ }
33
+ }
34
+ ]
@@ -0,0 +1,194 @@
1
+ from datetime import datetime, timezone
2
+ import io
3
+ import os
4
+ from pathlib import Path
5
+ import tempfile
6
+ import time
7
+
8
+ from eotdl.datasets.retrieve import retrieve_dataset_files
9
+ import numpy as np
10
+ import pytest
11
+
12
+ from eotdl.datasets import ingest_dataset, retrieve_dataset
13
+ import rasterio
14
+
15
+
16
+ os.environ["EOTDL_API_URL"] = "http://localhost:8000/"
17
+
18
+
19
+ def write_readme(path: Path, name: str):
20
+ readme_text = f"""---
21
+ name: {name}
22
+ authors:
23
+ - Derek van de Ven
24
+ license: free
25
+ source: https://github.com/earthpulse/eotdl/blob/main/tutorials/notebooks/02_ingesting.ipynb
26
+ ---
27
+
28
+ # {name}
29
+
30
+ This file is nonsensical data used for load testing. It should not be stored on the web.
31
+ """
32
+
33
+ with open(path / name / "README.md", "w") as outfile:
34
+ outfile.write(readme_text)
35
+
36
+
37
+ def generate_fake_tif_dataset(
38
+ path: Path, n_tifs: int = 10, tif_size: int = 256, name: str = "test_set"
39
+ ):
40
+ files_to_upload = []
41
+ for count in range(n_tifs):
42
+ files_to_upload.append(f"{name}/FakeFolder/Fake_tif{count}.tif")
43
+
44
+ for rel_path in files_to_upload:
45
+ file_path = path / rel_path
46
+ file_path.parent.mkdir(parents=True, exist_ok=True)
47
+
48
+ width, height = tif_size, tif_size
49
+ data = np.full((height, width, 3), 256, dtype=np.uint32)
50
+
51
+ with rasterio.open(
52
+ file_path,
53
+ "w",
54
+ driver="GTiff",
55
+ height=height,
56
+ width=width,
57
+ count=3,
58
+ dtype="uint32",
59
+ crs="EPSG:4326",
60
+ transform=rasterio.transform.from_origin(0, height, 1, 1),
61
+ ) as dst:
62
+ dst.write(data[:, :, 0], 1)
63
+ dst.write(data[:, :, 1], 2)
64
+ dst.write(data[:, :, 2], 3)
65
+
66
+ write_readme(path, name)
67
+
68
+
69
+ def generate_fake_dataset(
70
+ path: Path, size_mb: int = 10, n_files: int = 5, name: str = "test_set"
71
+ ):
72
+ files_to_upload = []
73
+ for count in range(n_files):
74
+ files_to_upload.append(
75
+ f"{name}/FakeFolder/Fake_tif{count}.tif",
76
+ )
77
+
78
+ total_size_bytes = size_mb * 1024 * 1024
79
+ file_size = total_size_bytes // len(files_to_upload)
80
+
81
+ for rel_path in files_to_upload:
82
+ file_path = path / rel_path
83
+ file_path.parent.mkdir(parents=True, exist_ok=True)
84
+
85
+ # Write random binary data to simulate `.tif` files
86
+ with open(file_path, "wb") as f:
87
+ f.write(os.urandom(file_size)) # Random binary data
88
+
89
+ write_readme(path, name)
90
+
91
+
92
+ @pytest.mark.parametrize(
93
+ "total_size, n_files",
94
+ [
95
+ (1, 10),
96
+ # (1e1, 10),
97
+ # (1e3, 1), # 1 files amounting to 1 GB,
98
+ # (1e3, 20), # 20 files amounting to 1 GB
99
+ # (1e4, 100), # 100 10 MB files
100
+ # (3e4, 100), # 10 times 1 GB
101
+ ],
102
+ )
103
+ def test_load(setup_mongo, setup_minio, total_size, n_files):
104
+ name = f"LoadTest-{int(total_size)}MB"
105
+ with tempfile.TemporaryDirectory(prefix="loadtest_") as tmpdir:
106
+ tmpdir = Path(tmpdir)
107
+ generate_fake_dataset(
108
+ path=tmpdir, size_mb=int(total_size), n_files=n_files, name=name
109
+ )
110
+
111
+ # upload
112
+ start_time = time.time()
113
+ ingest_dataset(path=str(tmpdir / name))
114
+
115
+ upload_duration = time.time() - start_time
116
+ print(f"Upload for {name} took {upload_duration:.2f} seconds.")
117
+
118
+ with open("eotdl/tests/load/upload_times.log", "a") as log_file:
119
+ log_file.write(
120
+ f"{total_size} MB made of {n_files} files took {upload_duration:.2f} seconds.\n"
121
+ )
122
+
123
+ # assert dataset ingested
124
+ dataset = retrieve_dataset(name=name)
125
+ assert dataset["name"] == name
126
+ assert round(dataset["versions"][0]["size"] / (1024 * 1024), 2) == total_size
127
+
128
+ # check files in minio
129
+ minio_files = []
130
+ minio_sizes = []
131
+ for obj in setup_minio.list_objects("eotdl-test", recursive=True):
132
+ minio_files.append(obj.object_name)
133
+ minio_sizes.append(obj.size)
134
+ assert obj.object_name.endswith(("tif", "md", "parquet"))
135
+
136
+ assert len(minio_files) == n_files + 2
137
+ assert round(sum(minio_sizes) / (1024 * 1024)) == total_size
138
+
139
+
140
+ @pytest.mark.parametrize(
141
+ "tif_size, n_tifs",
142
+ [
143
+ # (256, 100),
144
+ # (512, 100),
145
+ # (1024, 100),
146
+ (2048, 200),
147
+ ],
148
+ )
149
+ def test_load_tifs(setup_mongo, setup_minio, tif_size, n_tifs):
150
+ name = f"LoadTest-{int(tif_size)}"
151
+ with tempfile.TemporaryDirectory(prefix="loadtest_") as tmpdir:
152
+ tmpdir = Path(tmpdir)
153
+ generate_fake_tif_dataset(
154
+ path=tmpdir, tif_size=tif_size, n_tifs=n_tifs, name=name
155
+ )
156
+
157
+ # upload
158
+ start_time = time.time()
159
+ ingest_dataset(path=str(tmpdir / name))
160
+
161
+ upload_duration = time.time() - start_time
162
+ print(f"Upload for {name} took {upload_duration:.2f} seconds.")
163
+
164
+ # assert dataset ingested
165
+ dataset = retrieve_dataset(name=name)
166
+ assert dataset["name"] == name
167
+
168
+ with open("eotdl/tests/load/upload_times_tifs.log", "a") as log_file:
169
+ log_file.write(
170
+ f"{tif_size}x {tif_size} made of {n_tifs} files took {upload_duration:.2f} seconds. ({round(dataset['versions'][0]['size'] / (1024 * 1024), 2)} mb)\n"
171
+ )
172
+
173
+ # check files in minio
174
+ minio_files = []
175
+ minio_sizes = []
176
+ for obj in setup_minio.list_objects("eotdl-test", recursive=True):
177
+ minio_files.append(obj.object_name)
178
+ minio_sizes.append(obj.size)
179
+ assert obj.object_name.endswith(("tif", "md", "parquet"))
180
+
181
+ if obj.object_name.endswith(".tif"):
182
+ file = setup_minio.get_object("eotdl-test", obj.object_name)
183
+ file_content = file.read()
184
+ with io.BytesIO(file_content) as byte_file:
185
+ with rasterio.open(byte_file) as src:
186
+ assert src.crs == "EPSG:4326"
187
+ assert src.width == src.height == tif_size
188
+ assert src.dtypes == ("uint32", "uint32", "uint32")
189
+
190
+ for band in range(1, 4):
191
+ colour = src.read(band)
192
+ assert set(colour.flatten()) == {np.uint32(256)}
193
+
194
+ assert len(minio_files) == n_tifs + 2
@@ -0,0 +1,6 @@
1
+ 1 MB made of 10 files took 0.46 seconds.
2
+ 10.0 MB made of 10 files took 0.44 seconds.
3
+ 1000.0 MB made of 1 files took 9.78 seconds.
4
+ 1000.0 MB made of 20 files took 10.35 seconds.
5
+ 10000.0 MB made of 100 files took 120.89 seconds.
6
+ 10000.0 MB made of 10 files took 133.24 seconds.
@@ -0,0 +1,5 @@
1
+ 256x 256 made of 100 files took 5.89 seconds. (75.11 mb)
2
+ 512x 512 made of 100 files took 8.48 seconds. (300.33 mb)
3
+ 1024x 1024 made of 100 files took 23.38 seconds. (1200.62 mb)
4
+ 2048x 2048 made of 100 files took 81.27 seconds. (4801.21 mb)
5
+ 2048x 2048 made of 200 files took 202.64 seconds. (9602.42 mb)
@@ -110,7 +110,7 @@ wheels = [
110
110
 
111
111
  [[package]]
112
112
  name = "eotdl"
113
- version = "2025.4.2.post4"
113
+ version = "2025.4.22"
114
114
  source = { editable = "." }
115
115
  dependencies = [
116
116
  { name = "geopandas" },
@@ -1 +0,0 @@
1
- __version__ = "2025.04.22"
@@ -1,35 +0,0 @@
1
- [project]
2
- name = "eotdl"
3
- version = "2025.04.02"
4
- description = "Earth Observation Training Data Lab"
5
- authors = [
6
- {name = "earthpulse", email = "it@earthpulse.es"},
7
- ]
8
- license = "MIT"
9
- readme = "README.md"
10
- requires-python = ">=3.8"
11
- dependencies = [
12
- "geomet>=1.1.0",
13
- "geopandas>=0.13.2",
14
- "markdown>=3.7",
15
- "pydantic>=2.10.6",
16
- "pyjwt>=2.9.0",
17
- "pystac>=1.8.4",
18
- "python-frontmatter>=1.1.0",
19
- "pyyaml>=6.0.2",
20
- "rasterio>=1.3.11",
21
- "requests>=2.32.3",
22
- "sentinelhub>=3.11.1",
23
- "tqdm>=4.67.1",
24
- "typer>=0.15.1",
25
- ]
26
-
27
- [project.scripts]
28
- eotdl = "eotdl.cli:app"
29
-
30
- [build-system]
31
- requires = ["hatchling"]
32
- build-backend = "hatchling.build"
33
-
34
- [tool.hatch.build.targets.wheel]
35
- packages = ["eotdl"]
File without changes
File without changes
File without changes
File without changes
File without changes