esgvoc 0.2.1__py3-none-any.whl → 0.4.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.

Files changed (79) hide show
  1. esgvoc/__init__.py +3 -1
  2. esgvoc/api/__init__.py +96 -72
  3. esgvoc/api/data_descriptors/__init__.py +18 -12
  4. esgvoc/api/data_descriptors/activity.py +8 -45
  5. esgvoc/api/data_descriptors/area_label.py +6 -0
  6. esgvoc/api/data_descriptors/branded_suffix.py +5 -0
  7. esgvoc/api/data_descriptors/branded_variable.py +5 -0
  8. esgvoc/api/data_descriptors/consortium.py +16 -56
  9. esgvoc/api/data_descriptors/data_descriptor.py +106 -0
  10. esgvoc/api/data_descriptors/date.py +3 -46
  11. esgvoc/api/data_descriptors/directory_date.py +3 -46
  12. esgvoc/api/data_descriptors/experiment.py +19 -54
  13. esgvoc/api/data_descriptors/forcing_index.py +3 -45
  14. esgvoc/api/data_descriptors/frequency.py +6 -43
  15. esgvoc/api/data_descriptors/grid_label.py +6 -44
  16. esgvoc/api/data_descriptors/horizontal_label.py +6 -0
  17. esgvoc/api/data_descriptors/initialisation_index.py +3 -44
  18. esgvoc/api/data_descriptors/institution.py +11 -54
  19. esgvoc/api/data_descriptors/license.py +4 -44
  20. esgvoc/api/data_descriptors/mip_era.py +6 -44
  21. esgvoc/api/data_descriptors/model_component.py +7 -45
  22. esgvoc/api/data_descriptors/organisation.py +3 -40
  23. esgvoc/api/data_descriptors/physic_index.py +3 -45
  24. esgvoc/api/data_descriptors/product.py +4 -43
  25. esgvoc/api/data_descriptors/realisation_index.py +3 -44
  26. esgvoc/api/data_descriptors/realm.py +4 -42
  27. esgvoc/api/data_descriptors/resolution.py +6 -44
  28. esgvoc/api/data_descriptors/source.py +18 -53
  29. esgvoc/api/data_descriptors/source_type.py +3 -41
  30. esgvoc/api/data_descriptors/sub_experiment.py +3 -41
  31. esgvoc/api/data_descriptors/table.py +6 -48
  32. esgvoc/api/data_descriptors/temporal_label.py +6 -0
  33. esgvoc/api/data_descriptors/time_range.py +3 -27
  34. esgvoc/api/data_descriptors/variable.py +13 -71
  35. esgvoc/api/data_descriptors/variant_label.py +3 -47
  36. esgvoc/api/data_descriptors/vertical_label.py +5 -0
  37. esgvoc/api/project_specs.py +3 -2
  38. esgvoc/api/projects.py +727 -446
  39. esgvoc/api/py.typed +0 -0
  40. esgvoc/api/report.py +29 -16
  41. esgvoc/api/search.py +140 -95
  42. esgvoc/api/universe.py +362 -156
  43. esgvoc/apps/__init__.py +3 -4
  44. esgvoc/apps/drs/constants.py +1 -1
  45. esgvoc/apps/drs/generator.py +185 -198
  46. esgvoc/apps/drs/report.py +272 -136
  47. esgvoc/apps/drs/validator.py +132 -145
  48. esgvoc/apps/py.typed +0 -0
  49. esgvoc/cli/drs.py +32 -21
  50. esgvoc/cli/get.py +35 -31
  51. esgvoc/cli/install.py +11 -8
  52. esgvoc/cli/main.py +0 -2
  53. esgvoc/cli/status.py +5 -5
  54. esgvoc/cli/valid.py +40 -40
  55. esgvoc/core/constants.py +1 -1
  56. esgvoc/core/db/__init__.py +2 -4
  57. esgvoc/core/db/connection.py +5 -3
  58. esgvoc/core/db/models/project.py +50 -8
  59. esgvoc/core/db/models/universe.py +51 -12
  60. esgvoc/core/db/project_ingestion.py +60 -46
  61. esgvoc/core/db/universe_ingestion.py +58 -29
  62. esgvoc/core/exceptions.py +33 -0
  63. esgvoc/core/logging_handler.py +1 -1
  64. esgvoc/core/repo_fetcher.py +4 -3
  65. esgvoc/core/service/__init__.py +37 -5
  66. esgvoc/core/service/configuration/config_manager.py +188 -0
  67. esgvoc/core/service/configuration/setting.py +88 -0
  68. esgvoc/core/service/state.py +49 -32
  69. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/METADATA +34 -3
  70. esgvoc-0.4.0.dist-info/RECORD +80 -0
  71. esgvoc/api/_utils.py +0 -39
  72. esgvoc/cli/config.py +0 -82
  73. esgvoc/core/service/settings.py +0 -73
  74. esgvoc/core/service/settings.toml +0 -17
  75. esgvoc/core/service/settings_default.toml +0 -17
  76. esgvoc-0.2.1.dist-info/RECORD +0 -73
  77. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/WHEEL +0 -0
  78. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/entry_points.txt +0 -0
  79. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/licenses/LICENSE.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
