esgpull 0.7.2__tar.gz → 0.8.0__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.
- esgpull-0.8.0/CITATION.cff +33 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/PKG-INFO +22 -2
- {esgpull-0.7.2 → esgpull-0.8.0}/README.md +20 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/alembic.ini +0 -6
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/show.py +29 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/status.py +6 -4
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/update.py +17 -6
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/utils.py +18 -24
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/config.py +1 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/context.py +6 -6
- esgpull-0.8.0/esgpull/migrations/versions/0.7.3_update_tables.py +28 -0
- esgpull-0.8.0/esgpull/migrations/versions/0.8.0_update_tables.py +28 -0
- esgpull-0.8.0/esgpull/migrations/versions/14c72daea083_query_add_column_updated_at.py +36 -0
- esgpull-0.8.0/esgpull/migrations/versions/c7c8541fa741_query_add_column_added_at.py +37 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/options.py +1 -1
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/query.py +40 -1
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/utils.py +14 -2
- {esgpull-0.7.2 → esgpull-0.8.0}/pyproject.toml +7 -2
- {esgpull-0.7.2 → esgpull-0.8.0}/requirements-dev.lock +68 -63
- {esgpull-0.7.2 → esgpull-0.8.0}/requirements.lock +29 -27
- esgpull-0.8.0/tests/cli/test_parse.py +40 -0
- esgpull-0.8.0/tests/cli/test_show_dates.py +61 -0
- esgpull-0.8.0/tests/cli/test_update.py +104 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_context.py +1 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_db.py +2 -1
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_graph.py +70 -30
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_query.py +36 -7
- esgpull-0.8.0/tests/test_utils.py +47 -0
- esgpull-0.8.0/tests/utils.py +11 -0
- esgpull-0.7.2/tests/cli/test_update.py +0 -36
- esgpull-0.7.2/tests/test_utils.py +0 -23
- {esgpull-0.7.2 → esgpull-0.8.0}/.github/workflows/ci.yml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/.github/workflows/doc.yml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/.github/workflows/pypi-publish.yml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/.gitignore +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/.pre-commit-config.yaml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/LICENSE +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/configuration.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/download.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/glossary.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_1.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_2.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_3.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_4.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_5.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/download_6.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_1.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_2.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_3.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_4.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_5.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/intro_6.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/quickstart_1.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_1.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_2.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_3.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_4.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_5.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_6.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_7.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/images/search_ignore.svg +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/index.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/installation.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/queries.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/quickstart.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/search.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/docs/stylesheets/extra.css +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/includes/abbreviations.md +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/docs/mkdocs.yml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/__init__.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/auth.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/__init__.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/add.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/autoremove.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/config.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/convert.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/datasets.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/decorators.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/download.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/facet.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/get.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/install.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/login.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/remove.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/retry.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/search.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/self.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/cli/track.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/constants.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/database.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/download.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/esgpull.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/exceptions.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/fs.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/graph.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/install_config.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/README +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/env.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/script.py.mako +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.0_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.1_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.2_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.3_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.4_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.5_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.6_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.7_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.3.8_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.4.0_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.0_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.1_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.2_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.3_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.4_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.5.5_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.0_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.1_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.2_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.3_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.4_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.6.5_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.7.0_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.7.1_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/migrations/versions/0.7.2_update_tables.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/__init__.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/base.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/dataset.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/facet.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/file.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/selection.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/sql.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/synda_file.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/tag.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/models/utils.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/processor.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/py.typed +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/result.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/tui.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/esgpull/version.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/pdm.lock +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/recipe/meta.yaml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/recipe/recipe.yaml +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/__init__.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/cli/__init__.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/conftest.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_auth.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_config.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_esgpull.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_fs.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_processor.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_selection.py +0 -0
- {esgpull-0.7.2 → esgpull-0.8.0}/tests/test_synda.py +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This CITATION.cff file was generated with cffinit.
|
|
2
|
+
|
|
3
|
+
cff-version: 1.2.0
|
|
4
|
+
title: esgpull
|
|
5
|
+
message: ESGF command line download tool esgpull
|
|
6
|
+
type: software
|
|
7
|
+
authors:
|
|
8
|
+
- given-names: Sven
|
|
9
|
+
family-names: Rodriguez
|
|
10
|
+
email: sven.rodriguez@ipsl.fr
|
|
11
|
+
affiliation: Sorbonne University
|
|
12
|
+
- given-names: Atef
|
|
13
|
+
family-names: Ben Nasser
|
|
14
|
+
email: abennasser@ipsl.fr
|
|
15
|
+
affiliation: Centre National de Recherche Scientifique
|
|
16
|
+
orcid: 'https://orcid.org/0000-0001-6948-8735'
|
|
17
|
+
- given-names: Guillaume
|
|
18
|
+
family-names: Levavasseur
|
|
19
|
+
email: glipsl@ipsl.fr
|
|
20
|
+
affiliation: Sorbonne University
|
|
21
|
+
orcid: 'https://orcid.org/0000-0002-0801-0890'
|
|
22
|
+
repository-code: 'https://github.com/ESGF/esgf-download.git'
|
|
23
|
+
url: 'https://esgf.github.io/esgf-download/'
|
|
24
|
+
abstract: >-
|
|
25
|
+
esgpull is a modern ESGF data management tool, bundled
|
|
26
|
+
with a custom asynchronous interface with the ESGF Search
|
|
27
|
+
API. It handles scanning, downloading and updating
|
|
28
|
+
datasets, files and queries from ESGF.
|
|
29
|
+
license: BSD-3-Clause
|
|
30
|
+
commit: 762494e
|
|
31
|
+
version: 0.7.4
|
|
32
|
+
doi: 10.5281/zenodo.14228984
|
|
33
|
+
date-released: '2024-11-27'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: esgpull
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: ESGF data discovery, download, replication tool
|
|
5
5
|
Project-URL: Repository, https://github.com/ESGF/esgf-download
|
|
6
6
|
Project-URL: Documentation, https://esgf.github.io/esgf-download/
|
|
@@ -60,6 +60,18 @@ for dataset in datasets:
|
|
|
60
60
|
- Command-line interface
|
|
61
61
|
- HTTP download (async multi-file)
|
|
62
62
|
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
Install `esgpull` using pip or conda:
|
|
66
|
+
|
|
67
|
+
```shell
|
|
68
|
+
pip install esgpull
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```shell
|
|
72
|
+
conda install -c conda-forge ipsl::esgpull
|
|
73
|
+
```
|
|
74
|
+
|
|
63
75
|
## Usage
|
|
64
76
|
|
|
65
77
|
```console
|
|
@@ -87,3 +99,11 @@ Commands:
|
|
|
87
99
|
untrack Untrack queries
|
|
88
100
|
update Fetch files, link files <-> queries, send files to download...
|
|
89
101
|
```
|
|
102
|
+
|
|
103
|
+
## Useful links
|
|
104
|
+
* [ESGF Webinar: An Introduction to esgpull, A Replacement for Synda](https://www.youtube.com/watch?v=xv2RVMd1iCA)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
## Contributions
|
|
108
|
+
|
|
109
|
+
You can use the common github workflow (through pull requests and issues) to contribute.
|
|
@@ -25,6 +25,18 @@ for dataset in datasets:
|
|
|
25
25
|
- Command-line interface
|
|
26
26
|
- HTTP download (async multi-file)
|
|
27
27
|
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Install `esgpull` using pip or conda:
|
|
31
|
+
|
|
32
|
+
```shell
|
|
33
|
+
pip install esgpull
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
conda install -c conda-forge ipsl::esgpull
|
|
38
|
+
```
|
|
39
|
+
|
|
28
40
|
## Usage
|
|
29
41
|
|
|
30
42
|
```console
|
|
@@ -52,3 +64,11 @@ Commands:
|
|
|
52
64
|
untrack Untrack queries
|
|
53
65
|
update Fetch files, link files <-> queries, send files to download...
|
|
54
66
|
```
|
|
67
|
+
|
|
68
|
+
## Useful links
|
|
69
|
+
* [ESGF Webinar: An Introduction to esgpull, A Replacement for Synda](https://www.youtube.com/watch?v=xv2RVMd1iCA)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Contributions
|
|
73
|
+
|
|
74
|
+
You can use the common github workflow (through pull requests and issues) to contribute.
|
|
@@ -3,12 +3,6 @@ script_location = esgpull/migrations
|
|
|
3
3
|
prepend_sys_path = .
|
|
4
4
|
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
|
5
5
|
|
|
6
|
-
[post_write_hooks]
|
|
7
|
-
hooks = black
|
|
8
|
-
black.type = console_scripts
|
|
9
|
-
black.entrypoint = black
|
|
10
|
-
black.options = -l 79 REVISION_SCRIPT_FILENAME
|
|
11
|
-
|
|
12
6
|
# Logging configuration
|
|
13
7
|
[loggers]
|
|
14
8
|
keys = root,sqlalchemy,alembic
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
3
5
|
import click
|
|
4
6
|
from click.exceptions import Abort, BadArgumentUsage, Exit
|
|
5
7
|
|
|
@@ -15,6 +17,18 @@ from esgpull.tui import Verbosity
|
|
|
15
17
|
@groups.json_yaml
|
|
16
18
|
@opts.files
|
|
17
19
|
@opts.shas
|
|
20
|
+
@click.option(
|
|
21
|
+
"--after",
|
|
22
|
+
type=click.DateTime(["%Y-%m-%d"]),
|
|
23
|
+
default=None,
|
|
24
|
+
help="Filter queries added after this date (YYYY-MM-DD)",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--before",
|
|
28
|
+
type=click.DateTime(["%Y-%m-%d"]),
|
|
29
|
+
default=None,
|
|
30
|
+
help="Filter queries added before this date (YYYY-MM-DD)",
|
|
31
|
+
)
|
|
18
32
|
@opts.verbosity
|
|
19
33
|
def show(
|
|
20
34
|
query_id: str | None,
|
|
@@ -26,6 +40,8 @@ def show(
|
|
|
26
40
|
json: bool,
|
|
27
41
|
yaml: bool,
|
|
28
42
|
shas: bool,
|
|
43
|
+
after: datetime | None,
|
|
44
|
+
before: datetime | None,
|
|
29
45
|
verbosity: Verbosity,
|
|
30
46
|
) -> None:
|
|
31
47
|
"""
|
|
@@ -49,6 +65,19 @@ def show(
|
|
|
49
65
|
parents=parents,
|
|
50
66
|
keep_db=True,
|
|
51
67
|
)
|
|
68
|
+
|
|
69
|
+
# Apply date filters if provided
|
|
70
|
+
if after or before:
|
|
71
|
+
filtered_queries = {}
|
|
72
|
+
for sha, query in graph.queries.items():
|
|
73
|
+
if after and query.added_at < after:
|
|
74
|
+
continue
|
|
75
|
+
if before and query.added_at > before:
|
|
76
|
+
continue
|
|
77
|
+
filtered_queries[sha] = query
|
|
78
|
+
graph.queries = filtered_queries
|
|
79
|
+
# Update the shas set to match the filtered queries
|
|
80
|
+
graph._shas = set(filtered_queries.keys())
|
|
52
81
|
if tag is not None:
|
|
53
82
|
tag_db = esg.graph.get_tag(tag)
|
|
54
83
|
if tag_db is not None and tag_db.description is not None:
|
|
@@ -44,8 +44,10 @@ def status(
|
|
|
44
44
|
for status, count, total_size in status_count_size:
|
|
45
45
|
first_row = True
|
|
46
46
|
for query in esg.graph.queries.values():
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if not query.tracked:
|
|
48
|
+
continue
|
|
49
|
+
st_count, st_size = query.files_count_size(status)
|
|
50
|
+
if st_count:
|
|
49
51
|
if first_row:
|
|
50
52
|
if first_line:
|
|
51
53
|
first_line = False
|
|
@@ -61,7 +63,7 @@ def status(
|
|
|
61
63
|
first_row = False
|
|
62
64
|
table.add_row(
|
|
63
65
|
query.rich_name,
|
|
64
|
-
str(
|
|
65
|
-
format_size(
|
|
66
|
+
str(st_count),
|
|
67
|
+
format_size(st_size),
|
|
66
68
|
)
|
|
67
69
|
esg.ui.print(table)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime, timezone
|
|
4
5
|
|
|
5
6
|
import click
|
|
6
7
|
from click.exceptions import Abort, Exit
|
|
@@ -100,12 +101,20 @@ def update(
|
|
|
100
101
|
# It might be interesting for the special case where all files already
|
|
101
102
|
# exist in db, then the detailed fetch could be skipped.
|
|
102
103
|
for qf in qfs:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
if esg.config.api.use_custom_distribution_algorithm:
|
|
105
|
+
qf_results = esg.context.prepare_search_distributed(
|
|
106
|
+
qf.expanded,
|
|
107
|
+
file=True,
|
|
108
|
+
hints=[qf.hints],
|
|
109
|
+
max_hits=None,
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
qf_results = esg.context.prepare_search(
|
|
113
|
+
qf.expanded,
|
|
114
|
+
file=True,
|
|
115
|
+
hits=[qf.hits],
|
|
116
|
+
max_hits=None,
|
|
117
|
+
)
|
|
109
118
|
nb_req = len(qf_results)
|
|
110
119
|
if nb_req > 50:
|
|
111
120
|
msg = (
|
|
@@ -184,4 +193,6 @@ def update(
|
|
|
184
193
|
elif has_legacy and legacy in file_db.queries:
|
|
185
194
|
esg.db.unlink(query=legacy, file=file_db)
|
|
186
195
|
esg.db.link(query=qf.query, file=file)
|
|
196
|
+
qf.query.updated_at = datetime.now(timezone.utc)
|
|
197
|
+
esg.db.session.add(qf.query)
|
|
187
198
|
esg.ui.raise_maybe_record(Exit(0))
|
|
@@ -15,7 +15,7 @@ from rich.text import Text
|
|
|
15
15
|
from esgpull import Esgpull
|
|
16
16
|
from esgpull.graph import Graph
|
|
17
17
|
from esgpull.models import Dataset, File, Option, Options, Query, Selection
|
|
18
|
-
from esgpull.tui import UI, TempUI, Verbosity
|
|
18
|
+
from esgpull.tui import UI, TempUI, Verbosity, logger
|
|
19
19
|
from esgpull.utils import format_size
|
|
20
20
|
|
|
21
21
|
|
|
@@ -121,9 +121,15 @@ def totable(docs: list[OrderedDict[str, Any]]) -> Table:
|
|
|
121
121
|
return table
|
|
122
122
|
|
|
123
123
|
|
|
124
|
+
def safe_value(value: str) -> str:
|
|
125
|
+
if " " in value:
|
|
126
|
+
return f'"{value}"'
|
|
127
|
+
else:
|
|
128
|
+
return value
|
|
129
|
+
|
|
130
|
+
|
|
124
131
|
def parse_facets(facets: list[str]) -> Selection:
|
|
125
132
|
facet_dict: dict[str, list[str]] = {}
|
|
126
|
-
exact_terms: list[str] | None = None
|
|
127
133
|
for facet in facets:
|
|
128
134
|
match facet.split(":"):
|
|
129
135
|
case [value]:
|
|
@@ -132,28 +138,9 @@ def parse_facets(facets: list[str]) -> Selection:
|
|
|
132
138
|
...
|
|
133
139
|
case _:
|
|
134
140
|
raise BadArgumentUsage(f"{facet!r} is not valid syntax.")
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
exact_terms = []
|
|
139
|
-
if exact_terms is not None:
|
|
140
|
-
if name != "query":
|
|
141
|
-
raise BadArgumentUsage(
|
|
142
|
-
"Cannot use facet term inside an exact string."
|
|
143
|
-
)
|
|
144
|
-
exact_terms.append(value)
|
|
145
|
-
if value.endswith("/"):
|
|
146
|
-
final_exact_str = " ".join(exact_terms)
|
|
147
|
-
value = '"' + final_exact_str.strip("/") + '"'
|
|
148
|
-
exact_terms = None
|
|
149
|
-
else:
|
|
150
|
-
continue
|
|
151
|
-
facet_dict.setdefault(name, [])
|
|
152
|
-
facet_dict[name].append(value)
|
|
153
|
-
else:
|
|
154
|
-
values = value.split(",")
|
|
155
|
-
facet_dict.setdefault(name, [])
|
|
156
|
-
facet_dict[name].extend(values)
|
|
141
|
+
values = list(map(safe_value, value.split(",")))
|
|
142
|
+
facet_dict.setdefault(name, [])
|
|
143
|
+
facet_dict[name].extend(values)
|
|
157
144
|
selection = Selection()
|
|
158
145
|
for name, values in facet_dict.items():
|
|
159
146
|
selection[name] = values
|
|
@@ -170,6 +157,13 @@ def parse_query(
|
|
|
170
157
|
replica: str | None,
|
|
171
158
|
retracted: str | None,
|
|
172
159
|
) -> Query:
|
|
160
|
+
logger.info(f"{facets=}")
|
|
161
|
+
logger.info(f"{tags=}")
|
|
162
|
+
logger.info(f"{require=}")
|
|
163
|
+
logger.info(f"{distrib=}")
|
|
164
|
+
logger.info(f"{latest=}")
|
|
165
|
+
logger.info(f"{replica=}")
|
|
166
|
+
logger.info(f"{retracted=}")
|
|
173
167
|
options = Options(
|
|
174
168
|
distrib=distrib or Option.notset,
|
|
175
169
|
latest=latest or Option.notset,
|
|
@@ -126,6 +126,7 @@ class API:
|
|
|
126
126
|
page_limit: int = 50
|
|
127
127
|
default_options: DefaultOptions = Factory(DefaultOptions)
|
|
128
128
|
default_query_id: str = ""
|
|
129
|
+
use_custom_distribution_algorithm: bool = False
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
def fix_rename_search_api(doc: TOMLDocument) -> TOMLDocument:
|
|
@@ -18,7 +18,7 @@ from esgpull.config import Config
|
|
|
18
18
|
from esgpull.exceptions import SolrUnstableQueryError
|
|
19
19
|
from esgpull.models import Dataset, File, Query
|
|
20
20
|
from esgpull.tui import logger
|
|
21
|
-
from esgpull.utils import
|
|
21
|
+
from esgpull.utils import format_date_iso, index2url, sync
|
|
22
22
|
|
|
23
23
|
# workaround for notebooks with running event loop
|
|
24
24
|
if asyncio.get_event_loop().is_running():
|
|
@@ -77,9 +77,9 @@ class Result:
|
|
|
77
77
|
else:
|
|
78
78
|
params["fields"] = "instance_id"
|
|
79
79
|
if date_from is not None:
|
|
80
|
-
params["from"] =
|
|
80
|
+
params["from"] = format_date_iso(date_from)
|
|
81
81
|
if date_to is not None:
|
|
82
|
-
params["to"] =
|
|
82
|
+
params["to"] = format_date_iso(date_to)
|
|
83
83
|
if facets_param is not None:
|
|
84
84
|
if len(set(facets_param) & DangerousFacets) > 0:
|
|
85
85
|
raise SolrUnstableQueryError(pretty_repr(self.query))
|
|
@@ -90,9 +90,9 @@ class Result:
|
|
|
90
90
|
facets_star = False
|
|
91
91
|
# [?]TODO: add nominal temporal constraints `to`
|
|
92
92
|
# if "start" in facets:
|
|
93
|
-
# query["start"] =
|
|
93
|
+
# query["start"] = format_date_iso(str(facets.pop("start")))
|
|
94
94
|
# if "end" in facets:
|
|
95
|
-
# query["end"] =
|
|
95
|
+
# query["end"] = format_date_iso(str(facets.pop("end")))
|
|
96
96
|
solr_terms: list[str] = []
|
|
97
97
|
for name, values in self.query.selection.items():
|
|
98
98
|
value_term = " ".join(values)
|
|
@@ -282,7 +282,7 @@ class Context:
|
|
|
282
282
|
# # if since is None:
|
|
283
283
|
# # self.since = since
|
|
284
284
|
# # else:
|
|
285
|
-
# # self.since =
|
|
285
|
+
# # self.since = format_date_iso(since)
|
|
286
286
|
|
|
287
287
|
async def __aenter__(self) -> Context:
|
|
288
288
|
if hasattr(self, "client"):
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.7.3
|
|
4
|
+
Revises: 0.7.2
|
|
5
|
+
Create Date: 2024-09-20 12:22:11.989996
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '0.7.3'
|
|
14
|
+
down_revision = '0.7.2'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
pass
|
|
22
|
+
# ### end Alembic commands ###
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downgrade() -> None:
|
|
26
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
27
|
+
pass
|
|
28
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.8.0
|
|
4
|
+
Revises: 14c72daea083
|
|
5
|
+
Create Date: 2025-05-15 11:28:10.755003
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '0.8.0'
|
|
14
|
+
down_revision = '14c72daea083'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
pass
|
|
22
|
+
# ### end Alembic commands ###
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downgrade() -> None:
|
|
26
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
27
|
+
pass
|
|
28
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""query_add_column_updated_at
|
|
2
|
+
|
|
3
|
+
Revision ID: 14c72daea083
|
|
4
|
+
Revises: c7c8541fa741
|
|
5
|
+
Create Date: 2025-05-07 14:49:43.993125
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '14c72daea083'
|
|
14
|
+
down_revision = 'c7c8541fa741'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
21
|
+
batch_op.add_column(sa.Column('updated_at', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True))
|
|
22
|
+
|
|
23
|
+
# Backfill nulls
|
|
24
|
+
op.execute('UPDATE query SET updated_at = CURRENT_TIMESTAMP WHERE updated_at IS NULL')
|
|
25
|
+
|
|
26
|
+
# Make non-nullable
|
|
27
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
28
|
+
batch_op.alter_column('updated_at', nullable=False)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def downgrade() -> None:
|
|
32
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
33
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
34
|
+
batch_op.drop_column('updated_at')
|
|
35
|
+
|
|
36
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""query_add_column_added_at
|
|
2
|
+
|
|
3
|
+
Revision ID: c7c8541fa741
|
|
4
|
+
Revises: 0.7.3
|
|
5
|
+
Create Date: 2025-05-05 16:14:57.140262
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'c7c8541fa741'
|
|
14
|
+
down_revision = '0.7.3'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
# Add as nullable first
|
|
21
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
22
|
+
batch_op.add_column(sa.Column('added_at', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True))
|
|
23
|
+
|
|
24
|
+
# Backfill nulls
|
|
25
|
+
op.execute('UPDATE query SET added_at = CURRENT_TIMESTAMP WHERE added_at IS NULL')
|
|
26
|
+
|
|
27
|
+
# Make non-nullable
|
|
28
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
29
|
+
batch_op.alter_column('added_at', nullable=False)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
34
|
+
with op.batch_alter_table('query', schema=None) as batch_op:
|
|
35
|
+
batch_op.drop_column('added_at')
|
|
36
|
+
|
|
37
|
+
# ### end Alembic commands ###
|
|
@@ -53,7 +53,7 @@ class Options(Base):
|
|
|
53
53
|
replica: Mapped[Option] = mapped_column(sa.Enum(Option))
|
|
54
54
|
retracted: Mapped[Option] = mapped_column(sa.Enum(Option))
|
|
55
55
|
|
|
56
|
-
_distrib_ = Option(
|
|
56
|
+
_distrib_ = Option(True)
|
|
57
57
|
_latest_ = Option(True)
|
|
58
58
|
_replica_ = Option(None)
|
|
59
59
|
_retracted_ = Option(False)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Iterator, MutableMapping, Sequence
|
|
4
|
+
from datetime import datetime, timezone
|
|
4
5
|
from typing import Any, Literal
|
|
5
6
|
|
|
6
7
|
import sqlalchemy as sa
|
|
@@ -11,6 +12,7 @@ from rich.tree import Tree
|
|
|
11
12
|
from sqlalchemy.orm import Mapped, mapped_column, object_session, relationship
|
|
12
13
|
from typing_extensions import NotRequired, TypedDict
|
|
13
14
|
|
|
15
|
+
from esgpull import utils
|
|
14
16
|
from esgpull.exceptions import UntrackableQuery
|
|
15
17
|
from esgpull.models.base import Base, Sha
|
|
16
18
|
from esgpull.models.file import FileDict, FileStatus
|
|
@@ -24,7 +26,18 @@ from esgpull.models.utils import (
|
|
|
24
26
|
rich_measure_impl,
|
|
25
27
|
short_sha,
|
|
26
28
|
)
|
|
27
|
-
from esgpull.utils import format_size
|
|
29
|
+
from esgpull.utils import format_date_iso, format_size
|
|
30
|
+
|
|
31
|
+
QUERY_DATE_FMT = "%Y-%m-%d %H:%M:%S"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def parse_date(d: datetime | str) -> datetime:
|
|
35
|
+
return utils.parse_date(d, fmt=QUERY_DATE_FMT)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def format_date(d: datetime | str) -> str:
|
|
39
|
+
return utils.format_date(d, fmt=QUERY_DATE_FMT)
|
|
40
|
+
|
|
28
41
|
|
|
29
42
|
query_file_proxy = sa.Table(
|
|
30
43
|
"query_file",
|
|
@@ -152,6 +165,8 @@ class QueryDict(TypedDict):
|
|
|
152
165
|
options: NotRequired[MutableMapping[str, bool | None]]
|
|
153
166
|
selection: NotRequired[MutableMapping[str, FacetValues]]
|
|
154
167
|
files: NotRequired[list[FileDict]]
|
|
168
|
+
added_at: NotRequired[str]
|
|
169
|
+
updated_at: NotRequired[str]
|
|
155
170
|
|
|
156
171
|
|
|
157
172
|
class Query(Base):
|
|
@@ -181,6 +196,14 @@ class Query(Base):
|
|
|
181
196
|
back_populates="queries",
|
|
182
197
|
repr=False,
|
|
183
198
|
)
|
|
199
|
+
added_at: Mapped[datetime] = mapped_column(
|
|
200
|
+
server_default=sa.func.now(),
|
|
201
|
+
default_factory=lambda: datetime.now(timezone.utc),
|
|
202
|
+
)
|
|
203
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
204
|
+
server_default=sa.func.now(),
|
|
205
|
+
default_factory=lambda: datetime.now(timezone.utc),
|
|
206
|
+
)
|
|
184
207
|
|
|
185
208
|
def __init__(
|
|
186
209
|
self,
|
|
@@ -191,6 +214,8 @@ class Query(Base):
|
|
|
191
214
|
options: Options | MutableMapping[str, bool | None] | None = None,
|
|
192
215
|
selection: Selection | MutableMapping[str, FacetValues] | None = None,
|
|
193
216
|
files: list[FileDict] | None = None,
|
|
217
|
+
added_at: datetime | str | None = None,
|
|
218
|
+
updated_at: datetime | str | None = None,
|
|
194
219
|
) -> None:
|
|
195
220
|
self.tracked = tracked
|
|
196
221
|
self.require = require
|
|
@@ -219,6 +244,14 @@ class Query(Base):
|
|
|
219
244
|
if files is not None:
|
|
220
245
|
for file in files:
|
|
221
246
|
self.files.append(File.fromdict(file))
|
|
247
|
+
if added_at is not None:
|
|
248
|
+
self.added_at = parse_date(added_at)
|
|
249
|
+
else:
|
|
250
|
+
self.added_at = datetime.now(timezone.utc)
|
|
251
|
+
if updated_at is not None:
|
|
252
|
+
self.updated_at = parse_date(updated_at)
|
|
253
|
+
else:
|
|
254
|
+
self.updated_at = datetime.now(timezone.utc)
|
|
222
255
|
|
|
223
256
|
@property
|
|
224
257
|
def has_files(self) -> bool:
|
|
@@ -313,6 +346,8 @@ class Query(Base):
|
|
|
313
346
|
result["options"] = self.options.asdict()
|
|
314
347
|
if self.selection:
|
|
315
348
|
result["selection"] = self.selection.asdict()
|
|
349
|
+
result["added_at"] = format_date(self.added_at)
|
|
350
|
+
result["updated_at"] = format_date(self.updated_at)
|
|
316
351
|
return result
|
|
317
352
|
|
|
318
353
|
def clone(self, compute_sha: bool = True) -> Query:
|
|
@@ -409,6 +444,10 @@ class Query(Base):
|
|
|
409
444
|
title = Text.from_markup(self.rich_name)
|
|
410
445
|
if not self.tracked:
|
|
411
446
|
title.append(" untracked", style="i red")
|
|
447
|
+
title.append(
|
|
448
|
+
f"\n│ added {format_date_iso(self.added_at)}"
|
|
449
|
+
f"\n│ updated {format_date_iso(self.updated_at)}"
|
|
450
|
+
)
|
|
412
451
|
contents = Table.grid(padding=(0, 1))
|
|
413
452
|
if not hasattr(self, "_rich_no_require") and self.require is not None:
|
|
414
453
|
if len(self.require) == 40:
|
|
@@ -31,7 +31,9 @@ def format_size(size: int) -> str:
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def parse_date(
|
|
35
|
+
date: str | datetime.datetime, fmt: str = "%Y-%m-%d"
|
|
36
|
+
) -> datetime.datetime:
|
|
35
37
|
match date:
|
|
36
38
|
case datetime.datetime():
|
|
37
39
|
...
|
|
@@ -39,7 +41,17 @@ def format_date(date: str | datetime.datetime, fmt: str = "%Y-%m-%d") -> str:
|
|
|
39
41
|
date = datetime.datetime.strptime(date, fmt)
|
|
40
42
|
case _:
|
|
41
43
|
raise ValueError(date)
|
|
42
|
-
return date
|
|
44
|
+
return date
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def format_date(date: str | datetime.datetime, fmt: str = "%Y-%m-%d") -> str:
|
|
48
|
+
return parse_date(date, fmt).strftime(fmt)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def format_date_iso(
|
|
52
|
+
date: str | datetime.datetime, fmt: str = "%Y-%m-%d"
|
|
53
|
+
) -> str:
|
|
54
|
+
return parse_date(date, fmt).replace(microsecond=0).isoformat() + "Z"
|
|
43
55
|
|
|
44
56
|
|
|
45
57
|
def url2index(url: str) -> str:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "esgpull"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
classifiers = [
|
|
9
9
|
"License :: OSI Approved :: BSD License",
|
|
10
10
|
"Programming Language :: Python :: 3",
|
|
@@ -102,5 +102,10 @@ dev-dependencies = [
|
|
|
102
102
|
"types-pyyaml>=6.0.12.20240808",
|
|
103
103
|
"types-aiofiles>=24.1.0.20240626",
|
|
104
104
|
"pytest-mypy>=0.10.3",
|
|
105
|
-
"pytest-xdist>=3.6.1"
|
|
105
|
+
"pytest-xdist>=3.6.1",
|
|
106
|
+
"ipdb>=0.13.13",
|
|
107
|
+
"orjson>=3.10.7"
|
|
106
108
|
]
|
|
109
|
+
|
|
110
|
+
[tool.rye.scripts]
|
|
111
|
+
esg = {cmd = "esgpull", env-file = ".env"}
|