freva-client 2410.0.0b1__tar.gz → 2410.0.1__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.
Potentially problematic release.
This version of freva-client might be problematic. Click here for more details.
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/PKG-INFO +2 -1
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/pyproject.toml +1 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/__init__.py +1 -1
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/auth_cli.py +3 -1
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/databrowser_cli.py +27 -114
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/query.py +43 -165
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/utils/databrowser_utils.py +12 -10
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/MANIFEST.in +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/README.md +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/assets/share/freva/freva.toml +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/__main__.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/auth.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/__init__.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/cli_app.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/cli_parser.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/cli/cli_utils.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/py.typed +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/utils/__init__.py +0 -0
- {freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/utils/logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: freva-client
|
|
3
|
-
Version: 2410.0.
|
|
3
|
+
Version: 2410.0.1
|
|
4
4
|
Summary: Search for climate data based on key-value pairs
|
|
5
5
|
Author-email: "DKRZ, Clint" <freva@dkrz.de>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -22,6 +22,7 @@ Requires-Dist: authlib
|
|
|
22
22
|
Requires-Dist: requests
|
|
23
23
|
Requires-Dist: intake_esm
|
|
24
24
|
Requires-Dist: rich
|
|
25
|
+
Requires-Dist: setuptools
|
|
25
26
|
Requires-Dist: tomli
|
|
26
27
|
Requires-Dist: typer
|
|
27
28
|
Requires-Dist: tox ; extra == "dev"
|
|
@@ -49,7 +49,9 @@ def authenticate_cli(
|
|
|
49
49
|
"-f",
|
|
50
50
|
help="Force token recreation, even if current token is still valid.",
|
|
51
51
|
),
|
|
52
|
-
verbose: int = typer.Option(
|
|
52
|
+
verbose: int = typer.Option(
|
|
53
|
+
0, "-v", help="Increase verbosity", count=True
|
|
54
|
+
),
|
|
53
55
|
version: Optional[bool] = typer.Option(
|
|
54
56
|
False,
|
|
55
57
|
"-V",
|
|
@@ -163,7 +163,9 @@ def metadata_search(
|
|
|
163
163
|
parse_json: bool = typer.Option(
|
|
164
164
|
False, "-j", "--json", help="Parse output in json format."
|
|
165
165
|
),
|
|
166
|
-
verbose: int = typer.Option(
|
|
166
|
+
verbose: int = typer.Option(
|
|
167
|
+
0, "-v", help="Increase verbosity", count=True
|
|
168
|
+
),
|
|
167
169
|
version: Optional[bool] = typer.Option(
|
|
168
170
|
False,
|
|
169
171
|
"-V",
|
|
@@ -185,7 +187,9 @@ def metadata_search(
|
|
|
185
187
|
result = databrowser.metadata_search(
|
|
186
188
|
*(facets or []),
|
|
187
189
|
time=time or "",
|
|
188
|
-
time_select=cast(
|
|
190
|
+
time_select=cast(
|
|
191
|
+
Literal["file", "flexible", "strict"], time_select.value
|
|
192
|
+
),
|
|
189
193
|
flavour=cast(
|
|
190
194
|
Literal["freva", "cmip6", "cmip5", "cordex", "nextgems"],
|
|
191
195
|
flavour.value,
|
|
@@ -249,7 +253,9 @@ def data_search(
|
|
|
249
253
|
"--time-select",
|
|
250
254
|
help=TimeSelect.get_help(),
|
|
251
255
|
),
|
|
252
|
-
zarr: bool = typer.Option(
|
|
256
|
+
zarr: bool = typer.Option(
|
|
257
|
+
False, "--zarr", help="Create zarr stream files."
|
|
258
|
+
),
|
|
253
259
|
access_token: Optional[str] = typer.Option(
|
|
254
260
|
None,
|
|
255
261
|
"--access-token",
|
|
@@ -283,7 +289,9 @@ def data_search(
|
|
|
283
289
|
"the hostname is read from a config file"
|
|
284
290
|
),
|
|
285
291
|
),
|
|
286
|
-
verbose: int = typer.Option(
|
|
292
|
+
verbose: int = typer.Option(
|
|
293
|
+
0, "-v", help="Increase verbosity", count=True
|
|
294
|
+
),
|
|
287
295
|
multiversion: bool = typer.Option(
|
|
288
296
|
False,
|
|
289
297
|
"--multi-version",
|
|
@@ -417,7 +425,9 @@ def intake_catalogue(
|
|
|
417
425
|
"the hostname is read from a config file"
|
|
418
426
|
),
|
|
419
427
|
),
|
|
420
|
-
verbose: int = typer.Option(
|
|
428
|
+
verbose: int = typer.Option(
|
|
429
|
+
0, "-v", help="Increase verbosity", count=True
|
|
430
|
+
),
|
|
421
431
|
multiversion: bool = typer.Option(
|
|
422
432
|
False,
|
|
423
433
|
"--multi-version",
|
|
@@ -459,7 +469,9 @@ def intake_catalogue(
|
|
|
459
469
|
print(Path(temp_f.name).read_text())
|
|
460
470
|
|
|
461
471
|
|
|
462
|
-
@databrowser_app.command(
|
|
472
|
+
@databrowser_app.command(
|
|
473
|
+
name="data-count", help="Count the databrowser search results"
|
|
474
|
+
)
|
|
463
475
|
@exception_handler
|
|
464
476
|
def count_values(
|
|
465
477
|
search_keys: Optional[List[str]] = typer.Argument(
|
|
@@ -535,7 +547,9 @@ def count_values(
|
|
|
535
547
|
parse_json: bool = typer.Option(
|
|
536
548
|
False, "-j", "--json", help="Parse output in json format."
|
|
537
549
|
),
|
|
538
|
-
verbose: int = typer.Option(
|
|
550
|
+
verbose: int = typer.Option(
|
|
551
|
+
0, "-v", help="Increase verbosity", count=True
|
|
552
|
+
),
|
|
539
553
|
version: Optional[bool] = typer.Option(
|
|
540
554
|
False,
|
|
541
555
|
"-V",
|
|
@@ -562,7 +576,9 @@ def count_values(
|
|
|
562
576
|
result = databrowser.count_values(
|
|
563
577
|
*facets,
|
|
564
578
|
time=time or "",
|
|
565
|
-
time_select=cast(
|
|
579
|
+
time_select=cast(
|
|
580
|
+
Literal["file", "flexible", "strict"], time_select
|
|
581
|
+
),
|
|
566
582
|
flavour=cast(
|
|
567
583
|
Literal["freva", "cmip6", "cmip5", "cordex", "nextgems"],
|
|
568
584
|
flavour.value,
|
|
@@ -578,7 +594,9 @@ def count_values(
|
|
|
578
594
|
databrowser(
|
|
579
595
|
*facets,
|
|
580
596
|
time=time or "",
|
|
581
|
-
time_select=cast(
|
|
597
|
+
time_select=cast(
|
|
598
|
+
Literal["file", "flexible", "strict"], time_select
|
|
599
|
+
),
|
|
582
600
|
flavour=cast(
|
|
583
601
|
Literal["freva", "cmip6", "cmip5", "cordex", "nextgems"],
|
|
584
602
|
flavour.value,
|
|
@@ -602,108 +620,3 @@ def count_values(
|
|
|
602
620
|
print(f"{key}: {', '.join(counts)}")
|
|
603
621
|
else:
|
|
604
622
|
print(result)
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
user_data_app = typer.Typer(help="Add or delete user data.")
|
|
608
|
-
databrowser_app.add_typer(user_data_app, name="user-data")
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
@user_data_app.command(name="add", help="Add user data into the databrowser.")
|
|
612
|
-
@exception_handler
|
|
613
|
-
def user_data_add(
|
|
614
|
-
username: str = typer.Argument(..., help="Username of the data owner"),
|
|
615
|
-
paths: List[str] = typer.Option(
|
|
616
|
-
...,
|
|
617
|
-
"--path",
|
|
618
|
-
"-p",
|
|
619
|
-
help="Paths to the user's data to be added.",
|
|
620
|
-
),
|
|
621
|
-
facets: Optional[List[str]] = typer.Option(
|
|
622
|
-
None,
|
|
623
|
-
"--facet",
|
|
624
|
-
"-f",
|
|
625
|
-
help="Facet key-value pairs for metadata in the format key=value.",
|
|
626
|
-
),
|
|
627
|
-
host: Optional[str] = typer.Option(
|
|
628
|
-
None,
|
|
629
|
-
"--host",
|
|
630
|
-
help=(
|
|
631
|
-
"Set the hostname of the databrowser. If not set (default), "
|
|
632
|
-
"the hostname is read from a config file."
|
|
633
|
-
),
|
|
634
|
-
),
|
|
635
|
-
access_token: Optional[str] = typer.Option(
|
|
636
|
-
None,
|
|
637
|
-
"--access-token",
|
|
638
|
-
help="Access token for authentication when adding user data.",
|
|
639
|
-
),
|
|
640
|
-
verbose: int = typer.Option(0, "-v", help="Increase verbosity", count=True),
|
|
641
|
-
) -> None:
|
|
642
|
-
"""Add user data into the databrowser."""
|
|
643
|
-
logger.set_verbosity(verbose)
|
|
644
|
-
logger.debug("Checking if the user has the right to add data")
|
|
645
|
-
result = databrowser(host=host)
|
|
646
|
-
_auth(result._cfg.auth_url, access_token)
|
|
647
|
-
|
|
648
|
-
facet_dict = {}
|
|
649
|
-
if facets:
|
|
650
|
-
for facet in facets:
|
|
651
|
-
if "=" not in facet:
|
|
652
|
-
logger.error(
|
|
653
|
-
f"Invalid facet format: {facet}. Expected format: key=value."
|
|
654
|
-
)
|
|
655
|
-
raise typer.Exit(code=1)
|
|
656
|
-
key, value = facet.split("=", 1)
|
|
657
|
-
facet_dict[key] = value
|
|
658
|
-
|
|
659
|
-
logger.debug(
|
|
660
|
-
f"Adding user data for {username} with paths {paths} and facets {facet_dict}"
|
|
661
|
-
)
|
|
662
|
-
result.add_user_data(username=username, paths=paths, facets=facet_dict)
|
|
663
|
-
logger.info("User data started crawling. Check the Databrowser to see the updates.")
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
@user_data_app.command(name="delete", help="Delete user data from the databrowser.")
|
|
667
|
-
@exception_handler
|
|
668
|
-
def user_data_remove(
|
|
669
|
-
username: str = typer.Argument(..., help="Username of the data owner"),
|
|
670
|
-
search_keys: List[str] = typer.Option(
|
|
671
|
-
None,
|
|
672
|
-
"--search-key",
|
|
673
|
-
"-s",
|
|
674
|
-
help="Search keys for the data to be deleted in the format key=value.",
|
|
675
|
-
),
|
|
676
|
-
host: Optional[str] = typer.Option(
|
|
677
|
-
None,
|
|
678
|
-
"--host",
|
|
679
|
-
help=(
|
|
680
|
-
"Set the hostname of the databrowser. If not set (default), "
|
|
681
|
-
"the hostname is read from a config file."
|
|
682
|
-
),
|
|
683
|
-
),
|
|
684
|
-
access_token: Optional[str] = typer.Option(
|
|
685
|
-
None,
|
|
686
|
-
"--access-token",
|
|
687
|
-
help="Access token for authentication when deleting user data.",
|
|
688
|
-
),
|
|
689
|
-
verbose: int = typer.Option(0, "-v", help="Increase verbosity", count=True),
|
|
690
|
-
) -> None:
|
|
691
|
-
"""Delete user data from the databrowser."""
|
|
692
|
-
logger.set_verbosity(verbose)
|
|
693
|
-
logger.debug("Checking if the user has the right to delete data")
|
|
694
|
-
result = databrowser(host=host)
|
|
695
|
-
_auth(result._cfg.auth_url, access_token)
|
|
696
|
-
|
|
697
|
-
search_key_dict = {}
|
|
698
|
-
if search_keys:
|
|
699
|
-
for search_key in search_keys:
|
|
700
|
-
if "=" not in search_key:
|
|
701
|
-
logger.error(
|
|
702
|
-
f"Invalid search key format: {search_key}. "
|
|
703
|
-
"Expected format: key=value."
|
|
704
|
-
)
|
|
705
|
-
raise typer.Exit(code=1)
|
|
706
|
-
key, value = search_key.split("=", 1)
|
|
707
|
-
search_key_dict[key] = value
|
|
708
|
-
result.delete_user_data(username=username, search_keys=search_key_dict)
|
|
709
|
-
logger.info("User data deleted successfully.")
|
|
@@ -206,7 +206,9 @@ class databrowser:
|
|
|
206
206
|
self,
|
|
207
207
|
*facets: str,
|
|
208
208
|
uniq_key: Literal["file", "uri"] = "file",
|
|
209
|
-
flavour: Literal[
|
|
209
|
+
flavour: Literal[
|
|
210
|
+
"freva", "cmip6", "cmip5", "cordex", "nextgems"
|
|
211
|
+
] = "freva",
|
|
210
212
|
time: Optional[str] = None,
|
|
211
213
|
host: Optional[str] = None,
|
|
212
214
|
time_select: Literal["flexible", "strict", "file"] = "flexible",
|
|
@@ -241,7 +243,8 @@ class databrowser:
|
|
|
241
243
|
self, facets: Tuple[str, ...], search_kw: Dict[str, List[str]]
|
|
242
244
|
) -> None:
|
|
243
245
|
metadata = {
|
|
244
|
-
k: v[::2]
|
|
246
|
+
k: v[::2]
|
|
247
|
+
for (k, v) in self._facet_search(extended_search=True).items()
|
|
245
248
|
}
|
|
246
249
|
primary_key = list(metadata.keys() or ["project"])[0]
|
|
247
250
|
num_facets = 0
|
|
@@ -264,7 +267,9 @@ class databrowser:
|
|
|
264
267
|
headers = {}
|
|
265
268
|
if self._stream_zarr:
|
|
266
269
|
query_url = self._cfg.zarr_loader_url
|
|
267
|
-
token = self._auth.check_authentication(
|
|
270
|
+
token = self._auth.check_authentication(
|
|
271
|
+
auth_url=self._cfg.auth_url
|
|
272
|
+
)
|
|
268
273
|
headers = {"Authorization": f"Bearer {token['access_token']}"}
|
|
269
274
|
result = self._get(query_url, headers=headers, stream=True)
|
|
270
275
|
if result is not None:
|
|
@@ -272,7 +277,9 @@ class databrowser:
|
|
|
272
277
|
for res in result.iter_lines():
|
|
273
278
|
yield res.decode("utf-8")
|
|
274
279
|
except KeyboardInterrupt:
|
|
275
|
-
pprint(
|
|
280
|
+
pprint(
|
|
281
|
+
"[red][b]User interrupt: Exit[/red][/b]", file=sys.stderr
|
|
282
|
+
)
|
|
276
283
|
|
|
277
284
|
def __repr__(self) -> str:
|
|
278
285
|
params = ", ".join(
|
|
@@ -299,7 +306,9 @@ class databrowser:
|
|
|
299
306
|
|
|
300
307
|
# Create a table-like structure for available flavors and search facets
|
|
301
308
|
style = 'style="text-align: left"'
|
|
302
|
-
facet_heading =
|
|
309
|
+
facet_heading = (
|
|
310
|
+
f"Available search facets for <em>{self._flavour}</em> flavour"
|
|
311
|
+
)
|
|
303
312
|
html_repr = (
|
|
304
313
|
"<table>"
|
|
305
314
|
f"<tr><th colspan='2' {style}>{self.__class__.__name__}"
|
|
@@ -338,9 +347,13 @@ class databrowser:
|
|
|
338
347
|
kwargs: Dict[str, Any] = {"stream": True}
|
|
339
348
|
url = self._cfg.intake_url
|
|
340
349
|
if self._stream_zarr:
|
|
341
|
-
token = self._auth.check_authentication(
|
|
350
|
+
token = self._auth.check_authentication(
|
|
351
|
+
auth_url=self._cfg.auth_url
|
|
352
|
+
)
|
|
342
353
|
url = self._cfg.zarr_loader_url
|
|
343
|
-
kwargs["headers"] = {
|
|
354
|
+
kwargs["headers"] = {
|
|
355
|
+
"Authorization": f"Bearer {token['access_token']}"
|
|
356
|
+
}
|
|
344
357
|
kwargs["params"] = {"catalogue-type": "intake"}
|
|
345
358
|
result = self._get(url, **kwargs)
|
|
346
359
|
if result is None:
|
|
@@ -352,7 +365,9 @@ class databrowser:
|
|
|
352
365
|
for content in result.iter_content(decode_unicode=False):
|
|
353
366
|
stream.write(content)
|
|
354
367
|
except Exception as error:
|
|
355
|
-
raise ValueError(
|
|
368
|
+
raise ValueError(
|
|
369
|
+
f"Couldn't write catalogue content: {error}"
|
|
370
|
+
) from None
|
|
356
371
|
|
|
357
372
|
def intake_catalogue(self) -> intake_esm.core.esm_datastore:
|
|
358
373
|
"""Create an intake esm catalogue object from the search.
|
|
@@ -389,7 +404,9 @@ class databrowser:
|
|
|
389
404
|
def count_values(
|
|
390
405
|
cls,
|
|
391
406
|
*facets: str,
|
|
392
|
-
flavour: Literal[
|
|
407
|
+
flavour: Literal[
|
|
408
|
+
"freva", "cmip6", "cmip5", "cordex", "nextgems"
|
|
409
|
+
] = "freva",
|
|
393
410
|
time: Optional[str] = None,
|
|
394
411
|
host: Optional[str] = None,
|
|
395
412
|
time_select: Literal["flexible", "strict", "file"] = "flexible",
|
|
@@ -487,7 +504,9 @@ class databrowser:
|
|
|
487
504
|
result = this._facet_search(extended_search=extended_search)
|
|
488
505
|
counts = {}
|
|
489
506
|
for facet, value_counts in result.items():
|
|
490
|
-
counts[facet] = dict(
|
|
507
|
+
counts[facet] = dict(
|
|
508
|
+
zip(value_counts[::2], map(int, value_counts[1::2]))
|
|
509
|
+
)
|
|
491
510
|
return counts
|
|
492
511
|
|
|
493
512
|
@cached_property
|
|
@@ -512,14 +531,17 @@ class databrowser:
|
|
|
512
531
|
|
|
513
532
|
"""
|
|
514
533
|
return {
|
|
515
|
-
k: v[::2]
|
|
534
|
+
k: v[::2]
|
|
535
|
+
for (k, v) in self._facet_search(extended_search=True).items()
|
|
516
536
|
}
|
|
517
537
|
|
|
518
538
|
@classmethod
|
|
519
539
|
def metadata_search(
|
|
520
540
|
cls,
|
|
521
541
|
*facets: str,
|
|
522
|
-
flavour: Literal[
|
|
542
|
+
flavour: Literal[
|
|
543
|
+
"freva", "cmip6", "cmip5", "cordex", "nextgems"
|
|
544
|
+
] = "freva",
|
|
523
545
|
time: Optional[str] = None,
|
|
524
546
|
host: Optional[str] = None,
|
|
525
547
|
time_select: Literal["flexible", "strict", "file"] = "flexible",
|
|
@@ -642,7 +664,9 @@ class databrowser:
|
|
|
642
664
|
)
|
|
643
665
|
return {
|
|
644
666
|
k: v[::2]
|
|
645
|
-
for (k, v) in this._facet_search(
|
|
667
|
+
for (k, v) in this._facet_search(
|
|
668
|
+
extended_search=extended_search
|
|
669
|
+
).items()
|
|
646
670
|
}
|
|
647
671
|
|
|
648
672
|
@classmethod
|
|
@@ -709,172 +733,26 @@ class databrowser:
|
|
|
709
733
|
constraints = data["primary_facets"]
|
|
710
734
|
return {f: v for f, v in data["facets"].items() if f in constraints}
|
|
711
735
|
|
|
712
|
-
def
|
|
713
|
-
self,
|
|
714
|
-
) ->
|
|
715
|
-
"""Add user data to the databrowser.
|
|
716
|
-
|
|
717
|
-
Via this functionality, user would be able to add data to the databrowser.
|
|
718
|
-
It accepts file paths and metadata facets to categorize and store the user's
|
|
719
|
-
data.
|
|
720
|
-
|
|
721
|
-
Parameters
|
|
722
|
-
~~~~~~~~~~
|
|
723
|
-
username: str
|
|
724
|
-
The username of user.
|
|
725
|
-
paths: list[str]
|
|
726
|
-
A list of paths to the data files that should be uploaded or cataloged.
|
|
727
|
-
facets: dict[str, str]
|
|
728
|
-
A dictionary containing metadata facets (key-value pairs) to describe the
|
|
729
|
-
data.
|
|
730
|
-
|
|
731
|
-
Returns
|
|
732
|
-
~~~~~~~~
|
|
733
|
-
None
|
|
734
|
-
If the operation is successful, no return value is provided.
|
|
735
|
-
|
|
736
|
-
Raises
|
|
737
|
-
~~~~~~~
|
|
738
|
-
ValueError
|
|
739
|
-
If the operation fails to add the user data.
|
|
740
|
-
|
|
741
|
-
Example
|
|
742
|
-
~~~~~~~
|
|
743
|
-
.. execute_code::
|
|
744
|
-
|
|
745
|
-
from freva_client import authenticate, databrowser
|
|
746
|
-
token_info = authenticate(username="janedoe")
|
|
747
|
-
db = databrowser()
|
|
748
|
-
db.add_user_data(
|
|
749
|
-
"janedoe",
|
|
750
|
-
["."],
|
|
751
|
-
{"project": "cmip5", "experiment": "something"}
|
|
752
|
-
)
|
|
753
|
-
"""
|
|
754
|
-
url = f"{self._cfg.userdata_url}/{username}"
|
|
755
|
-
token = self._auth.check_authentication(auth_url=self._cfg.auth_url)
|
|
756
|
-
headers = {"Authorization": f"Bearer {token['access_token']}"}
|
|
757
|
-
params = {"paths": paths}
|
|
758
|
-
if "username" in facets:
|
|
759
|
-
del facets["username"]
|
|
760
|
-
data = facets
|
|
761
|
-
result = self._put(url, data=data, headers=headers, params=params)
|
|
762
|
-
|
|
763
|
-
if result is None:
|
|
764
|
-
raise ValueError("Failed to add user data")
|
|
765
|
-
|
|
766
|
-
def delete_user_data(self, username: str, search_keys: Dict[str, str]) -> None:
|
|
767
|
-
"""
|
|
768
|
-
Delete user data from the databrowser.
|
|
769
|
-
|
|
770
|
-
Uing this, user would be able to delete the user's data from the databrowser
|
|
771
|
-
based on the provided search keys.
|
|
772
|
-
|
|
773
|
-
Parameters
|
|
774
|
-
~~~~~~~~~~
|
|
775
|
-
username: str
|
|
776
|
-
The username associated with the data to be deleted.
|
|
777
|
-
search_keys: dict[str, str]
|
|
778
|
-
A dictionary containing the search keys to identify the data to be deleted.
|
|
779
|
-
|
|
780
|
-
Returns
|
|
781
|
-
~~~~~~~~
|
|
782
|
-
None
|
|
783
|
-
If the operation is successful, no return value is provided.
|
|
784
|
-
|
|
785
|
-
Raises
|
|
786
|
-
~~~~~~~
|
|
787
|
-
ValueError
|
|
788
|
-
If the operation fails to delete the user data.
|
|
789
|
-
|
|
790
|
-
Example
|
|
791
|
-
~~~~~~~
|
|
792
|
-
.. execute_code::
|
|
793
|
-
|
|
794
|
-
from freva_client import databrowser, authenticate
|
|
795
|
-
token_info = authenticate(username="janedoe")
|
|
796
|
-
db = databrowser()
|
|
797
|
-
db.delete_user_data(
|
|
798
|
-
"janedoe",
|
|
799
|
-
{"project": "cmip5", "experiment": "something"}
|
|
800
|
-
)
|
|
801
|
-
"""
|
|
802
|
-
url = f"{self._cfg.userdata_url}/{username}"
|
|
803
|
-
token = self._auth.check_authentication(auth_url=self._cfg.auth_url)
|
|
804
|
-
headers = {"Authorization": f"Bearer {token['access_token']}"}
|
|
805
|
-
data = search_keys
|
|
806
|
-
result = self._delete(url, headers=headers, json=data)
|
|
807
|
-
if result is None:
|
|
808
|
-
raise ValueError("Failed to delete user data")
|
|
809
|
-
|
|
810
|
-
def _get(self, url: str, **kwargs: Any) -> Optional[requests.models.Response]:
|
|
736
|
+
def _get(
|
|
737
|
+
self, url: str, **kwargs: Any
|
|
738
|
+
) -> Optional[requests.models.Response]:
|
|
811
739
|
"""Apply the get method to the databrowser."""
|
|
812
740
|
logger.debug("Searching %s with parameters: %s", url, self._params)
|
|
813
741
|
params = kwargs.pop("params", {})
|
|
814
742
|
kwargs.setdefault("timeout", 30)
|
|
815
743
|
try:
|
|
816
|
-
res = requests.get(
|
|
817
|
-
|
|
818
|
-
return res
|
|
819
|
-
except KeyboardInterrupt:
|
|
820
|
-
pprint("[red][b]User interrupt: Exit[/red][/b]", file=sys.stderr)
|
|
821
|
-
except (
|
|
822
|
-
requests.exceptions.ConnectionError,
|
|
823
|
-
requests.exceptions.HTTPError,
|
|
824
|
-
) as error:
|
|
825
|
-
msg = f"Search request failed with {error}"
|
|
826
|
-
if self._fail_on_error:
|
|
827
|
-
raise ValueError(msg) from None
|
|
828
|
-
logger.warning(msg)
|
|
829
|
-
return None
|
|
830
|
-
|
|
831
|
-
def _put(
|
|
832
|
-
self, url: str, data: Dict[str, Any], **kwargs: Any
|
|
833
|
-
) -> Optional[requests.models.Response]:
|
|
834
|
-
"""Apply the PUT method to the databrowser."""
|
|
835
|
-
logger.debug(
|
|
836
|
-
"PUT request to %s with data: %s and parameters: %s",
|
|
837
|
-
url,
|
|
838
|
-
data,
|
|
839
|
-
self._params,
|
|
840
|
-
)
|
|
841
|
-
kwargs.setdefault("timeout", 30)
|
|
842
|
-
params = kwargs.pop("params", {})
|
|
843
|
-
try:
|
|
844
|
-
res = requests.put(
|
|
845
|
-
url, json=data, params={**self._params, **params}, **kwargs
|
|
744
|
+
res = requests.get(
|
|
745
|
+
url, params={**self._params, **params}, **kwargs
|
|
846
746
|
)
|
|
847
747
|
res.raise_for_status()
|
|
848
748
|
return res
|
|
849
749
|
except KeyboardInterrupt:
|
|
850
750
|
pprint("[red][b]User interrupt: Exit[/red][/b]", file=sys.stderr)
|
|
851
|
-
|
|
852
751
|
except (
|
|
853
752
|
requests.exceptions.ConnectionError,
|
|
854
753
|
requests.exceptions.HTTPError,
|
|
855
754
|
) as error:
|
|
856
|
-
msg = f"
|
|
857
|
-
if self._fail_on_error:
|
|
858
|
-
raise ValueError(msg) from None
|
|
859
|
-
logger.warning(msg)
|
|
860
|
-
return None
|
|
861
|
-
|
|
862
|
-
def _delete(self, url: str, **kwargs: Any) -> Optional[requests.models.Response]:
|
|
863
|
-
"""Apply the DELETE method to the databrowser."""
|
|
864
|
-
logger.debug("DELETE request to %s with parameters: %s", url, self._params)
|
|
865
|
-
params = kwargs.pop("params", {})
|
|
866
|
-
kwargs.setdefault("timeout", 30)
|
|
867
|
-
try:
|
|
868
|
-
res = requests.delete(url, params={**self._params, **params}, **kwargs)
|
|
869
|
-
res.raise_for_status()
|
|
870
|
-
return res
|
|
871
|
-
except KeyboardInterrupt:
|
|
872
|
-
pprint("[red][b]User interrupt: Exit[/red][/b]", file=sys.stderr)
|
|
873
|
-
except (
|
|
874
|
-
requests.exceptions.ConnectionError,
|
|
875
|
-
requests.exceptions.HTTPError,
|
|
876
|
-
) as error:
|
|
877
|
-
msg = f"DELETE request failed with {error}"
|
|
755
|
+
msg = f"Search request failed with {error}"
|
|
878
756
|
if self._fail_on_error:
|
|
879
757
|
raise ValueError(msg) from None
|
|
880
758
|
logger.warning(msg)
|
{freva_client-2410.0.0b1 → freva_client-2410.0.1}/src/freva_client/utils/databrowser_utils.py
RENAMED
|
@@ -57,7 +57,9 @@ class Config:
|
|
|
57
57
|
host = f"{host}:{port}"
|
|
58
58
|
return f"{scheme}://{host}"
|
|
59
59
|
|
|
60
|
-
def _read_config(
|
|
60
|
+
def _read_config(
|
|
61
|
+
self, path: Path, file_type: Literal["toml", "ini"]
|
|
62
|
+
) -> str:
|
|
61
63
|
"""Read the configuration."""
|
|
62
64
|
data_types = {"toml": self._read_toml, "ini": self._read_ini}
|
|
63
65
|
try:
|
|
@@ -70,9 +72,11 @@ class Config:
|
|
|
70
72
|
def overview(self) -> Dict[str, Any]:
|
|
71
73
|
"""Get an overview of the all databrowser flavours and search keys."""
|
|
72
74
|
try:
|
|
73
|
-
res = requests.get(f"{self.databrowser_url}/overview", timeout=
|
|
75
|
+
res = requests.get(f"{self.databrowser_url}/overview", timeout=3)
|
|
74
76
|
except requests.exceptions.ConnectionError:
|
|
75
|
-
raise ValueError(
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"Could not connect to {self.databrowser_url}"
|
|
79
|
+
) from None
|
|
76
80
|
return cast(Dict[str, Any], res.json())
|
|
77
81
|
|
|
78
82
|
def _get_databrowser_host_from_config(self) -> str:
|
|
@@ -87,7 +91,9 @@ class Config:
|
|
|
87
91
|
Path(appdirs.user_config_dir("freva")) / "freva.toml": "toml",
|
|
88
92
|
Path(self.get_dirs(user=True)) / "freva.toml": "toml",
|
|
89
93
|
freva_config: "toml",
|
|
90
|
-
Path(
|
|
94
|
+
Path(
|
|
95
|
+
os.environ.get("EVALUATION_SYSTEM_CONFIG_FILE") or eval_conf
|
|
96
|
+
): "ini",
|
|
91
97
|
}
|
|
92
98
|
for config_path, config_type in paths.items():
|
|
93
99
|
if config_path.is_file():
|
|
@@ -130,7 +136,8 @@ class Config:
|
|
|
130
136
|
def metadata_url(self) -> str:
|
|
131
137
|
"""Define the endpoint for the metadata search."""
|
|
132
138
|
return (
|
|
133
|
-
f"{self.databrowser_url}/metadata_search/"
|
|
139
|
+
f"{self.databrowser_url}/metadata_search/"
|
|
140
|
+
f"{self.flavour}/{self.uniq_key}"
|
|
134
141
|
)
|
|
135
142
|
|
|
136
143
|
@staticmethod
|
|
@@ -170,8 +177,3 @@ class Config:
|
|
|
170
177
|
# The default scheme is 'posix_prefix' or 'nt', and should work for e.g.
|
|
171
178
|
# installing into a virtualenv
|
|
172
179
|
return Path(sysconfig.get_path("data")) / "share" / "freva"
|
|
173
|
-
|
|
174
|
-
@property
|
|
175
|
-
def userdata_url(self) -> str:
|
|
176
|
-
"""Define the url for adding and deleting user-data."""
|
|
177
|
-
return f"{self.databrowser_url}/userdata"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|