+
@@ -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.settings import UniverseSettings, ProjectSettings, ServiceSettings
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
- self.local_path = local_path
27
- self.local_access = True # False if we dont have cloned the remote repo yet
28
- self.local_version = None
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.db_path = db_path
31
- self.db_access = True # False if we cant access the db for some reason
32
- self.db_version = None
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.rf = RepoFetcher()
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,7 +96,8 @@ class BaseState:
82
96
 
83
97
 
84
98
  def fetch_versions(self):
85
- self.fetch_version_remote()
99
+ if self.github_access:
100
+ self.fetch_version_remote()
86
101
  self.fetch_version_local()
87
102
  self.fetch_version_db()
88
103
 
@@ -92,18 +107,17 @@ class BaseState:
92
107
  "github" : self.github_version if self.github_version else None,
93
108
  "local": self.local_version if self.local_version else None,
94
109
  "db" : self.db_version if self.db_version else None,
95
- "github_local_sync": self.github_version == self.local_version if self.github_access and self.github_version and self.local_version else None,
96
- "local_db_sync": self.local_version == self.db_version if self.local_access and self.local_version and self.db_version else None,
97
-
98
- "github_db_sync": self.github_version == self.db_version if self.github_access and self.github_version and 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
99
113
  }
100
114
 
101
115
  def clone_remote(self):
102
116
  owner, repo = self.github_repo.lstrip("https://github.com/").split("/")
103
- #TODO add destination "local_path" in clone_repo
104
- 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)
105
119
  self.fetch_version_local()
106
-
120
+
107
121
 
108
122
  def build_db(self):
109
123
  from esgvoc.core.db.project_ingestion import ingest_project
@@ -121,25 +135,25 @@ class BaseState:
121
135
  if self.db_sqlmodel == Universe: # Ugly
122
136
  print("Building Universe DB from ",self.local_path)
123
137
  universe_create_db(Path(self.db_path))
124
- ingest_metadata_universe(DBConnection(Path(self.db_path)),self.local_version)
138
+ self.db_connection = DBConnection(db_file_path= Path(self.db_path))
139
+
140
+ ingest_metadata_universe(self.db_connection,self.local_version)
125
141
  print("Filling Universe DB")
126
- ingest_universe(Path(self.local_path), Path(self.db_path))
142
+ if self.local_path:
143
+ ingest_universe(Path(self.local_path), Path(self.db_path))
127
144
 
128
145
  elif self.db_sqlmodel == Project:
129
146
  print("Building Project DB from ", self.local_path)
130
147
  project_create_db(Path(self.db_path))
131
148
  print("Filling project DB")
