eotdl 2023.10.25.post10__py3-none-any.whl → 2023.11.2.post2__py3-none-any.whl
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.
- eotdl/__init__.py +1 -1
- eotdl/cli.py +6 -2
- eotdl/commands/auth.py +18 -1
- eotdl/commands/datasets.py +61 -11
- eotdl/commands/models.py +108 -0
- eotdl/curation/__init__.py +1 -4
- eotdl/curation/stac/assets.py +2 -1
- eotdl/curation/stac/dataframe.py +1 -1
- eotdl/curation/stac/extensions/label/image_name_labeler.py +6 -5
- eotdl/curation/stac/extensions/ml_dataset.py +15 -25
- eotdl/curation/stac/extent.py +1 -1
- eotdl/curation/stac/stac.py +1 -1
- eotdl/datasets/download.py +5 -4
- eotdl/datasets/ingest.py +25 -154
- eotdl/datasets/retrieve.py +1 -1
- eotdl/files/__init__.py +1 -0
- eotdl/files/ingest.py +175 -0
- eotdl/models/__init__.py +3 -0
- eotdl/models/download.py +119 -0
- eotdl/models/ingest.py +47 -0
- eotdl/models/metadata.py +16 -0
- eotdl/models/retrieve.py +26 -0
- eotdl/repos/FilesAPIRepo.py +136 -95
- eotdl/repos/ModelsAPIRepo.py +40 -0
- eotdl/repos/__init__.py +1 -0
- eotdl/shared/__init__.py +1 -0
- eotdl/tools/__init__.py +5 -6
- eotdl/tools/geo_utils.py +15 -1
- eotdl/tools/stac.py +144 -8
- eotdl/tools/time_utils.py +19 -6
- eotdl/tools/tools.py +2 -3
- {eotdl-2023.10.25.post10.dist-info → eotdl-2023.11.2.post2.dist-info}/METADATA +1 -1
- {eotdl-2023.10.25.post10.dist-info → eotdl-2023.11.2.post2.dist-info}/RECORD +38 -35
- eotdl/curation/folder_formatters/__init__.py +0 -1
- eotdl/curation/folder_formatters/base.py +0 -19
- eotdl/curation/folder_formatters/sentinel_hub.py +0 -135
- eotdl/curation/stac/utils/__init__.py +0 -5
- eotdl/curation/stac/utils/geometry.py +0 -22
- eotdl/curation/stac/utils/stac.py +0 -143
- eotdl/curation/stac/utils/time.py +0 -21
- /eotdl/{datasets/utils.py → shared/checksum.py} +0 -0
- /eotdl/{curation/stac/utils → tools}/metadata.py +0 -0
- /eotdl/{curation/stac/utils → tools}/paths.py +0 -0
- {eotdl-2023.10.25.post10.dist-info → eotdl-2023.11.2.post2.dist-info}/WHEEL +0 -0
- {eotdl-2023.10.25.post10.dist-info → eotdl-2023.11.2.post2.dist-info}/entry_points.txt +0 -0
eotdl/datasets/ingest.py
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
from pathlib import Path
|
2
|
-
from glob import glob
|
3
2
|
import yaml
|
4
|
-
from tqdm import tqdm
|
5
|
-
import os
|
6
3
|
|
7
4
|
from ..auth import with_auth
|
8
5
|
from .metadata import Metadata
|
9
|
-
from ..repos import DatasetsAPIRepo
|
10
|
-
from
|
6
|
+
from ..repos import DatasetsAPIRepo
|
7
|
+
from ..files import ingest_files
|
11
8
|
|
12
9
|
|
13
10
|
def ingest_dataset(path, verbose=False, logger=print):
|
@@ -19,160 +16,34 @@ def ingest_dataset(path, verbose=False, logger=print):
|
|
19
16
|
return ingest_folder(path, verbose, logger)
|
20
17
|
|
21
18
|
|
19
|
+
def retrieve_dataset(metadata, user):
|
20
|
+
repo = DatasetsAPIRepo()
|
21
|
+
data, error = repo.retrieve_dataset(metadata.name)
|
22
|
+
# print(data, error)
|
23
|
+
if data and data["uid"] != user["sub"]:
|
24
|
+
raise Exception("Dataset already exists.")
|
25
|
+
if error and error == "Dataset doesn't exist":
|
26
|
+
# create dataset
|
27
|
+
data, error = repo.create_dataset(metadata.dict(), user["id_token"])
|
28
|
+
# print(data, error)
|
29
|
+
if error:
|
30
|
+
raise Exception(error)
|
31
|
+
data["id"] = data["dataset_id"]
|
32
|
+
return data["id"]
|
33
|
+
|
34
|
+
|
22
35
|
@with_auth
|
23
36
|
def ingest_folder(folder, verbose=False, logger=print, user=None):
|
24
|
-
repo
|
25
|
-
logger(f"Uploading directory {folder}...")
|
26
|
-
# get all files in directory recursively
|
27
|
-
items = [Path(item) for item in glob(str(folder) + "/**/*", recursive=True)]
|
28
|
-
# remove directories
|
29
|
-
items = [item for item in items if not item.is_dir()]
|
30
|
-
if len(items) == 0:
|
31
|
-
raise Exception("No files found in directory")
|
32
|
-
if not any(item.name == "metadata.yml" for item in items):
|
33
|
-
raise Exception("metadata.yml not found in directory")
|
37
|
+
repo = DatasetsAPIRepo()
|
34
38
|
# load metadata
|
35
39
|
metadata = yaml.safe_load(open(folder.joinpath("metadata.yml"), "r").read()) or {}
|
36
40
|
metadata = Metadata(**metadata)
|
37
|
-
#
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
)
|
44
|
-
# create dataset
|
45
|
-
data, error = repo.create_dataset(metadata.dict(), user["id_token"])
|
46
|
-
# dataset may already exist, and will return an error, but if user is owner continue ingesting files
|
47
|
-
current_files = []
|
48
|
-
if error:
|
49
|
-
data, error2 = repo.retrieve_dataset(metadata.name)
|
50
|
-
if error2:
|
51
|
-
raise Exception(error)
|
52
|
-
if data["uid"] != user["sub"]:
|
53
|
-
raise Exception("Dataset already exists.")
|
54
|
-
data["dataset_id"] = data["id"]
|
55
|
-
dataset_id = data["dataset_id"]
|
56
|
-
# create new version
|
57
|
-
data, error = repo.create_version(dataset_id, user["id_token"])
|
58
|
-
if error:
|
59
|
-
raise Exception(error)
|
60
|
-
version = data["version"]
|
61
|
-
# upload files
|
62
|
-
current_files = []
|
63
|
-
if version > 1:
|
64
|
-
current_files, error = files_repo.retrieve_dataset_files(
|
65
|
-
dataset_id, version - 1
|
66
|
-
)
|
67
|
-
if error:
|
68
|
-
# print("retreive dataset files error: ", error)
|
69
|
-
current_files = []
|
70
|
-
for item in tqdm(items, desc="Uploading files", unit="files", disable=verbose):
|
71
|
-
data = ingest_file(
|
72
|
-
str(item),
|
73
|
-
dataset_id,
|
74
|
-
version,
|
75
|
-
str(item.relative_to(folder).parent),
|
76
|
-
logger=logger,
|
77
|
-
verbose=verbose,
|
78
|
-
user=user,
|
79
|
-
current_files=current_files,
|
80
|
-
)
|
81
|
-
return data
|
82
|
-
|
83
|
-
|
84
|
-
def ingest_file(
|
85
|
-
file,
|
86
|
-
dataset_id,
|
87
|
-
version,
|
88
|
-
parent,
|
89
|
-
logger=None,
|
90
|
-
verbose=True,
|
91
|
-
root=None,
|
92
|
-
user=None,
|
93
|
-
current_files=[],
|
94
|
-
):
|
95
|
-
id_token = user["id_token"]
|
96
|
-
if verbose:
|
97
|
-
logger(f"Uploading file {file}...")
|
98
|
-
repo = FilesAPIRepo()
|
99
|
-
if file.startswith("http://") or file.startswith("https://"):
|
100
|
-
raise NotImplementedError("URL ingestion not implemented yet")
|
101
|
-
# data, error = repo.ingest_file_url(file, dataset_id, id_token)
|
102
|
-
else:
|
103
|
-
file_path = Path(file)
|
104
|
-
if not file_path.is_absolute():
|
105
|
-
# file_path = glob(
|
106
|
-
# str(root) + "/**/" + os.path.basename(file_path),
|
107
|
-
# recursive=True,
|
108
|
-
# )
|
109
|
-
# if len(file_path) == 0:
|
110
|
-
# raise Exception(f"File {file} not found")
|
111
|
-
# elif len(file_path) > 1:
|
112
|
-
# raise Exception(f"Multiple files found for {file}")
|
113
|
-
# file_path = file_path[0]
|
114
|
-
file_path = str(file_path.absolute())
|
115
|
-
if verbose:
|
116
|
-
logger("Computing checksum...")
|
117
|
-
checksum = calculate_checksum(file_path)
|
118
|
-
# check if file already exists in dataset
|
119
|
-
filename = os.path.basename(file_path)
|
120
|
-
if parent != ".":
|
121
|
-
filename = parent + "/" + filename
|
122
|
-
if len(current_files) > 0:
|
123
|
-
matches = [
|
124
|
-
f
|
125
|
-
for f in current_files
|
126
|
-
if f["filename"] == filename and f["checksum"] == checksum
|
127
|
-
] # this could slow down ingestion in large datasets... should think of faster search algos, puede que sea mejor hacer el re-upload simplemente...
|
128
|
-
if len(matches) == 1:
|
129
|
-
if verbose:
|
130
|
-
print(f"File {file_path} already exists in dataset, skipping...")
|
131
|
-
data, error = repo.ingest_existing_file(
|
132
|
-
filename,
|
133
|
-
dataset_id,
|
134
|
-
version,
|
135
|
-
matches[0]["version"],
|
136
|
-
id_token,
|
137
|
-
checksum,
|
138
|
-
)
|
139
|
-
if error:
|
140
|
-
raise Exception(error)
|
141
|
-
if verbose:
|
142
|
-
logger("Done")
|
143
|
-
return data
|
144
|
-
if verbose:
|
145
|
-
logger("Ingesting file...")
|
146
|
-
filesize = os.path.getsize(file_path)
|
147
|
-
# ingest small file
|
148
|
-
if filesize < 1024 * 1024 * 16: # 16 MB
|
149
|
-
data, error = repo.ingest_file(
|
150
|
-
file_path,
|
151
|
-
dataset_id,
|
152
|
-
version,
|
153
|
-
parent,
|
154
|
-
id_token,
|
155
|
-
checksum,
|
156
|
-
)
|
157
|
-
if error:
|
158
|
-
raise Exception(error)
|
159
|
-
if verbose:
|
160
|
-
logger("Done")
|
161
|
-
return data
|
162
|
-
raise NotImplementedError("Large file ingestion not implemented yet")
|
163
|
-
# # ingest large file
|
164
|
-
# upload_id, parts = repo.prepare_large_upload(
|
165
|
-
# file_path, dataset_id, checksum, id_token
|
166
|
-
# )
|
167
|
-
# repo.ingest_large_dataset(file_path, upload_id, id_token, parts)
|
168
|
-
# if verbose:
|
169
|
-
# logger("\nCompleting upload...")
|
170
|
-
# data, error = repo.complete_upload(id_token, upload_id)
|
171
|
-
if error:
|
172
|
-
raise Exception(error)
|
173
|
-
if verbose:
|
174
|
-
logger("Done")
|
175
|
-
return data
|
41
|
+
# retrieve dataset (create if doesn't exist)
|
42
|
+
dataset_id = retrieve_dataset(metadata, user)
|
43
|
+
# ingest files
|
44
|
+
return ingest_files(
|
45
|
+
repo, dataset_id, folder, verbose, logger, user, endpoint="datasets"
|
46
|
+
)
|
176
47
|
|
177
48
|
|
178
49
|
# @with_auth
|
eotdl/datasets/retrieve.py
CHANGED
@@ -20,7 +20,7 @@ def retrieve_dataset(name):
|
|
20
20
|
|
21
21
|
def retrieve_dataset_files(dataset_id, version):
|
22
22
|
repo = FilesAPIRepo()
|
23
|
-
data, error = repo.
|
23
|
+
data, error = repo.retrieve_files(dataset_id, "datasets", version)
|
24
24
|
if error:
|
25
25
|
raise Exception(error)
|
26
26
|
return data
|
eotdl/files/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from .ingest import ingest_files
|
eotdl/files/ingest.py
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
import os
|
3
|
+
from tqdm import tqdm
|
4
|
+
import zipfile
|
5
|
+
import io
|
6
|
+
from glob import glob
|
7
|
+
import os
|
8
|
+
|
9
|
+
from ..repos import FilesAPIRepo
|
10
|
+
from ..shared import calculate_checksum
|
11
|
+
from ..shared import calculate_checksum
|
12
|
+
|
13
|
+
|
14
|
+
def retrieve_files(folder):
|
15
|
+
# get all files in directory recursively
|
16
|
+
items = [Path(item) for item in glob(str(folder) + "/**/*", recursive=True)]
|
17
|
+
if not any(item.name == "metadata.yml" for item in items):
|
18
|
+
raise Exception("metadata.yml not found in directory")
|
19
|
+
# remove directories
|
20
|
+
items = [item for item in items if not item.is_dir()]
|
21
|
+
if len(items) == 0:
|
22
|
+
raise Exception("No files found in directory")
|
23
|
+
return items
|
24
|
+
|
25
|
+
|
26
|
+
def prepare_item(item, folder):
|
27
|
+
return {
|
28
|
+
"filename": item.name,
|
29
|
+
"path": str(item.relative_to(folder)),
|
30
|
+
"absolute_path": item.absolute(),
|
31
|
+
"size": os.path.getsize(item.absolute()),
|
32
|
+
"checksum": calculate_checksum(item.absolute()),
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
def generate_batches(files, max_batch_size=1024 * 1024 * 10, max_batch_files=10):
|
37
|
+
batches = []
|
38
|
+
for item in tqdm(files):
|
39
|
+
if not batches:
|
40
|
+
batches.append([item])
|
41
|
+
continue
|
42
|
+
if max_batch_size:
|
43
|
+
size_check = sum([i["size"] for i in batches[-1]]) < max_batch_size
|
44
|
+
else:
|
45
|
+
size_check = True
|
46
|
+
if size_check and len(batches[-1]) < max_batch_files:
|
47
|
+
batches[-1].append(item)
|
48
|
+
else:
|
49
|
+
batches.append([item])
|
50
|
+
return batches
|
51
|
+
|
52
|
+
|
53
|
+
def compress_batch(batch):
|
54
|
+
memory_file = io.BytesIO()
|
55
|
+
with zipfile.ZipFile(memory_file, "w") as zf:
|
56
|
+
for f in batch:
|
57
|
+
zf.write(f["absolute_path"], arcname=f["path"])
|
58
|
+
memory_file.seek(0)
|
59
|
+
return memory_file
|
60
|
+
|
61
|
+
|
62
|
+
def generate_files_lists(
|
63
|
+
items, folder, dataset_or_model_id, endpoint, logger, max_size=1024 * 1024 * 16
|
64
|
+
):
|
65
|
+
files_repo = FilesAPIRepo()
|
66
|
+
current_files, error = files_repo.retrieve_files(
|
67
|
+
dataset_or_model_id, "models", endpoint
|
68
|
+
)
|
69
|
+
# print(len(current_files), len(items) - len(current_files))
|
70
|
+
# print(current_files, error)
|
71
|
+
if error:
|
72
|
+
current_files = []
|
73
|
+
# generate list of files to upload
|
74
|
+
logger("generating list of files to upload...")
|
75
|
+
upload_files, existing_files, large_files = [], [], []
|
76
|
+
current_names = [f["filename"] for f in current_files]
|
77
|
+
current_checksums = [f["checksum"] for f in current_files]
|
78
|
+
for item in tqdm(items):
|
79
|
+
data = prepare_item(item, folder)
|
80
|
+
if data["path"] in current_names and data["checksum"] in current_checksums:
|
81
|
+
existing_files.append(data)
|
82
|
+
else:
|
83
|
+
if data["size"] > max_size:
|
84
|
+
large_files.append(data)
|
85
|
+
else:
|
86
|
+
upload_files.append(data)
|
87
|
+
if len(upload_files) == 0 and len(large_files) == 0:
|
88
|
+
raise Exception("No files to upload")
|
89
|
+
return upload_files, existing_files, large_files
|
90
|
+
|
91
|
+
|
92
|
+
def create_new_version(repo, dataset_or_model_id, user):
|
93
|
+
data, error = repo.create_version(dataset_or_model_id, user["id_token"])
|
94
|
+
if error:
|
95
|
+
raise Exception(error)
|
96
|
+
return data["version"]
|
97
|
+
|
98
|
+
|
99
|
+
def ingest_files(repo, dataset_or_model_id, folder, verbose, logger, user, endpoint):
|
100
|
+
files_repo = FilesAPIRepo()
|
101
|
+
logger(f"Uploading directory {folder}...")
|
102
|
+
items = retrieve_files(folder)
|
103
|
+
# retrieve files
|
104
|
+
upload_files, existing_files, large_files = generate_files_lists(
|
105
|
+
items, folder, dataset_or_model_id, endpoint, logger
|
106
|
+
)
|
107
|
+
logger(f"{len(upload_files) + len(large_files)} new files will be ingested")
|
108
|
+
logger(f"{len(existing_files)} files already exist in dataset")
|
109
|
+
logger(f"{len(large_files)} large files will be ingested separately")
|
110
|
+
# create new version
|
111
|
+
version = create_new_version(repo, dataset_or_model_id, user)
|
112
|
+
logger("New version created, version: " + str(version))
|
113
|
+
# ingest new large files
|
114
|
+
if len(large_files) > 0:
|
115
|
+
logger("ingesting large files...")
|
116
|
+
for file in large_files:
|
117
|
+
logger("ingesting file: " + file["path"])
|
118
|
+
upload_id, parts = files_repo.prepare_large_upload(
|
119
|
+
file["path"],
|
120
|
+
dataset_or_model_id,
|
121
|
+
file["checksum"],
|
122
|
+
user["id_token"],
|
123
|
+
endpoint,
|
124
|
+
)
|
125
|
+
# print(upload_id, parts)
|
126
|
+
files_repo.ingest_large_file(
|
127
|
+
file["absolute_path"],
|
128
|
+
file["size"],
|
129
|
+
upload_id,
|
130
|
+
user["id_token"],
|
131
|
+
parts,
|
132
|
+
endpoint,
|
133
|
+
)
|
134
|
+
files_repo.complete_upload(user["id_token"], upload_id, version, endpoint)
|
135
|
+
# ingest new small files in batches
|
136
|
+
if len(upload_files) > 0:
|
137
|
+
logger("generating batches...")
|
138
|
+
batches = generate_batches(upload_files)
|
139
|
+
logger(
|
140
|
+
f"Uploading {len(upload_files)} small files in {len(batches)} batches..."
|
141
|
+
)
|
142
|
+
repo = FilesAPIRepo()
|
143
|
+
for batch in tqdm(
|
144
|
+
batches, desc="Uploading batches", unit="batches", disable=verbose
|
145
|
+
):
|
146
|
+
# compress batch
|
147
|
+
memory_file = compress_batch(batch)
|
148
|
+
# ingest batch
|
149
|
+
data, error = repo.ingest_files_batch(
|
150
|
+
memory_file,
|
151
|
+
[f["checksum"] for f in batch],
|
152
|
+
dataset_or_model_id,
|
153
|
+
user["id_token"],
|
154
|
+
endpoint,
|
155
|
+
version,
|
156
|
+
)
|
157
|
+
# ingest existing files
|
158
|
+
if len(existing_files) > 0:
|
159
|
+
batches = generate_batches(existing_files, max_batch_size=None)
|
160
|
+
for batch in tqdm(
|
161
|
+
batches,
|
162
|
+
desc="Ingesting existing files",
|
163
|
+
unit="batches",
|
164
|
+
disable=verbose,
|
165
|
+
):
|
166
|
+
data, error = files_repo.add_files_batch_to_version(
|
167
|
+
batch,
|
168
|
+
dataset_or_model_id,
|
169
|
+
version,
|
170
|
+
user["id_token"],
|
171
|
+
endpoint,
|
172
|
+
)
|
173
|
+
if error:
|
174
|
+
raise Exception(error)
|
175
|
+
return data
|
eotdl/models/__init__.py
ADDED
eotdl/models/download.py
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
from tqdm import tqdm
|
4
|
+
|
5
|
+
from ..auth import with_auth
|
6
|
+
from .retrieve import retrieve_model, retrieve_model_files
|
7
|
+
from ..shared import calculate_checksum
|
8
|
+
from ..repos import FilesAPIRepo
|
9
|
+
|
10
|
+
|
11
|
+
@with_auth
|
12
|
+
def download_model(
|
13
|
+
model_name,
|
14
|
+
version=None,
|
15
|
+
path=None,
|
16
|
+
logger=None,
|
17
|
+
assets=False,
|
18
|
+
force=False,
|
19
|
+
verbose=False,
|
20
|
+
user=None,
|
21
|
+
file=None,
|
22
|
+
):
|
23
|
+
model = retrieve_model(model_name)
|
24
|
+
if version is None:
|
25
|
+
version = sorted(model["versions"], key=lambda v: v["version_id"])[-1][
|
26
|
+
"version_id"
|
27
|
+
]
|
28
|
+
else:
|
29
|
+
assert version in [
|
30
|
+
v["version_id"] for v in model["versions"]
|
31
|
+
], f"Version {version} not found"
|
32
|
+
download_base_path = os.getenv(
|
33
|
+
"EOTDL_DOWNLOAD_PATH", str(Path.home()) + "/.cache/eotdl/models"
|
34
|
+
)
|
35
|
+
if path is None:
|
36
|
+
download_path = download_base_path + "/" + model_name + "/v" + str(version)
|
37
|
+
else:
|
38
|
+
download_path = path + "/" + model_name + "/v" + str(version)
|
39
|
+
# check if model already exists
|
40
|
+
if os.path.exists(download_path) and not force:
|
41
|
+
os.makedirs(download_path, exist_ok=True)
|
42
|
+
raise Exception(
|
43
|
+
f"model `{model['name']} v{str(version)}` already exists at {download_path}. To force download, use force=True or -f in the CLI."
|
44
|
+
)
|
45
|
+
if model["quality"] == 0:
|
46
|
+
if file:
|
47
|
+
raise NotImplementedError("Downloading a specific file is not implemented")
|
48
|
+
# files = [f for f in model["files"] if f["name"] == file]
|
49
|
+
# if not files:
|
50
|
+
# raise Exception(f"File {file} not found")
|
51
|
+
# if len(files) > 1:
|
52
|
+
# raise Exception(f"Multiple files with name {file} found")
|
53
|
+
# dst_path = download(
|
54
|
+
# model,
|
55
|
+
# model["id"],
|
56
|
+
# file,
|
57
|
+
# files[0]["checksum"],
|
58
|
+
# download_path,
|
59
|
+
# user,
|
60
|
+
# )
|
61
|
+
# return Outputs(dst_path=dst_path)
|
62
|
+
model_files = retrieve_model_files(model["id"], version)
|
63
|
+
repo = FilesAPIRepo()
|
64
|
+
for file in tqdm(model_files, disable=verbose, unit="file"):
|
65
|
+
filename, file_version = file["filename"], file["version"]
|
66
|
+
if verbose:
|
67
|
+
logger(f"Downloading {file['filename']}...")
|
68
|
+
dst_path = repo.download_file(
|
69
|
+
model["id"],
|
70
|
+
filename,
|
71
|
+
user["id_token"],
|
72
|
+
download_path,
|
73
|
+
file_version,
|
74
|
+
endpoint="models",
|
75
|
+
)
|
76
|
+
# if calculate_checksum(dst_path) != checksum:
|
77
|
+
# logger(f"Checksum for {file} does not match")
|
78
|
+
if verbose:
|
79
|
+
logger(f"Done")
|
80
|
+
return "/".join(dst_path.split("/")[:-1])
|
81
|
+
else:
|
82
|
+
raise NotImplementedError("Downloading a STAC model is not implemented")
|
83
|
+
# logger("Downloading STAC metadata...")
|
84
|
+
# gdf, error = repo.download_stac(
|
85
|
+
# model["id"],
|
86
|
+
# user["id_token"],
|
87
|
+
# )
|
88
|
+
# if error:
|
89
|
+
# raise Exception(error)
|
90
|
+
# df = STACDataFrame(gdf)
|
91
|
+
# # df.geometry = df.geometry.apply(lambda x: Polygon() if x is None else x)
|
92
|
+
# path = path
|
93
|
+
# if path is None:
|
94
|
+
# path = download_base_path + "/" + model["name"]
|
95
|
+
# df.to_stac(path)
|
96
|
+
# # download assets
|
97
|
+
# if assets:
|
98
|
+
# logger("Downloading assets...")
|
99
|
+
# df = df.dropna(subset=["assets"])
|
100
|
+
# for row in tqdm(df.iterrows(), total=len(df)):
|
101
|
+
# id = row[1]["stac_id"]
|
102
|
+
# # print(row[1]["links"])
|
103
|
+
# for k, v in row[1]["assets"].items():
|
104
|
+
# href = v["href"]
|
105
|
+
# repo.download_file_url(
|
106
|
+
# href, f"{path}/assets/{id}", user["id_token"]
|
107
|
+
# )
|
108
|
+
# else:
|
109
|
+
# logger("To download assets, set assets=True or -a in the CLI.")
|
110
|
+
# return Outputs(dst_path=path)
|
111
|
+
|
112
|
+
|
113
|
+
# @with_auth
|
114
|
+
# def download_file_url(url, path, progress=True, logger=None, user=None):
|
115
|
+
# api_repo = APIRepo()
|
116
|
+
# download = DownloadFileURL(api_repo, logger, progress)
|
117
|
+
# inputs = DownloadFileURL.Inputs(url=url, path=path, user=user)
|
118
|
+
# outputs = download(inputs)
|
119
|
+
# return outputs.dst_path
|
eotdl/models/ingest.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
import yaml
|
3
|
+
|
4
|
+
from ..auth import with_auth
|
5
|
+
from .metadata import Metadata
|
6
|
+
from ..repos import ModelsAPIRepo
|
7
|
+
from ..shared import calculate_checksum
|
8
|
+
from ..files import ingest_files
|
9
|
+
|
10
|
+
|
11
|
+
def ingest_model(path, verbose=False, logger=print):
|
12
|
+
path = Path(path)
|
13
|
+
if not path.is_dir():
|
14
|
+
raise Exception("Path must be a folder")
|
15
|
+
# if "catalog.json" in [f.name for f in path.iterdir()]:
|
16
|
+
# return ingest_stac(path / "catalog.json", logger)
|
17
|
+
return ingest_folder(path, verbose, logger)
|
18
|
+
|
19
|
+
|
20
|
+
def retrieve_model(metadata, user):
|
21
|
+
repo = ModelsAPIRepo()
|
22
|
+
data, error = repo.retrieve_model(metadata.name)
|
23
|
+
# print(data, error)
|
24
|
+
if data and data["uid"] != user["sub"]:
|
25
|
+
raise Exception("Model already exists.")
|
26
|
+
if error and error == "Model doesn't exist":
|
27
|
+
# create dataset
|
28
|
+
data, error = repo.create_model(metadata.dict(), user["id_token"])
|
29
|
+
# print(data, error)
|
30
|
+
if error:
|
31
|
+
raise Exception(error)
|
32
|
+
data["id"] = data["model_id"]
|
33
|
+
return data["id"]
|
34
|
+
|
35
|
+
|
36
|
+
@with_auth
|
37
|
+
def ingest_folder(folder, verbose=False, logger=print, user=None):
|
38
|
+
repo = ModelsAPIRepo()
|
39
|
+
# load metadata
|
40
|
+
metadata = yaml.safe_load(open(folder.joinpath("metadata.yml"), "r").read()) or {}
|
41
|
+
metadata = Metadata(**metadata)
|
42
|
+
# retrieve model (create if doesn't exist)
|
43
|
+
model_id = retrieve_model(metadata, user)
|
44
|
+
# ingest files
|
45
|
+
return ingest_files(
|
46
|
+
repo, model_id, folder, verbose, logger, user, endpoint="models"
|
47
|
+
)
|
eotdl/models/metadata.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from pydantic import BaseModel, validator
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
|
5
|
+
class Metadata(BaseModel):
|
6
|
+
authors: List[str]
|
7
|
+
license: str
|
8
|
+
source: str
|
9
|
+
name: str
|
10
|
+
|
11
|
+
# validate source is a URL
|
12
|
+
@validator("source")
|
13
|
+
def source_is_url(cls, v):
|
14
|
+
if not v.startswith("http") and not v.startswith("https"):
|
15
|
+
raise ValueError("source must be a URL")
|
16
|
+
return v
|
eotdl/models/retrieve.py
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
from ..repos import ModelsAPIRepo, FilesAPIRepo
|
2
|
+
|
3
|
+
|
4
|
+
def retrieve_models(name=None, limit=None):
|
5
|
+
api_repo = ModelsAPIRepo()
|
6
|
+
data, error = api_repo.retrieve_models(name, limit)
|
7
|
+
if data and not error:
|
8
|
+
models = [d["name"] for d in data] if data else []
|
9
|
+
return models
|
10
|
+
return []
|
11
|
+
|
12
|
+
|
13
|
+
def retrieve_model(name):
|
14
|
+
repo = ModelsAPIRepo()
|
15
|
+
data, error = repo.retrieve_model(name)
|
16
|
+
if error:
|
17
|
+
raise Exception(error)
|
18
|
+
return data
|
19
|
+
|
20
|
+
|
21
|
+
def retrieve_model_files(model_id, version):
|
22
|
+
repo = FilesAPIRepo()
|
23
|
+
data, error = repo.retrieve_files(model_id, "models", version)
|
24
|
+
if error:
|
25
|
+
raise Exception(error)
|
26
|
+
return data
|