esgvoc 0.1.2__py3-none-any.whl → 0.3.0__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.
Potentially problematic release.
This version of esgvoc might be problematic. Click here for more details.
- esgvoc/__init__.py +3 -1
- esgvoc/api/__init__.py +30 -30
- esgvoc/api/_utils.py +28 -14
- esgvoc/api/data_descriptors/__init__.py +19 -10
- esgvoc/api/data_descriptors/activity.py +8 -45
- esgvoc/api/data_descriptors/area_label.py +6 -0
- esgvoc/api/data_descriptors/branded_suffix.py +5 -0
- esgvoc/api/data_descriptors/branded_variable.py +5 -0
- esgvoc/api/data_descriptors/consortium.py +16 -56
- esgvoc/api/data_descriptors/data_descriptor.py +106 -0
- esgvoc/api/data_descriptors/date.py +3 -46
- esgvoc/api/data_descriptors/directory_date.py +5 -0
- esgvoc/api/data_descriptors/experiment.py +19 -54
- esgvoc/api/data_descriptors/forcing_index.py +3 -45
- esgvoc/api/data_descriptors/frequency.py +6 -43
- esgvoc/api/data_descriptors/grid_label.py +6 -44
- esgvoc/api/data_descriptors/horizontal_label.py +6 -0
- esgvoc/api/data_descriptors/initialisation_index.py +3 -44
- esgvoc/api/data_descriptors/institution.py +11 -54
- esgvoc/api/data_descriptors/license.py +4 -44
- esgvoc/api/data_descriptors/mip_era.py +6 -44
- esgvoc/api/data_descriptors/model_component.py +7 -45
- esgvoc/api/data_descriptors/organisation.py +3 -40
- esgvoc/api/data_descriptors/physic_index.py +3 -45
- esgvoc/api/data_descriptors/product.py +4 -43
- esgvoc/api/data_descriptors/realisation_index.py +3 -44
- esgvoc/api/data_descriptors/realm.py +4 -42
- esgvoc/api/data_descriptors/resolution.py +6 -44
- esgvoc/api/data_descriptors/source.py +18 -53
- esgvoc/api/data_descriptors/source_type.py +3 -41
- esgvoc/api/data_descriptors/sub_experiment.py +3 -41
- esgvoc/api/data_descriptors/table.py +6 -48
- esgvoc/api/data_descriptors/temporal_label.py +6 -0
- esgvoc/api/data_descriptors/time_range.py +3 -27
- esgvoc/api/data_descriptors/variable.py +13 -71
- esgvoc/api/data_descriptors/variant_label.py +3 -47
- esgvoc/api/data_descriptors/vertical_label.py +5 -0
- esgvoc/api/project_specs.py +82 -0
- esgvoc/api/projects.py +284 -238
- esgvoc/api/report.py +89 -52
- esgvoc/api/search.py +31 -11
- esgvoc/api/universe.py +57 -48
- esgvoc/apps/__init__.py +6 -0
- esgvoc/apps/drs/__init__.py +0 -16
- esgvoc/apps/drs/constants.py +2 -0
- esgvoc/apps/drs/generator.py +429 -0
- esgvoc/apps/drs/report.py +492 -0
- esgvoc/apps/drs/validator.py +330 -0
- esgvoc/cli/drs.py +248 -0
- esgvoc/cli/get.py +26 -25
- esgvoc/cli/install.py +11 -8
- esgvoc/cli/main.py +4 -5
- esgvoc/cli/status.py +14 -2
- esgvoc/cli/valid.py +41 -45
- esgvoc/core/db/models/mixins.py +7 -0
- esgvoc/core/db/models/project.py +3 -8
- esgvoc/core/db/models/universe.py +3 -3
- esgvoc/core/db/project_ingestion.py +4 -1
- esgvoc/core/db/universe_ingestion.py +8 -7
- esgvoc/core/logging_handler.py +1 -1
- esgvoc/core/repo_fetcher.py +4 -3
- esgvoc/core/service/__init__.py +37 -5
- esgvoc/core/service/configuration/config_manager.py +188 -0
- esgvoc/core/service/configuration/setting.py +88 -0
- esgvoc/core/service/state.py +66 -42
- esgvoc-0.3.0.dist-info/METADATA +89 -0
- esgvoc-0.3.0.dist-info/RECORD +78 -0
- esgvoc-0.3.0.dist-info/licenses/LICENSE.txt +519 -0
- esgvoc/apps/drs/models.py +0 -43
- esgvoc/apps/drs/parser.py +0 -27
- esgvoc/cli/config.py +0 -79
- esgvoc/core/service/settings.py +0 -64
- esgvoc/core/service/settings.toml +0 -12
- esgvoc/core/service/settings_default.toml +0 -20
- esgvoc-0.1.2.dist-info/METADATA +0 -54
- esgvoc-0.1.2.dist-info/RECORD +0 -66
- {esgvoc-0.1.2.dist-info → esgvoc-0.3.0.dist-info}/WHEEL +0 -0
- {esgvoc-0.1.2.dist-info → esgvoc-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import ClassVar, Dict, Optional
|
|
2
|
+
import toml
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ProjectSettings(BaseModel):
|
|
7
|
+
project_name: str
|
|
8
|
+
github_repo: str
|
|
9
|
+
branch: Optional[str] = "main"
|
|
10
|
+
local_path: Optional[str] = None
|
|
11
|
+
db_path: Optional[str] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UniverseSettings(BaseModel):
|
|
15
|
+
github_repo: str
|
|
16
|
+
branch: Optional[str] = None
|
|
17
|
+
local_path: Optional[str] = None
|
|
18
|
+
db_path: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ServiceSettings(BaseModel):
|
|
22
|
+
universe: UniverseSettings
|
|
23
|
+
projects: Dict[str, ProjectSettings] = Field(default_factory=dict)
|
|
24
|
+
|
|
25
|
+
# 🔹 Define default settings
|
|
26
|
+
DEFAULT_SETTINGS : ClassVar[dict]= {
|
|
27
|
+
"universe": {
|
|
28
|
+
"github_repo": "https://github.com/WCRP-CMIP/WCRP-universe",
|
|
29
|
+
"branch": "esgvoc",
|
|
30
|
+
"local_path": "repos/WCRP-universe",
|
|
31
|
+
"db_path": "dbs/universe.sqlite",
|
|
32
|
+
},
|
|
33
|
+
"projects": [
|
|
34
|
+
{
|
|
35
|
+
"project_name": "cmip6",
|
|
36
|
+
"github_repo": "https://github.com/WCRP-CMIP/CMIP6_CVs",
|
|
37
|
+
"branch": "esgvoc",
|
|
38
|
+
"local_path": "repos/CMIP6_CVs",
|
|
39
|
+
"db_path": "dbs/cmip6.sqlite",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"project_name": "cmip6plus",
|
|
43
|
+
"github_repo": "https://github.com/WCRP-CMIP/CMIP6Plus_CVs",
|
|
44
|
+
"branch": "esgvoc",
|
|
45
|
+
"local_path": "repos/CMIP6Plus_CVs",
|
|
46
|
+
"db_path": "dbs/cmip6plus.sqlite",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def load_from_file(cls, file_path: str) -> "ServiceSettings":
|
|
53
|
+
"""Load configuration from a TOML file, falling back to defaults if necessary."""
|
|
54
|
+
try:
|
|
55
|
+
data = toml.load(file_path)
|
|
56
|
+
except FileNotFoundError:
|
|
57
|
+
data = cls.DEFAULT_SETTINGS # Use defaults if the file is missing
|
|
58
|
+
|
|
59
|
+
projects = {p["project_name"]: ProjectSettings(**p) for p in data.pop("projects", [])}
|
|
60
|
+
return cls(universe=UniverseSettings(**data["universe"]), projects=projects)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def load_default(cls) -> "ServiceSettings":
|
|
64
|
+
"""Load default settings."""
|
|
65
|
+
return cls.load_from_dict(cls.DEFAULT_SETTINGS)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def load_from_dict(cls, config_data: dict) -> "ServiceSettings":
|
|
69
|
+
"""Load configuration from a dictionary."""
|
|
70
|
+
projects = {p["project_name"]: ProjectSettings(**p) for p in config_data.get("projects", [])}
|
|
71
|
+
return cls(universe=UniverseSettings(**config_data["universe"]), projects=projects)
|
|
72
|
+
|
|
73
|
+
def save_to_file(self, file_path: str):
|
|
74
|
+
"""Save the configuration to a TOML file."""
|
|
75
|
+
data = {
|
|
76
|
+
"universe": self.universe.model_dump(),
|
|
77
|
+
"projects": [p.model_dump() for p in self.projects.values()],
|
|
78
|
+
}
|
|
79
|
+
with open(file_path, "w") as f:
|
|
80
|
+
toml.dump(data, f)
|
|
81
|
+
|
|
82
|
+
def dump(self)->dict:
|
|
83
|
+
data = {
|
|
84
|
+
"universe": self.universe.model_dump(),
|
|
85
|
+
"projects": [p.model_dump() for p in self.projects.values()],
|
|
86
|
+
}
|
|
87
|
+
return data
|
|
88
|
+
|
esgvoc/core/service/state.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from esgvoc.core.repo_fetcher import RepoFetcher
|
|
7
|
-
from esgvoc.core.service.
|
|
7
|
+
from esgvoc.core.service.configuration.setting import UniverseSettings, ProjectSettings, ServiceSettings
|
|
8
8
|
from esgvoc.core.db.connection import DBConnection
|
|
9
9
|
|
|
10
10
|
from rich.table import Table
|
|
@@ -13,27 +13,38 @@ from sqlmodel import select
|
|
|
13
13
|
from esgvoc.core.db.models.universe import Universe
|
|
14
14
|
from esgvoc.core.db.models.project import Project
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
class BaseState:
|
|
19
20
|
def __init__(self, github_repo: str, branch: str = "main", local_path: Optional[str] = None, db_path: Optional[str] = None):
|
|
20
|
-
|
|
21
|
-
self.github_repo = github_repo
|
|
22
|
-
self.branch = branch
|
|
23
|
-
self.github_access = True # False if we dont have internet and some other cases
|
|
24
|
-
self.github_version = None
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
self.
|
|
28
|
-
|
|
22
|
+
from esgvoc.core.service import config_manager
|
|
23
|
+
self.base_dir = config_manager.data_config_dir # needed for repofetcher
|
|
24
|
+
|
|
25
|
+
self.github_repo : str = github_repo
|
|
26
|
+
self.branch : str = branch
|
|
27
|
+
self.github_access : bool = True # False if we dont have internet and some other cases
|
|
28
|
+
self.github_version : str | None = None
|
|
29
29
|
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
30
|
+
self.local_path : str | None = self._get_absolute_path(str(self.base_dir),local_path)
|
|
31
|
+
self.local_access : bool = True # False if we dont have cloned the remote repo yet
|
|
32
|
+
self.local_version : str | None = None
|
|
33
33
|
|
|
34
|
-
self.
|
|
34
|
+
self.db_path : str | None = self._get_absolute_path(str(self.base_dir), db_path)
|
|
35
|
+
self.db_access : bool = True # False if we cant access the db for some reason
|
|
36
|
+
self.db_version : str | None = None
|
|
37
|
+
|
|
38
|
+
self.rf = RepoFetcher(local_path=str(self.base_dir))
|
|
35
39
|
self.db_connection:DBConnection|None = None
|
|
36
|
-
self.db_sqlmodel = None
|
|
40
|
+
self.db_sqlmodel :Universe | Project| None = None
|
|
41
|
+
|
|
42
|
+
def _get_absolute_path(self,base_dir:str,path:str|None)->str|None:
|
|
43
|
+
if base_dir !="" and path is not None:
|
|
44
|
+
return base_dir + "/"+ path
|
|
45
|
+
if base_dir == "":
|
|
46
|
+
return path
|
|
47
|
+
|
|
37
48
|
|
|
38
49
|
def fetch_version_local(self):
|
|
39
50
|
if self.local_path:
|
|
@@ -54,9 +65,12 @@ class BaseState:
|
|
|
54
65
|
self.github_version = self.rf.get_github_version(owner, repo, self.branch)
|
|
55
66
|
self.github_access = True
|
|
56
67
|
logger.debug(f"Latest GitHub commit: {self.github_version}")
|
|
68
|
+
except IndexError as e:
|
|
69
|
+
self.github_access = False
|
|
57
70
|
except Exception as e:
|
|
58
71
|
logger.exception(f"Failed to fetch GitHub version: {e} ,for {self.github_repo},owner : {owner}, repo : {repo},branch : {self.branch}")
|
|
59
72
|
self.github_access = False
|
|
73
|
+
|
|
60
74
|
if self.github_version is None:
|
|
61
75
|
self.github_access = False
|
|
62
76
|
|
|
@@ -67,7 +81,7 @@ class BaseState:
|
|
|
67
81
|
self.db_access = False
|
|
68
82
|
else:
|
|
69
83
|
try:
|
|
70
|
-
self.db_connection =DBConnection(db_file_path= Path(self.db_path))
|
|
84
|
+
self.db_connection = DBConnection(db_file_path= Path(self.db_path))
|
|
71
85
|
with self.db_connection.create_session() as session:
|
|
72
86
|
self.db_version = session.exec(select(self.db_sqlmodel.git_hash)).one()
|
|
73
87
|
self.db_access = True
|
|
@@ -82,25 +96,28 @@ class BaseState:
|
|
|
82
96
|
|
|
83
97
|
|
|
84
98
|
def fetch_versions(self):
|
|
85
|
-
self.
|
|
99
|
+
if self.github_access:
|
|
100
|
+
self.fetch_version_remote()
|
|
86
101
|
self.fetch_version_local()
|
|
87
102
|
self.fetch_version_db()
|
|
88
103
|
|
|
89
104
|
def check_sync_status(self):
|
|
90
105
|
self.fetch_versions()
|
|
91
106
|
return {
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
"
|
|
107
|
+
"github" : self.github_version if self.github_version else None,
|
|
108
|
+
"local": self.local_version if self.local_version else None,
|
|
109
|
+
"db" : self.db_version if self.db_version else None,
|
|
110
|
+
"github_local_sync": self.github_version == self.local_version if self.github_access and self.github_version and self.local_version else False,
|
|
111
|
+
"local_db_sync": self.local_version == self.db_version if self.local_access and self.local_version else False,
|
|
112
|
+
"github_db_sync": self.github_version == self.db_version if self.github_access and self.github_version else False
|
|
96
113
|
}
|
|
97
114
|
|
|
98
115
|
def clone_remote(self):
|
|
99
116
|
owner, repo = self.github_repo.lstrip("https://github.com/").split("/")
|
|
100
|
-
#TODO add destination "local_path" in clone_repo
|
|
101
|
-
self.rf.clone_repository(owner, repo, self.branch)
|
|
117
|
+
#TODO add destination "local_path" in clone_repo, done in a wierd way Improve that:
|
|
118
|
+
self.rf.clone_repository(owner, repo, self.branch, self.local_path)
|
|
102
119
|
self.fetch_version_local()
|
|
103
|
-
|
|
120
|
+
|
|
104
121
|
|
|
105
122
|
def build_db(self):
|
|
106
123
|
from esgvoc.core.db.project_ingestion import ingest_project
|
|
@@ -108,7 +125,7 @@ class BaseState:
|
|
|
108
125
|
from esgvoc.core.db.models.project import project_create_db
|
|
109
126
|
from esgvoc.core.db.models.universe import universe_create_db
|
|
110
127
|
from esgvoc.core.db.universe_ingestion import ingest_universe
|
|
111
|
-
|
|
128
|
+
|
|
112
129
|
if self.db_path :
|
|
113
130
|
if os.path.exists(self.db_path):
|
|
114
131
|
os.remove(self.db_path)
|
|
@@ -116,48 +133,52 @@ class BaseState:
|
|
|
116
133
|
os.makedirs(Path(self.db_path).parent,exist_ok=True)
|
|
117
134
|
|
|
118
135
|
if self.db_sqlmodel == Universe: # Ugly
|
|
136
|
+
print("Building Universe DB from ",self.local_path)
|
|
119
137
|
universe_create_db(Path(self.db_path))
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
self.db_connection = DBConnection(db_file_path= Path(self.db_path))
|
|
139
|
+
|
|
140
|
+
ingest_metadata_universe(self.db_connection,self.local_version)
|
|
141
|
+
print("Filling Universe DB")
|
|
142
|
+
if self.local_path:
|
|
143
|
+
ingest_universe(Path(self.local_path), Path(self.db_path))
|
|
122
144
|
|
|
123
145
|
elif self.db_sqlmodel == Project:
|
|
146
|
+
print("Building Project DB from ", self.local_path)
|
|
124
147
|
project_create_db(Path(self.db_path))
|
|
125
|
-
|
|
148
|
+
print("Filling project DB")
|
|
149
|
+
if self.local_path and self.local_version:
|
|
150
|
+
ingest_project(Path(self.local_path),Path(self.db_path),self.local_version)
|
|
126
151
|
self.fetch_version_db()
|
|
127
152
|
|
|
128
153
|
|
|
129
154
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
155
|
def sync(self):
|
|
134
156
|
summary = self.check_sync_status()
|
|
135
157
|
if self.github_access and summary["github_db_sync"] is None and summary["local_db_sync"]is None and summary["github_local_sync"] is None:
|
|
136
158
|
self.clone_remote()
|
|
137
159
|
self.build_db()
|
|
138
160
|
elif self.github_access and not summary["github_db_sync"]:
|
|
161
|
+
|
|
139
162
|
if not summary["local_db_sync"] and summary["local_db_sync"] is not None:
|
|
140
163
|
self.clone_remote()
|
|
141
164
|
self.build_db()
|
|
165
|
+
elif not summary["github_local_sync"] :
|
|
166
|
+
self.clone_remote()
|
|
167
|
+
self.build_db()
|
|
142
168
|
else: # can be simply build in root and clone if neccessary
|
|
143
169
|
self.build_db()
|
|
144
170
|
elif self.local_access:
|
|
145
|
-
if not summary["local_db_sync"] and summary is not None:
|
|
171
|
+
if not summary["local_db_sync"] and summary["local_db_sync"] is not None:
|
|
146
172
|
self.build_db()
|
|
173
|
+
else:
|
|
174
|
+
print("Cache db is uptodate from local repository")
|
|
147
175
|
elif not self.db_access: # it can happen if the db is created but not filled
|
|
148
176
|
self.build_db()
|
|
177
|
+
else:
|
|
178
|
+
print("Nothing to install, everything up to date")
|
|
179
|
+
print("Try 'esgvoc status' for more details")
|
|
149
180
|
|
|
150
181
|
|
|
151
|
-
"""
|
|
152
|
-
if self.github_version and self.github_version != self.local_version:
|
|
153
|
-
owner, repo = self.github_repo.lstrip("https://github.com/").split("/")
|
|
154
|
-
self.rf.clone_repository(owner, repo, self.branch)
|
|
155
|
-
#self.fetch_versions()
|
|
156
|
-
|
|
157
|
-
if self.local_version != self.db_version:
|
|
158
|
-
# delete and redo the DB?
|
|
159
|
-
pass
|
|
160
|
-
"""
|
|
161
182
|
class StateUniverse(BaseState):
|
|
162
183
|
def __init__(self, settings: UniverseSettings):
|
|
163
184
|
super().__init__(**settings.model_dump())
|
|
@@ -187,13 +208,16 @@ class StateService:
|
|
|
187
208
|
proj_state.fetch_versions()
|
|
188
209
|
|
|
189
210
|
def synchronize_all(self):
|
|
211
|
+
print("sync universe")
|
|
190
212
|
self.universe.sync()
|
|
213
|
+
print("sync projects")
|
|
191
214
|
for project in self.projects.values():
|
|
192
215
|
project.sync()
|
|
216
|
+
|
|
193
217
|
def table(self):
|
|
194
218
|
table = Table(show_header=False, show_lines=True)
|
|
195
219
|
table.add_row("","Remote github repo","Local repository","Cache Database")
|
|
196
|
-
table.add_row("Universe path",self.universe.github_repo,self.universe.local_path,self.universe.db_path)
|
|
220
|
+
table.add_row("Universe path",self.universe.github_repo,self.universe.local_path,self.universe.db_path)
|
|
197
221
|
table.add_row("Version",self.universe.github_version,self.universe.local_version,self.universe.db_version)
|
|
198
222
|
for proj_name,proj in self.projects.items():
|
|
199
223
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: esgvoc
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: python library and CLI to interact with WCRP CVs
|
|
5
|
+
Project-URL: Repository, https://github.com/ESGF/esgf-vocab
|
|
6
|
+
Author-email: Sébastien Gardoll <sebastien@gardoll.fr>, Guillaume Levavasseur <guillaume.levavasseur@ipsl.fr>, Laurent Troussellier <laurent.troussellier@ipsl.fr>
|
|
7
|
+
License: CECILL-2.1
|
|
8
|
+
Requires-Python: <3.13,>=3.12
|
|
9
|
+
Requires-Dist: idna>=3.10
|
|
10
|
+
Requires-Dist: platformdirs>=4.3.6
|
|
11
|
+
Requires-Dist: pydantic>=2.9.2
|
|
12
|
+
Requires-Dist: pyld>=2.0.4
|
|
13
|
+
Requires-Dist: requests>=2.32.3
|
|
14
|
+
Requires-Dist: sqlalchemy>=2.0.36
|
|
15
|
+
Requires-Dist: sqlmodel>=0.0.22
|
|
16
|
+
Requires-Dist: toml>=0.10.2
|
|
17
|
+
Requires-Dist: typer>=0.15.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# ESGVOC Library
|
|
21
|
+
|
|
22
|
+
ESGVOC is a Python library designed to simplify interaction with controlled vocabularies (CVs) used in WCRP climate data projects. It supports querying, caching, and validating terms across various CV repositories like the [universe](https://github.com/WCRP-CMIP/WCRP-universe/tree/esgvoc) and project-specific repositories (e.g., [CMIP6Plus](https://github.com/WCRP-CMIP/CMIP6Plus_CVs/tree/esgvoc), [CMIP6](https://github.com/WCRP-CMIP/CMIP6_CVs/tree/esgvoc), etc.).
|
|
23
|
+
|
|
24
|
+
Full documentation is available at [https://esgf.github.io/esgf-vocab/](https://esgf.github.io/esgf-vocab/).
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- **Query controlled vocabularies**:
|
|
31
|
+
- Retrieve terms, collections, or descriptors.
|
|
32
|
+
- Perform cross-validation and search operations.
|
|
33
|
+
- Supports case-sensitive, wildcard, and approximate matching.
|
|
34
|
+
|
|
35
|
+
- **Caching**:
|
|
36
|
+
- Download CVs to a local database for offline use.
|
|
37
|
+
- Keep the local cache up-to-date.
|
|
38
|
+
|
|
39
|
+
- **Validation**:
|
|
40
|
+
- Validate strings against CV terms and templates.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
ESGVOC is available on PyPI. Install it with pip:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install esgvoc
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Following this command to install or update the latest CVs.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
esgvoc install
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## How to contribute
|
|
60
|
+
|
|
61
|
+
### Install Python dev environment
|
|
62
|
+
|
|
63
|
+
* Pip
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
* Rye
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
rye sync
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Linters & code formatters
|
|
76
|
+
|
|
77
|
+
* Pip
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install pre-commit
|
|
81
|
+
pre-commit install
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
* Rye
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
rye install
|
|
88
|
+
rye run pre-commit install
|
|
89
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
esgvoc/__init__.py,sha256=D1rteIBXx1OIT6Jg9Yqo4pOud1VZTXZI8vrhJxm7Nvs,66
|
|
2
|
+
esgvoc/api/__init__.py,sha256=Oe6ch-MhVnN886jQAJ-OuyCI1eO3BxOtB_zvdbkVTXk,2764
|
|
3
|
+
esgvoc/api/_utils.py,sha256=pddSwnqYqLua0r2SXX6098OaCDxWJxaW7zlt2Xarl9o,2149
|
|
4
|
+
esgvoc/api/project_specs.py,sha256=uX1zqvTC8Dj5bOegZQVMAqU1VOle01s_8ncPXvtp0J4,2328
|
|
5
|
+
esgvoc/api/projects.py,sha256=E-Jt3vlu4vhPpVUixDGc7SHSj1HrNeIYz7n3cd9GhWI,42306
|
|
6
|
+
esgvoc/api/report.py,sha256=rAqOwYTqmzMguONT54YzNU1i7ZtmjYaCRD0Yz7mJsOI,3511
|
|
7
|
+
esgvoc/api/search.py,sha256=yV00GjvC9ZRodmfTp9HhOSk5OBsKq1s6ZNfpfxkbCwc,4208
|
|
8
|
+
esgvoc/api/universe.py,sha256=oFgYXCwVZP4HSgdGXYMFmGolSah1uCmfCMWKhNTuhog,10581
|
|
9
|
+
esgvoc/api/data_descriptors/__init__.py,sha256=6ldSy7x4rnol7ZykPBn9xtnKq1bT_d8G19LIyJnc_mw,3248
|
|
10
|
+
esgvoc/api/data_descriptors/activity.py,sha256=HZIaFqOVeYKmMJd1B_ihbKTAAAr13wr8IFISZI_jdO8,620
|
|
11
|
+
esgvoc/api/data_descriptors/area_label.py,sha256=sJqHuuLeXqDajZfEatAWX7Jot6I0xnCd1gvFJaA2s_M,161
|
|
12
|
+
esgvoc/api/data_descriptors/branded_suffix.py,sha256=7uvoUfJ5KBWMWmEu_QmjJ9wEzLhvqGuFqsvdS9sXrNQ,157
|
|
13
|
+
esgvoc/api/data_descriptors/branded_variable.py,sha256=vnZxR3EQv9X1z3pRmY_4C_sXkKkYf9-9gebY_F2ldDI,159
|
|
14
|
+
esgvoc/api/data_descriptors/consortium.py,sha256=F8qFon8aKnVrUDkrBIg1DoQwZLUNt8QYUTQaxyYvMCI,807
|
|
15
|
+
esgvoc/api/data_descriptors/data_descriptor.py,sha256=xmrJv1ZLMBBBysVZI6zCC2ZRyUwkOv-aKRHPw9d4bhk,3116
|
|
16
|
+
esgvoc/api/data_descriptors/date.py,sha256=PsLSEI_qNXYxxgdcA2cmxmD5wo9Dd_uoaw9f5hmFHTs,131
|
|
17
|
+
esgvoc/api/data_descriptors/directory_date.py,sha256=UcmBEV-n8-Nl6ML5xVjIswAnOVCvi25aJHj8-1HE8tY,140
|
|
18
|
+
esgvoc/api/data_descriptors/experiment.py,sha256=dZQe0VURghn5zZuX3fPRx9ukGqe4R-8IoIsw7iEiKNY,1073
|
|
19
|
+
esgvoc/api/data_descriptors/forcing_index.py,sha256=ELEUgv5VaRD548EEl1T-_PpdqPdz1rmWPdh-7mfGLQU,139
|
|
20
|
+
esgvoc/api/data_descriptors/frequency.py,sha256=LmDjBSAwHi8vuHIz3SW0r4ewrqh1W94kbFEetCJuPNs,193
|
|
21
|
+
esgvoc/api/data_descriptors/grid_label.py,sha256=HuR37-usYh6FjzE2_e6b7WBbrgAvyjUCc29NJnBEHDE,197
|
|
22
|
+
esgvoc/api/data_descriptors/horizontal_label.py,sha256=V-QkV5Qn3uVYAZ6BGCiaTLxlBx9x1bJZ_nc6H8kH00s,167
|
|
23
|
+
esgvoc/api/data_descriptors/initialisation_index.py,sha256=IknUFQkFCsyvawsUETomiVYhR1vEmCZSeeMqfgie0_8,146
|
|
24
|
+
esgvoc/api/data_descriptors/institution.py,sha256=8AzWTg9tNqkpVa9TnAmfx119opZ_JzvXnnJg-VuSmDE,542
|
|
25
|
+
esgvoc/api/data_descriptors/license.py,sha256=nJaVoJ-CGHd1JYDE8-mo0P6xljRKg1Y9ZopEOcWLeVk,176
|
|
26
|
+
esgvoc/api/data_descriptors/mip_era.py,sha256=WsNPE1t8LBY9HXz97SVoQHWRz0Ao0_5-e1fO4J1lEAE,177
|
|
27
|
+
esgvoc/api/data_descriptors/model_component.py,sha256=rmpKs0njm4mKQ2lxTV0vhZ4aORBSCuouI5zT_DzW6ZE,227
|
|
28
|
+
esgvoc/api/data_descriptors/organisation.py,sha256=m5CHah1NxVl0OmNOUb_Nln8MwMG_Mx-Y3VRlpqY377w,135
|
|
29
|
+
esgvoc/api/data_descriptors/physic_index.py,sha256=jWa1sQdLm90U0TY1JdQ3kgnhhpyJaMUswxdjamMJy8U,138
|
|
30
|
+
esgvoc/api/data_descriptors/product.py,sha256=PYsV_mu8jj62pv4FQJcUoqe7MM_9JqRe-Otn4RdrgQw,157
|
|
31
|
+
esgvoc/api/data_descriptors/realisation_index.py,sha256=zWgUnoM0DWsME5WciQUjb7pIw_FAzDfGPDCyGQ4xSOo,143
|
|
32
|
+
esgvoc/api/data_descriptors/realm.py,sha256=s1J-FsTlEr_BOgW3veynUPVzSwbv71V03TWM4wbUYds,154
|
|
33
|
+
esgvoc/api/data_descriptors/resolution.py,sha256=cEfu1XstWsVIwRs4z34lcyP5wiZYNMyhWNQZ5IplVps,190
|
|
34
|
+
esgvoc/api/data_descriptors/source.py,sha256=IAVh0a3yXEOv0_O025wIRPyZET5_EwgyPxCKfyBwOQ8,1128
|
|
35
|
+
esgvoc/api/data_descriptors/source_type.py,sha256=FQUQwAix2JDo_rx_E-iVV5vHdL3pVGajoSaQMCjDOGQ,145
|
|
36
|
+
esgvoc/api/data_descriptors/sub_experiment.py,sha256=HAQLq8Aq9XQjdmKfkAewTAe4rO7oTiEGdPqhDITVzJg,149
|
|
37
|
+
esgvoc/api/data_descriptors/table.py,sha256=rZp5CpoJSFzc869IaJOMjMDTb6KfcUEZM_fthtVt1Gc,254
|
|
38
|
+
esgvoc/api/data_descriptors/temporal_label.py,sha256=lcEfH36GmWEYL83B0uG5S5Uat17GG8F-9EeFiZ3O01M,165
|
|
39
|
+
esgvoc/api/data_descriptors/time_range.py,sha256=4G9-69DG9bikb1-X9ja2Hsocd_id0Fhco7L35E0uf-c,151
|
|
40
|
+
esgvoc/api/data_descriptors/variable.py,sha256=WtWlYRO0NLPRn1qe7Dt84doJWZYNCvCCqhC0slOPidk,1014
|
|
41
|
+
esgvoc/api/data_descriptors/variant_label.py,sha256=FL8nz0BfvJgKFjMmfBgNyRb8jcHaLBDLPpOvr6mBx9A,155
|
|
42
|
+
esgvoc/api/data_descriptors/vertical_label.py,sha256=g2t-38eE-FY4H_aHrOj-ScZSPHIX6m71oltLcRHOtqI,141
|
|
43
|
+
esgvoc/apps/__init__.py,sha256=Kyq36qRjvTWN7gu4_iFaLOjNUYvW0k1xp8bvkgJlQ5w,269
|
|
44
|
+
esgvoc/apps/drs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
esgvoc/apps/drs/constants.py,sha256=kZxFo7Aa8DNy3WBq2j0FuZVEqOyxG53ZAkkjQaObE8s,95
|
|
46
|
+
esgvoc/apps/drs/generator.py,sha256=2-l68Zp4XVzZ42a6x1GN5hHwqYDe--JaiVjfWIGgiBw,22996
|
|
47
|
+
esgvoc/apps/drs/report.py,sha256=HywC7LXA7ER8VQu5g56izm5Z-zLxhoDekQJbhNUNkmI,17316
|
|
48
|
+
esgvoc/apps/drs/validator.py,sha256=-hX5PEYeiuRyIwS6jsY9XRyvj-wPpdcUD8PJn31D9hE,14913
|
|
49
|
+
esgvoc/cli/drs.py,sha256=ykisrZEOz3sl9rFuQehJfgOf4xsE9tKjPd_CmMWR08M,9133
|
|
50
|
+
esgvoc/cli/get.py,sha256=NrNCb2nWftlPkdRmYysbMU_pVUksFTeYItCuVviAAqQ,5171
|
|
51
|
+
esgvoc/cli/install.py,sha256=zMssevZDrigrUlw1QHWITGUL1TvsHMZBQdYeNtHgWQA,433
|
|
52
|
+
esgvoc/cli/main.py,sha256=kjB-yus-cmG9rOmVIPBhmjOr3tkwB13dHLcNqrdpYAM,483
|
|
53
|
+
esgvoc/cli/status.py,sha256=hmpyrszrb5ke9l_1SgendPSeoXW1h-h7nH0zGFt_vUw,1357
|
|
54
|
+
esgvoc/cli/valid.py,sha256=XrseGONeWR6gnnwZrRMJNjVBFQLT82Uzn5rHrjjM1Uk,7040
|
|
55
|
+
esgvoc/core/constants.py,sha256=b9GoM3pTICki95gMCnUZbg4_mMiywKhJX5ME01pgwMs,431
|
|
56
|
+
esgvoc/core/convert.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
+
esgvoc/core/data_handler.py,sha256=BmcD_dSvX5fCkUEGAQnadPTeuKA7IvgMmQtesMXKh5g,5004
|
|
58
|
+
esgvoc/core/logging.conf,sha256=GK52lsTu17CfC2tKiMAIvkuHkIt5tqPmkWv68COOilc,278
|
|
59
|
+
esgvoc/core/logging_handler.py,sha256=VgRBWeW_xdC4HtXG0TleQFwoy_YbJR4wlpci_sFezK0,147
|
|
60
|
+
esgvoc/core/repo_fetcher.py,sha256=Rg668F4NN60jd8Ydz9dXpJQxd2eQOgaO0Ziwm53mcaI,9823
|
|
61
|
+
esgvoc/core/db/__init__.py,sha256=gjAT_lUJtPqxsc8WnwbG_sxDqSIFAlG8vEKxdGyH82w,150
|
|
62
|
+
esgvoc/core/db/connection.py,sha256=dhnCy1mwytUUvt149WHk7eYW0KSP64IaF3kMvKOQSJE,877
|
|
63
|
+
esgvoc/core/db/project_ingestion.py,sha256=c7sgl91ScPfB4n05OR6HbtDqxNELv0jTiFf77S7tqc4,6327
|
|
64
|
+
esgvoc/core/db/universe_ingestion.py,sha256=kH9TryeHbahLMaF1BFfOzrakWkcllIi9KWz90IKAphI,5215
|
|
65
|
+
esgvoc/core/db/models/mixins.py,sha256=S4_6iuKf1kYLdUXAgqRKSTXs8H9I--43MKlEq4F-dm4,445
|
|
66
|
+
esgvoc/core/db/models/project.py,sha256=tuVcwNOkv0fQLFojpQCWacpnM7ZIX3eMDq6Mnko5OI0,2284
|
|
67
|
+
esgvoc/core/db/models/universe.py,sha256=_WwuqrLGrheLbSOXaFN-2rWmHmeEGseO5sN400p6BuY,2202
|
|
68
|
+
esgvoc/core/service/__init__.py,sha256=hveqCB4oC6gKDf_L-wZxu9iBz7RiY4x9OeJGP6S5xtU,1534
|
|
69
|
+
esgvoc/core/service/data_merger.py,sha256=GNFp5DTV2jlBVJZNpILngi6jCbUvVGcqka4EMWKj_Os,3456
|
|
70
|
+
esgvoc/core/service/esg_voc.py,sha256=5G0P4_xmQzoI_RG_agpq-yHoYYZx220P27v2nPrpyNs,2420
|
|
71
|
+
esgvoc/core/service/state.py,sha256=f1Pb11yYRAcLyK93zJ60i5l-mifDXP8_81tJQtMAvPo,10622
|
|
72
|
+
esgvoc/core/service/configuration/config_manager.py,sha256=K-gU3Kd-eJMunxDKOk4x72CRcyJ50IZXLfqQgyI9zTs,8282
|
|
73
|
+
esgvoc/core/service/configuration/setting.py,sha256=WJgo9ZjZJrTGR9WEBhp1d7ab0Yb2Y6XmnO1oImTPc2s,3042
|
|
74
|
+
esgvoc-0.3.0.dist-info/METADATA,sha256=nIkD0iIw2LFnD7DTYzWIml_af3Rx5Io6KkCFKmADoZE,2165
|
|
75
|
+
esgvoc-0.3.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
76
|
+
esgvoc-0.3.0.dist-info/entry_points.txt,sha256=ZXufSC7Jlx1lb52U6Buv9IitJMcqAAXOerR2V9DaIto,48
|
|
77
|
+
esgvoc-0.3.0.dist-info/licenses/LICENSE.txt,sha256=rWJoZt3vach8ZNdLq-Ee5djzCMFnJ1gIfBeJU5RIop4,21782
|
|
78
|
+
esgvoc-0.3.0.dist-info/RECORD,,
|