132
- ingest_project(Path(self.local_path),Path(self.db_path),self.local_version)
149
+ if self.local_path and self.local_version:
150
+ ingest_project(Path(self.local_path),Path(self.db_path),self.local_version)
133
151
  self.fetch_version_db()
134
152
 
135
153
 
136
154
 
137
-
138
-
139
-
140
155
  def sync(self):
141
156
  summary = self.check_sync_status()
142
-
143
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:
144
158
  self.clone_remote()
145
159
  self.build_db()
@@ -148,7 +162,7 @@ class BaseState:
148
162
  if not summary["local_db_sync"] and summary["local_db_sync"] is not None:
149
163
  self.clone_remote()
150
164
  self.build_db()
151
- elif not summary["github_local_sync"] and summary["github_local_sync"] is not None:
165
+ elif not summary["github_local_sync"] :
152
166
  self.clone_remote()
153
167
  self.build_db()
154
168
  else: # can be simply build in root and clone if neccessary
@@ -194,13 +208,16 @@ class StateService:
194
208
  proj_state.fetch_versions()
195
209
 
196
210
  def synchronize_all(self):
211
+ print("sync universe")
197
212
  self.universe.sync()
213
+ print("sync projects")
198
214
  for project in self.projects.values():
199
215
  project.sync()
216
+
200
217
  def table(self):
201
218
  table = Table(show_header=False, show_lines=True)
202
219
  table.add_row("","Remote github repo","Local repository","Cache Database")
203
- 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)
204
221
  table.add_row("Version",self.universe.github_version,self.universe.local_version,self.universe.db_version)
205
222
  for proj_name,proj in self.projects.items():
206
223
 
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: esgvoc
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: python library and CLI to interact with WCRP CVs
5
5
  Project-URL: Repository, https://github.com/ESGF/esgf-vocab
6
6
  Author-email: Sébastien Gardoll <sebastien@gardoll.fr>, Guillaume Levavasseur <guillaume.levavasseur@ipsl.fr>, Laurent Troussellier <laurent.troussellier@ipsl.fr>
7
7
  License: CECILL-2.1
8
8
  Requires-Python: <3.13,>=3.12
9
9
  Requires-Dist: idna>=3.10
10
+ Requires-Dist: platformdirs>=4.3.6
10
11
  Requires-Dist: pydantic>=2.9.2
11
12
  Requires-Dist: pyld>=2.0.4
12
13
  Requires-Dist: requests>=2.32.3
13
- Requires-Dist: sphinx-copybutton>=0.5.2
14
- Requires-Dist: sphinx-tabs>=3.4.7
15
14
  Requires-Dist: sqlalchemy>=2.0.36
16
15
  Requires-Dist: sqlmodel>=0.0.22
17
16
  Requires-Dist: toml>=0.10.2
@@ -56,3 +55,35 @@ Following this command to install or update the latest CVs.
56
55
  ```bash
57
56
  esgvoc install
58
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,80 @@
1
+ esgvoc/__init__.py,sha256=1U7uasmBR5VzekoxCEz4GBNgpPcsEU5BwY-QOSW37qA,66
2
+ esgvoc/api/__init__.py,sha256=w68CdVRS553bDWezZoCTxIFq_vsP7mFluSoO4yUo_Uc,4130
3
+ esgvoc/api/project_specs.py,sha256=a-hEL-tXsfvwFeSU5PBnQZq_tqB7L67wxpB1ACah-no,2330
4
+ esgvoc/api/projects.py,sha256=y6qcBSuW22a4nDB9V8lU6PFUj1j2SOrj3xtu9FKVyS4,57466
5
+ esgvoc/api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ esgvoc/api/report.py,sha256=OlK5ApnaikMKmC6FyJ1uTSBeTezZe85yTCJwsk14uZE,3526
7
+ esgvoc/api/search.py,sha256=Ss_-EvCQHZc6pBTDJzPUrOE_ErTcdqa-zmLkcBjsVkk,6667
8
+ esgvoc/api/universe.py,sha256=8aEvkTHWv9PEWz9hw_JYDdjJBbHzizi_RdVg9Ua-ook,21830
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/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ esgvoc/apps/drs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ esgvoc/apps/drs/constants.py,sha256=rVWq1QQwAFgISjvl3YzJDLLPNUPXHpqgv66spmjyPMQ,96
47
+ esgvoc/apps/drs/generator.py,sha256=M0QMtpAaMU4E0142FPqdhix3n034CU_QaPuwvXsWtlA,22392
48
+ esgvoc/apps/drs/report.py,sha256=ZRu5l6T-U-hqY7O3ZwAseYbWZPcJiMhJ2dpFKZJE3Gk,17371
49
+ esgvoc/apps/drs/validator.py,sha256=yNijdOPhF9adgZbya5Ugvs13GbL4MvgQepCT38A66vM,13825
50
+ esgvoc/cli/drs.py,sha256=PvVbLxef34A1IO600AFWOEWb5iLaWrBRHwwgMJ4u-PM,9237
51
+ esgvoc/cli/get.py,sha256=zwLWkWOfAddK5onyRjWZ0zU909MtTDjEB_YrQtIwXqw,5242
52
+ esgvoc/cli/install.py,sha256=zMssevZDrigrUlw1QHWITGUL1TvsHMZBQdYeNtHgWQA,433
53
+ esgvoc/cli/main.py,sha256=kjB-yus-cmG9rOmVIPBhmjOr3tkwB13dHLcNqrdpYAM,483
54
+ esgvoc/cli/status.py,sha256=hmpyrszrb5ke9l_1SgendPSeoXW1h-h7nH0zGFt_vUw,1357
55
+ esgvoc/cli/valid.py,sha256=XrseGONeWR6gnnwZrRMJNjVBFQLT82Uzn5rHrjjM1Uk,7040
56
+ esgvoc/core/constants.py,sha256=i03VR29sQmg89DdQpGZ1fzBT-elT3-_S0bTNraGA6T4,432
57
+ esgvoc/core/convert.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ esgvoc/core/data_handler.py,sha256=BmcD_dSvX5fCkUEGAQnadPTeuKA7IvgMmQtesMXKh5g,5004
59
+ esgvoc/core/exceptions.py,sha256=hs2D1lRWYhFuXDRbApKyJmEZMs8HxTPlSGkDSpkeQiA,538
60
+ esgvoc/core/logging.conf,sha256=GK52lsTu17CfC2tKiMAIvkuHkIt5tqPmkWv68COOilc,278
61
+ esgvoc/core/logging_handler.py,sha256=VgRBWeW_xdC4HtXG0TleQFwoy_YbJR4wlpci_sFezK0,147
62
+ esgvoc/core/repo_fetcher.py,sha256=Rg668F4NN60jd8Ydz9dXpJQxd2eQOgaO0Ziwm53mcaI,9823
63
+ esgvoc/core/db/__init__.py,sha256=fszGxJfRUa6uuuogrdh8_ExtdyMLZSaaVawpdgXzqKM,113
64
+ esgvoc/core/db/connection.py,sha256=AIFo0IWznu0Alk0SK_4bqp6FL5ZqSezNrfc_AlM9Z14,882
65
+ esgvoc/core/db/project_ingestion.py,sha256=M0Yruh7w--Q9JyRnbLynoPiVfmufp8b6h5UuUeNnnPA,7687
66
+ esgvoc/core/db/universe_ingestion.py,sha256=vPNr_svmduX3JKuLA_-SQMTxO2FWMqCNyCWkPSDznNE,6522
67
+ esgvoc/core/db/models/mixins.py,sha256=S4_6iuKf1kYLdUXAgqRKSTXs8H9I--43MKlEq4F-dm4,445
68
+ esgvoc/core/db/models/project.py,sha256=hkDCPJNo3wGt-UMOfujeQYPgju_aH1oExDAnIgoe96M,4369
69
+ esgvoc/core/db/models/universe.py,sha256=vrR1TMD9ZI6RSGRi-qnLEKHD2Qk6Mh3qz8gciPgsELQ,4199
70
+ esgvoc/core/service/__init__.py,sha256=hveqCB4oC6gKDf_L-wZxu9iBz7RiY4x9OeJGP6S5xtU,1534
71
+ esgvoc/core/service/data_merger.py,sha256=GNFp5DTV2jlBVJZNpILngi6jCbUvVGcqka4EMWKj_Os,3456
72
+ esgvoc/core/service/esg_voc.py,sha256=5G0P4_xmQzoI_RG_agpq-yHoYYZx220P27v2nPrpyNs,2420
73
+ esgvoc/core/service/state.py,sha256=f1Pb11yYRAcLyK93zJ60i5l-mifDXP8_81tJQtMAvPo,10622
74
+ esgvoc/core/service/configuration/config_manager.py,sha256=K-gU3Kd-eJMunxDKOk4x72CRcyJ50IZXLfqQgyI9zTs,8282
75
+ esgvoc/core/service/configuration/setting.py,sha256=WJgo9ZjZJrTGR9WEBhp1d7ab0Yb2Y6XmnO1oImTPc2s,3042
76
+ esgvoc-0.4.0.dist-info/METADATA,sha256=rDbK18ahBLJtPMTi00NI64tEhNn8ulPlfvMoJ4TBIlc,2165
77
+ esgvoc-0.4.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
78
+ esgvoc-0.4.0.dist-info/entry_points.txt,sha256=ZXufSC7Jlx1lb52U6Buv9IitJMcqAAXOerR2V9DaIto,48
79
+ esgvoc-0.4.0.dist-info/licenses/LICENSE.txt,sha256=rWJoZt3vach8ZNdLq-Ee5djzCMFnJ1gIfBeJU5RIop4,21782
80
+ esgvoc-0.4.0.dist-info/RECORD,,
esgvoc/api/_utils.py DELETED
@@ -1,39 +0,0 @@
1
- from typing import Sequence
2
-
3
-
4
- import esgvoc.core.constants as api_settings
5
- from esgvoc.api.data_descriptors import DATA_DESCRIPTOR_CLASS_MAPPING
6
- from esgvoc.core.db.models.project import PTerm
7
- from esgvoc.core.db.models.universe import UTerm
8
- from pydantic import BaseModel
9
- from sqlmodel import Session
10
-
11
- import esgvoc.core.service as service
12
- UNIVERSE_DB_CONNECTION = service.state_service.universe.db_connection
13
-
14
-
15
- def get_pydantic_class(data_descriptor_id_or_term_type: str) -> type[BaseModel]:
16
- if data_descriptor_id_or_term_type in DATA_DESCRIPTOR_CLASS_MAPPING:
17
- return DATA_DESCRIPTOR_CLASS_MAPPING[data_descriptor_id_or_term_type]
18
- else:
19
- raise ValueError(f"{data_descriptor_id_or_term_type} pydantic class not found")
20
-
21
-
22
- def get_universe_session() -> Session:
23
-
24
- if UNIVERSE_DB_CONNECTION:
25
- return UNIVERSE_DB_CONNECTION.create_session()
26
- else:
27
- raise RuntimeError('universe connection is not initialized')
28
-
29
-
30
- def instantiate_pydantic_term(term: UTerm|PTerm) -> BaseModel:
31
- term_class = get_pydantic_class(term.specs[api_settings.TERM_TYPE_JSON_KEY])
32
- return term_class(**term.specs)
33
-
34
-
35
- def instantiate_pydantic_terms(db_terms: Sequence[UTerm|PTerm],
36
- list_to_populate: list[BaseModel]) -> None:
37
- for db_term in db_terms:
38
- term = instantiate_pydantic_term(db_term)
39
- list_to_populate.append(term)
esgvoc/cli/config.py DELETED
@@ -1,82 +0,0 @@
1
-
2
- import json
3
- from rich.syntax import Syntax
4
- import typer
5
- from esgvoc.core.service.settings import SETTINGS_FILE, ServiceSettings, load_settings
6
- from rich import print
7
- import toml
8
-
9
- app = typer.Typer()
10
-
11
-
12
- def get_nested_value(settings_dict: dict, key_path: str):
13
- """Navigate through nested dictionary keys using dot-separated key paths."""
14
- keys = key_path.split(".")
15
- value = settings_dict
16
- for key in keys:
17
- value = value[key]
18
- return value
19
-
20
- def set_nested_value(settings_dict: dict, key_path: str, new_value):
21
- """Set a value in a nested dictionary using a dot-separated key path."""
22
- keys = key_path.split(".")
23
- sub_dict = settings_dict
24
- for key in keys[:-1]:
25
- sub_dict = sub_dict[key]
26
- sub_dict[keys[-1]] = new_value
27
- return settings_dict
28
-
29
- @app.command()
30
- def config(key: str |None = typer.Argument(None), value: str|None = typer.Argument(None)):
31
- """
32
- Manage configuration settings.
33
-
34
- - With no arguments: display all settings.
35
- - With one argument (key): display the value of the key.
36
- - With two arguments (key and value): modify the key's value and save.
37
-
38
- usage :
39
- esgvoc config universe.db_path .cache/dbs/somethingelse
40
- """
41
-
42
- settings = load_settings()
43
- if key is None:
44
- # No key provided, print all settings
45
- # typer.echo(settings.model_dump())
46
- syntax = Syntax(toml.dumps(settings.model_dump()), "toml")
47
- print(syntax)
48
- return
49
- if value is None:
50
- # Key provided but no value, print the specific key's value
51
- try:
52
- selected_value = get_nested_value(json.loads(settings.model_dump_json()),key)
53
- typer.echo(selected_value)
54
- except KeyError:
55
- try:
56
- selected_value = get_nested_value(json.loads(settings.model_dump_json()),"projects."+key)
57
- typer.echo(selected_value)
58
- return
59
- except KeyError:
60
- pass
61
- typer.echo(f"Key '{key}' not found in settings.")
62
- return
63
-
64
- # Modify the key's value
65
- try :
66
- selected_value = get_nested_value(json.loads(settings.model_dump_json()),key)
67
- except Exception:
68
- key = "projects."+key
69
- try :
70
- selected_value = get_nested_value(json.loads(settings.model_dump_json()),key)
71
- if selected_value:
72
- new_settings_dict = set_nested_value(json.loads(settings.model_dump_json()),key, value )
73
- new_settings = ServiceSettings(**new_settings_dict)
74
- new_settings.save_to_file(str(SETTINGS_FILE)) #TODO improved that .. remove SETTINGS_FILE dependancy
75
- # save_settings(new_settings)
76
- typer.echo(f"New settings {new_settings.model_dump_json(indent=4)}")
77
- typer.echo(f"Updated '{key}' to '{value}'.")
78
- else:
79
- typer.echo(f"Key '{key}' not found in settings.")
80
- except Exception as e:
81
- typer.echo(f"Error updating settings: {e}")
82
-
@@ -1,73 +0,0 @@
1
- from pydantic import BaseModel, Field
2
- from typing import Dict, Optional
3
- from pathlib import Path
4
- import toml
5
-
6
-
7
- SETTINGS_FILE = Path(__file__).parent / "settings.toml"
8
-
9
- class ProjectSettings(BaseModel):
10
- project_name: str
11
- github_repo: str
12
- branch: Optional[str] = "main"
13
- local_path: Optional[str] = None
14
- db_path: Optional[str] = None
15
-
16
- class UniverseSettings(BaseModel):
17
- github_repo: str
18
- branch: Optional[str] = None
19
- local_path: Optional[str] = None
20
- db_path: Optional[str] = None
21
-
22
- class ServiceSettings(BaseModel):
23
- universe: UniverseSettings
24
- projects: Dict[str, ProjectSettings] = Field(default_factory=dict)
25
-
26
- @classmethod
27
- def load_from_file(cls, file_path: str) -> "ServiceSettings":
28
- data = toml.load(file_path)
29
- projects = {p['project_name']: ProjectSettings(**p) for p in data.pop('projects', [])}
30
- return cls(universe=UniverseSettings(**data['universe']), projects=projects)
31
-
32
- def save_to_file(self, file_path: str):
33
- data = {
34
- "universe": self.universe.model_dump(),
35
- "projects": [p.model_dump() for p in self.projects.values()]
36
- }
37
- with open(file_path, "w") as f:
38
- toml.dump(data, f)
39
-
40
- def load_settings() -> ServiceSettings:
41
- """Load the settings from the TOML file."""
42
- if SETTINGS_FILE.exists():
43
- return ServiceSettings.load_from_file(str(SETTINGS_FILE))
44
- else:
45
- default_settings = ServiceSettings(
46
- universe=UniverseSettings(
47
- github_repo="https://github.com/WCRP-CMIP/WCRP-universe",
48
- branch="esgvoc",
49
- local_path=".cache/repos/WCRP-universe",
50
- db_path=".cache/dbs/universe.sqlite"
51
- ),
52
- projects={"cmip6plus":ProjectSettings(
53
- project_name="CMIP6Plus_CVs",
54
- github_repo="https://github.com/WCRP-CMIP/CMIP6Plus_CVs",
55
- branch="esgvoc",
56
- local_path=".cache/repos/CMIP6Plus_CVs",
57
- db_path=".cache/dbs/cmip6plus.sqlite"
58
- ),
59
-
60
- "cmip6":ProjectSettings(
61
- project_name="CMIP6_CVs",
62
- github_repo="https://github.com/WCRP-CMIP/CMIP6_CVs",
63
- branch="esgvoc",
64
- local_path=".cache/repos/CMIP6_CVs",
65
- db_path=".cache/dbs/cmip6.sqlite"
66
- )
67
- }
68
-
69
- )
70
-
71
-
72
- default_settings.save_to_file(str(SETTINGS_FILE))
73
- return default_settings
@@ -1,17 +0,0 @@
1
- [[projects]]
2
- project_name = "cmip6"
3
- github_repo = "https://github.com/WCRP-CMIP/CMIP6_CVs"
4
- branch = "esgvoc"
5
- local_path = ".cache/repos/CMIP6_CVs"
6
- db_path = ".cache/dbs/cmip6.sqlite"
7
- [[projects]]
8
- project_name = "cmip6plus"
9
- github_repo = "https://github.com/WCRP-CMIP/CMIP6Plus_CVs"
10
- branch = "esgvoc"
11
- local_path = ".cache/repos/CMIP6Plus_CVs"
12
- db_path = ".cache/dbs/cmip6plus.sqlite"
13
- [universe]
14
- github_repo = "https://github.com/WCRP-CMIP/WCRP-universe"
15
- branch = "esgvoc"
16
- local_path = ".cache/repos/WCRP-universe"
17
- db_path = ".cache/dbs/universe.sqlite"
@@ -1,17 +0,0 @@
1
- [[projects]]
2
- project_name = "cmip6"
3
- github_repo = "https://github.com/WCRP-CMIP/CMIP6_CVs"
4
- branch = "esgvoc"
5
- local_path = ".cache/repos/CMIP6_CVs"
6
- db_path = ".cache/dbs/cmip6.sqlite"
7
- [[projects]]
8
- project_name = "cmip6plus"
9
- github_repo = "https://github.com/WCRP-CMIP/CMIP6Plus_CVs"
10
- branch = "esgvoc"
11
- local_path = ".cache/repos/CMIP6Plus_CVs"
12
- db_path = ".cache/dbs/cmip6plus.sqlite"
13
- [universe]
14
- github_repo = "https://github.com/WCRP-CMIP/WCRP-universe"
15
- branch = "esgvoc"
16
- local_path = ".cache/repos/WCRP-universe"
17
- db_path = ".cache/dbs/universe.sqlite"