esgvoc 2.0.2__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.
- esgvoc/__init__.py +3 -0
- esgvoc/api/__init__.py +91 -0
- esgvoc/api/data_descriptors/EMD_models/__init__.py +66 -0
- esgvoc/api/data_descriptors/EMD_models/arrangement.py +21 -0
- esgvoc/api/data_descriptors/EMD_models/calendar.py +5 -0
- esgvoc/api/data_descriptors/EMD_models/cell_variable_type.py +20 -0
- esgvoc/api/data_descriptors/EMD_models/component_type.py +5 -0
- esgvoc/api/data_descriptors/EMD_models/coordinate.py +52 -0
- esgvoc/api/data_descriptors/EMD_models/grid_mapping.py +19 -0
- esgvoc/api/data_descriptors/EMD_models/grid_region.py +19 -0
- esgvoc/api/data_descriptors/EMD_models/grid_type.py +19 -0
- esgvoc/api/data_descriptors/EMD_models/horizontal_computational_grid.py +56 -0
- esgvoc/api/data_descriptors/EMD_models/horizontal_grid_cells.py +230 -0
- esgvoc/api/data_descriptors/EMD_models/horizontal_subgrid.py +41 -0
- esgvoc/api/data_descriptors/EMD_models/horizontal_units.py +5 -0
- esgvoc/api/data_descriptors/EMD_models/model.py +139 -0
- esgvoc/api/data_descriptors/EMD_models/model_component.py +115 -0
- esgvoc/api/data_descriptors/EMD_models/reference.py +61 -0
- esgvoc/api/data_descriptors/EMD_models/resolution.py +48 -0
- esgvoc/api/data_descriptors/EMD_models/temporal_refinement.py +19 -0
- esgvoc/api/data_descriptors/EMD_models/truncation_method.py +17 -0
- esgvoc/api/data_descriptors/EMD_models/vertical_computational_grid.py +91 -0
- esgvoc/api/data_descriptors/EMD_models/vertical_coordinate.py +5 -0
- esgvoc/api/data_descriptors/EMD_models/vertical_units.py +19 -0
- esgvoc/api/data_descriptors/__init__.py +159 -0
- esgvoc/api/data_descriptors/activity.py +72 -0
- esgvoc/api/data_descriptors/archive.py +5 -0
- esgvoc/api/data_descriptors/area_label.py +30 -0
- esgvoc/api/data_descriptors/branded_suffix.py +30 -0
- esgvoc/api/data_descriptors/branded_variable.py +21 -0
- esgvoc/api/data_descriptors/citation_url.py +5 -0
- esgvoc/api/data_descriptors/contact.py +5 -0
- esgvoc/api/data_descriptors/conventions.py +28 -0
- esgvoc/api/data_descriptors/creation_date.py +18 -0
- esgvoc/api/data_descriptors/data_descriptor.py +127 -0
- esgvoc/api/data_descriptors/data_specs_version.py +25 -0
- esgvoc/api/data_descriptors/date.py +5 -0
- esgvoc/api/data_descriptors/directory_date.py +22 -0
- esgvoc/api/data_descriptors/drs_specs.py +38 -0
- esgvoc/api/data_descriptors/experiment.py +215 -0
- esgvoc/api/data_descriptors/forcing_index.py +21 -0
- esgvoc/api/data_descriptors/frequency.py +48 -0
- esgvoc/api/data_descriptors/further_info_url.py +5 -0
- esgvoc/api/data_descriptors/grid.py +43 -0
- esgvoc/api/data_descriptors/horizontal_label.py +20 -0
- esgvoc/api/data_descriptors/initialization_index.py +27 -0
- esgvoc/api/data_descriptors/institution.py +80 -0
- esgvoc/api/data_descriptors/known_branded_variable.py +75 -0
- esgvoc/api/data_descriptors/license.py +31 -0
- esgvoc/api/data_descriptors/member_id.py +9 -0
- esgvoc/api/data_descriptors/mip_era.py +26 -0
- esgvoc/api/data_descriptors/model_component.py +32 -0
- esgvoc/api/data_descriptors/models_test/models.py +17 -0
- esgvoc/api/data_descriptors/nominal_resolution.py +50 -0
- esgvoc/api/data_descriptors/obs_type.py +5 -0
- esgvoc/api/data_descriptors/organisation.py +22 -0
- esgvoc/api/data_descriptors/physics_index.py +21 -0
- esgvoc/api/data_descriptors/product.py +16 -0
- esgvoc/api/data_descriptors/publication_status.py +5 -0
- esgvoc/api/data_descriptors/realization_index.py +24 -0
- esgvoc/api/data_descriptors/realm.py +16 -0
- esgvoc/api/data_descriptors/regex.py +5 -0
- esgvoc/api/data_descriptors/region.py +35 -0
- esgvoc/api/data_descriptors/resolution.py +7 -0
- esgvoc/api/data_descriptors/source.py +120 -0
- esgvoc/api/data_descriptors/source_type.py +5 -0
- esgvoc/api/data_descriptors/sub_experiment.py +5 -0
- esgvoc/api/data_descriptors/table.py +28 -0
- esgvoc/api/data_descriptors/temporal_label.py +20 -0
- esgvoc/api/data_descriptors/time_range.py +17 -0
- esgvoc/api/data_descriptors/title.py +5 -0
- esgvoc/api/data_descriptors/tracking_id.py +67 -0
- esgvoc/api/data_descriptors/variable.py +56 -0
- esgvoc/api/data_descriptors/variant_label.py +25 -0
- esgvoc/api/data_descriptors/vertical_label.py +20 -0
- esgvoc/api/project_specs.py +143 -0
- esgvoc/api/projects.py +1253 -0
- esgvoc/api/py.typed +0 -0
- esgvoc/api/pydantic_handler.py +146 -0
- esgvoc/api/report.py +127 -0
- esgvoc/api/search.py +171 -0
- esgvoc/api/universe.py +434 -0
- esgvoc/apps/__init__.py +6 -0
- esgvoc/apps/cmor_tables/__init__.py +7 -0
- esgvoc/apps/cmor_tables/cvs_table.py +948 -0
- esgvoc/apps/drs/__init__.py +0 -0
- esgvoc/apps/drs/constants.py +2 -0
- esgvoc/apps/drs/generator.py +429 -0
- esgvoc/apps/drs/report.py +540 -0
- esgvoc/apps/drs/validator.py +312 -0
- esgvoc/apps/ga/__init__.py +104 -0
- esgvoc/apps/ga/example_usage.py +315 -0
- esgvoc/apps/ga/models/__init__.py +47 -0
- esgvoc/apps/ga/models/netcdf_header.py +306 -0
- esgvoc/apps/ga/models/validator.py +491 -0
- esgvoc/apps/ga/test_ga.py +161 -0
- esgvoc/apps/ga/validator.py +277 -0
- esgvoc/apps/jsg/json_schema_generator.py +341 -0
- esgvoc/apps/jsg/templates/template.jinja +241 -0
- esgvoc/apps/test_cv/README.md +214 -0
- esgvoc/apps/test_cv/__init__.py +0 -0
- esgvoc/apps/test_cv/cv_tester.py +1611 -0
- esgvoc/apps/test_cv/example_usage.py +216 -0
- esgvoc/apps/vr/__init__.py +12 -0
- esgvoc/apps/vr/build_variable_registry.py +71 -0
- esgvoc/apps/vr/example_usage.py +60 -0
- esgvoc/apps/vr/vr_app.py +333 -0
- esgvoc/cli/clean.py +304 -0
- esgvoc/cli/cmor.py +46 -0
- esgvoc/cli/config.py +1300 -0
- esgvoc/cli/drs.py +267 -0
- esgvoc/cli/find.py +138 -0
- esgvoc/cli/get.py +155 -0
- esgvoc/cli/install.py +41 -0
- esgvoc/cli/main.py +60 -0
- esgvoc/cli/offline.py +269 -0
- esgvoc/cli/status.py +79 -0
- esgvoc/cli/test_cv.py +258 -0
- esgvoc/cli/valid.py +147 -0
- esgvoc/core/constants.py +17 -0
- esgvoc/core/convert.py +0 -0
- esgvoc/core/data_handler.py +206 -0
- esgvoc/core/db/__init__.py +3 -0
- esgvoc/core/db/connection.py +40 -0
- esgvoc/core/db/models/mixins.py +25 -0
- esgvoc/core/db/models/project.py +102 -0
- esgvoc/core/db/models/universe.py +98 -0
- esgvoc/core/db/project_ingestion.py +231 -0
- esgvoc/core/db/universe_ingestion.py +172 -0
- esgvoc/core/exceptions.py +33 -0
- esgvoc/core/logging_handler.py +26 -0
- esgvoc/core/repo_fetcher.py +345 -0
- esgvoc/core/service/__init__.py +41 -0
- esgvoc/core/service/configuration/config_manager.py +196 -0
- esgvoc/core/service/configuration/setting.py +363 -0
- esgvoc/core/service/data_merger.py +634 -0
- esgvoc/core/service/esg_voc.py +77 -0
- esgvoc/core/service/resolver_config.py +56 -0
- esgvoc/core/service/state.py +324 -0
- esgvoc/core/service/string_heuristics.py +98 -0
- esgvoc/core/service/term_cache.py +108 -0
- esgvoc/core/service/uri_resolver.py +133 -0
- esgvoc-2.0.2.dist-info/METADATA +82 -0
- esgvoc-2.0.2.dist-info/RECORD +147 -0
- esgvoc-2.0.2.dist-info/WHEEL +4 -0
- esgvoc-2.0.2.dist-info/entry_points.txt +2 -0
- esgvoc-2.0.2.dist-info/licenses/LICENSE.txt +519 -0
esgvoc/cli/offline.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Offline mode management CLI commands.
|
|
3
|
+
|
|
4
|
+
This module provides CLI commands for managing offline mode settings
|
|
5
|
+
for universe and project components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from esgvoc.core.service import config_manager
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command()
|
|
20
|
+
def show(
|
|
21
|
+
component: str = typer.Argument(
|
|
22
|
+
None,
|
|
23
|
+
help="Component to show offline status for (universe or project name). Shows all if not specified."
|
|
24
|
+
),
|
|
25
|
+
config: str = typer.Option(
|
|
26
|
+
None,
|
|
27
|
+
"--config",
|
|
28
|
+
"-c",
|
|
29
|
+
help="Configuration name to use"
|
|
30
|
+
)
|
|
31
|
+
):
|
|
32
|
+
"""Show offline mode status for components."""
|
|
33
|
+
try:
|
|
34
|
+
if config:
|
|
35
|
+
settings = config_manager.get_config(config)
|
|
36
|
+
else:
|
|
37
|
+
settings = config_manager.get_active_config()
|
|
38
|
+
|
|
39
|
+
if component:
|
|
40
|
+
# Show specific component
|
|
41
|
+
if component == "universe":
|
|
42
|
+
if settings.universe.offline_mode:
|
|
43
|
+
console.print(f"[green]Universe is in offline mode[/green]")
|
|
44
|
+
else:
|
|
45
|
+
console.print(f"[yellow]Universe is in online mode[/yellow]")
|
|
46
|
+
elif component in settings.projects:
|
|
47
|
+
project = settings.projects[component]
|
|
48
|
+
if project.offline_mode:
|
|
49
|
+
console.print(f"[green]Project '{component}' is in offline mode[/green]")
|
|
50
|
+
else:
|
|
51
|
+
console.print(f"[yellow]Project '{component}' is in online mode[/yellow]")
|
|
52
|
+
else:
|
|
53
|
+
console.print(f"[red]Component '{component}' not found[/red]")
|
|
54
|
+
raise typer.Exit(1)
|
|
55
|
+
else:
|
|
56
|
+
# Show all components
|
|
57
|
+
table = Table(title="Offline Mode Status")
|
|
58
|
+
table.add_column("Component", style="cyan")
|
|
59
|
+
table.add_column("Type", style="magenta")
|
|
60
|
+
table.add_column("Offline Mode", style="bold")
|
|
61
|
+
|
|
62
|
+
# Universe
|
|
63
|
+
status = "[green]✓ Enabled[/green]" if settings.universe.offline_mode else "[yellow]✗ Disabled[/yellow]"
|
|
64
|
+
table.add_row("Universe", "Universe", status)
|
|
65
|
+
|
|
66
|
+
# Projects
|
|
67
|
+
for project_name, project in settings.projects.items():
|
|
68
|
+
status = "[green]✓ Enabled[/green]" if project.offline_mode else "[yellow]✗ Disabled[/yellow]"
|
|
69
|
+
table.add_row(project_name, "Project", status)
|
|
70
|
+
|
|
71
|
+
console.print(table)
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
console.print(f"[red]Error: {str(e)}[/red]")
|
|
75
|
+
raise typer.Exit(1)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@app.command()
|
|
79
|
+
def enable(
|
|
80
|
+
component: str = typer.Argument(
|
|
81
|
+
None,
|
|
82
|
+
help="Component to enable offline mode for (universe or project name). If not specified, enables for all components."
|
|
83
|
+
),
|
|
84
|
+
config: str = typer.Option(
|
|
85
|
+
None,
|
|
86
|
+
"--config",
|
|
87
|
+
"-c",
|
|
88
|
+
help="Configuration name to use"
|
|
89
|
+
)
|
|
90
|
+
):
|
|
91
|
+
"""Enable offline mode for a component or all components if none specified."""
|
|
92
|
+
try:
|
|
93
|
+
if config:
|
|
94
|
+
settings = config_manager.get_config(config)
|
|
95
|
+
else:
|
|
96
|
+
settings = config_manager.get_active_config()
|
|
97
|
+
|
|
98
|
+
if component is None:
|
|
99
|
+
# Enable for all components
|
|
100
|
+
settings.universe.offline_mode = True
|
|
101
|
+
for project in settings.projects.values():
|
|
102
|
+
project.offline_mode = True
|
|
103
|
+
console.print(f"[green]✓ Enabled offline mode for all components[/green]")
|
|
104
|
+
elif component == "universe":
|
|
105
|
+
settings.universe.offline_mode = True
|
|
106
|
+
console.print(f"[green]✓ Enabled offline mode for universe[/green]")
|
|
107
|
+
elif component in settings.projects:
|
|
108
|
+
settings.projects[component].offline_mode = True
|
|
109
|
+
console.print(f"[green]✓ Enabled offline mode for project '{component}'[/green]")
|
|
110
|
+
else:
|
|
111
|
+
console.print(f"[red]Component '{component}' not found[/red]")
|
|
112
|
+
raise typer.Exit(1)
|
|
113
|
+
|
|
114
|
+
# Save the updated settings
|
|
115
|
+
if config:
|
|
116
|
+
# Use the correct format for saving
|
|
117
|
+
data = {
|
|
118
|
+
"universe": settings.universe.model_dump(),
|
|
119
|
+
"projects": [p.model_dump() for p in settings.projects.values()],
|
|
120
|
+
}
|
|
121
|
+
config_manager.save_config(data, config)
|
|
122
|
+
else:
|
|
123
|
+
config_manager.save_active_config(settings)
|
|
124
|
+
console.print(f"[blue]Configuration saved[/blue]")
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
console.print(f"[red]Error: {str(e)}[/red]")
|
|
128
|
+
raise typer.Exit(1)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@app.command()
|
|
132
|
+
def disable(
|
|
133
|
+
component: str = typer.Argument(
|
|
134
|
+
None,
|
|
135
|
+
help="Component to disable offline mode for (universe or project name). If not specified, disables for all components."
|
|
136
|
+
),
|
|
137
|
+
config: str = typer.Option(
|
|
138
|
+
None,
|
|
139
|
+
"--config",
|
|
140
|
+
"-c",
|
|
141
|
+
help="Configuration name to use"
|
|
142
|
+
)
|
|
143
|
+
):
|
|
144
|
+
"""Disable offline mode for a component or all components if none specified."""
|
|
145
|
+
try:
|
|
146
|
+
if config:
|
|
147
|
+
settings = config_manager.get_config(config)
|
|
148
|
+
else:
|
|
149
|
+
settings = config_manager.get_active_config()
|
|
150
|
+
|
|
151
|
+
if component is None:
|
|
152
|
+
# Disable for all components
|
|
153
|
+
settings.universe.offline_mode = False
|
|
154
|
+
for project in settings.projects.values():
|
|
155
|
+
project.offline_mode = False
|
|
156
|
+
console.print(f"[yellow]✓ Disabled offline mode for all components[/yellow]")
|
|
157
|
+
elif component == "universe":
|
|
158
|
+
settings.universe.offline_mode = False
|
|
159
|
+
console.print(f"[yellow]✓ Disabled offline mode for universe[/yellow]")
|
|
160
|
+
elif component in settings.projects:
|
|
161
|
+
settings.projects[component].offline_mode = False
|
|
162
|
+
console.print(f"[yellow]✓ Disabled offline mode for project '{component}'[/yellow]")
|
|
163
|
+
else:
|
|
164
|
+
console.print(f"[red]Component '{component}' not found[/red]")
|
|
165
|
+
raise typer.Exit(1)
|
|
166
|
+
|
|
167
|
+
# Save the updated settings
|
|
168
|
+
if config:
|
|
169
|
+
# Use the correct format for saving
|
|
170
|
+
data = {
|
|
171
|
+
"universe": settings.universe.model_dump(),
|
|
172
|
+
"projects": [p.model_dump() for p in settings.projects.values()],
|
|
173
|
+
}
|
|
174
|
+
config_manager.save_config(data, config)
|
|
175
|
+
else:
|
|
176
|
+
config_manager.save_active_config(settings)
|
|
177
|
+
console.print(f"[blue]Configuration saved[/blue]")
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
console.print(f"[red]Error: {str(e)}[/red]")
|
|
181
|
+
raise typer.Exit(1)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@app.command()
|
|
185
|
+
def enable_all(
|
|
186
|
+
config: str = typer.Option(
|
|
187
|
+
None,
|
|
188
|
+
"--config",
|
|
189
|
+
"-c",
|
|
190
|
+
help="Configuration name to use"
|
|
191
|
+
)
|
|
192
|
+
):
|
|
193
|
+
"""Enable offline mode for all components."""
|
|
194
|
+
try:
|
|
195
|
+
if config:
|
|
196
|
+
settings = config_manager.get_config(config)
|
|
197
|
+
else:
|
|
198
|
+
settings = config_manager.get_active_config()
|
|
199
|
+
|
|
200
|
+
# Enable for universe
|
|
201
|
+
settings.universe.offline_mode = True
|
|
202
|
+
|
|
203
|
+
# Enable for all projects
|
|
204
|
+
for project in settings.projects.values():
|
|
205
|
+
project.offline_mode = True
|
|
206
|
+
|
|
207
|
+
# Save the updated settings
|
|
208
|
+
if config:
|
|
209
|
+
# Use the correct format for saving
|
|
210
|
+
data = {
|
|
211
|
+
"universe": settings.universe.model_dump(),
|
|
212
|
+
"projects": [p.model_dump() for p in settings.projects.values()],
|
|
213
|
+
}
|
|
214
|
+
config_manager.save_config(data, config)
|
|
215
|
+
else:
|
|
216
|
+
config_manager.save_active_config(settings)
|
|
217
|
+
|
|
218
|
+
console.print(f"[green]✓ Enabled offline mode for all components[/green]")
|
|
219
|
+
console.print(f"[blue]Configuration saved[/blue]")
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
console.print(f"[red]Error: {str(e)}[/red]")
|
|
223
|
+
raise typer.Exit(1)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@app.command()
|
|
227
|
+
def disable_all(
|
|
228
|
+
config: str = typer.Option(
|
|
229
|
+
None,
|
|
230
|
+
"--config",
|
|
231
|
+
"-c",
|
|
232
|
+
help="Configuration name to use"
|
|
233
|
+
)
|
|
234
|
+
):
|
|
235
|
+
"""Disable offline mode for all components."""
|
|
236
|
+
try:
|
|
237
|
+
if config:
|
|
238
|
+
settings = config_manager.get_config(config)
|
|
239
|
+
else:
|
|
240
|
+
settings = config_manager.get_active_config()
|
|
241
|
+
|
|
242
|
+
# Disable for universe
|
|
243
|
+
settings.universe.offline_mode = False
|
|
244
|
+
|
|
245
|
+
# Disable for all projects
|
|
246
|
+
for project in settings.projects.values():
|
|
247
|
+
project.offline_mode = False
|
|
248
|
+
|
|
249
|
+
# Save the updated settings
|
|
250
|
+
if config:
|
|
251
|
+
# Use the correct format for saving
|
|
252
|
+
data = {
|
|
253
|
+
"universe": settings.universe.model_dump(),
|
|
254
|
+
"projects": [p.model_dump() for p in settings.projects.values()],
|
|
255
|
+
}
|
|
256
|
+
config_manager.save_config(data, config)
|
|
257
|
+
else:
|
|
258
|
+
config_manager.save_active_config(settings)
|
|
259
|
+
|
|
260
|
+
console.print(f"[yellow]✓ Disabled offline mode for all components[/yellow]")
|
|
261
|
+
console.print(f"[blue]Configuration saved[/blue]")
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
console.print(f"[red]Error: {str(e)}[/red]")
|
|
265
|
+
raise typer.Exit(1)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
app()
|
esgvoc/cli/status.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
from esgvoc.core import service
|
|
6
|
+
|
|
7
|
+
app = typer.Typer()
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def display(table):
|
|
12
|
+
console = Console(record=True, width=200)
|
|
13
|
+
console.print(table)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command()
|
|
17
|
+
def status():
|
|
18
|
+
"""
|
|
19
|
+
Command to display status
|
|
20
|
+
i.e summary of version of usable ressources (between remote/cached)
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
assert service.current_state is not None
|
|
24
|
+
service.current_state.get_state_summary()
|
|
25
|
+
|
|
26
|
+
# Check for offline mode components and display summary
|
|
27
|
+
offline_components = []
|
|
28
|
+
if service.current_state.universe.offline_mode:
|
|
29
|
+
offline_components.append("universe")
|
|
30
|
+
for project_name, project in service.current_state.projects.items():
|
|
31
|
+
if project.offline_mode:
|
|
32
|
+
offline_components.append(project_name)
|
|
33
|
+
|
|
34
|
+
if offline_components:
|
|
35
|
+
console.print(f"[yellow]Offline mode enabled for: {', '.join(offline_components)}[/yellow]")
|
|
36
|
+
|
|
37
|
+
table = Table(show_header=False, show_lines=True)
|
|
38
|
+
|
|
39
|
+
table.add_row("", "Remote github repo", "Local repository", "Cache Database", "Offline Mode", style="bright_green")
|
|
40
|
+
|
|
41
|
+
# Universe row
|
|
42
|
+
universe_offline_status = "✓" if service.current_state.universe.offline_mode else "✗"
|
|
43
|
+
table.add_row(
|
|
44
|
+
"Universe path",
|
|
45
|
+
service.current_state.universe.github_repo,
|
|
46
|
+
service.current_state.universe.local_path,
|
|
47
|
+
service.current_state.universe.db_path,
|
|
48
|
+
universe_offline_status,
|
|
49
|
+
style="white",
|
|
50
|
+
)
|
|
51
|
+
table.add_row(
|
|
52
|
+
"Version",
|
|
53
|
+
service.current_state.universe.github_version or "N/A",
|
|
54
|
+
service.current_state.universe.local_version or "N/A",
|
|
55
|
+
service.current_state.universe.db_version or "N/A",
|
|
56
|
+
"",
|
|
57
|
+
style="bright_blue",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Projects rows
|
|
61
|
+
for proj_name, proj in service.current_state.projects.items():
|
|
62
|
+
proj_offline_status = "✓" if proj.offline_mode else "✗"
|
|
63
|
+
table.add_row(
|
|
64
|
+
f"{proj_name} path",
|
|
65
|
+
proj.github_repo,
|
|
66
|
+
proj.local_path,
|
|
67
|
+
proj.db_path,
|
|
68
|
+
proj_offline_status,
|
|
69
|
+
style="white"
|
|
70
|
+
)
|
|
71
|
+
table.add_row(
|
|
72
|
+
"Version",
|
|
73
|
+
proj.github_version or "N/A",
|
|
74
|
+
proj.local_version or "N/A",
|
|
75
|
+
proj.db_version or "N/A",
|
|
76
|
+
"",
|
|
77
|
+
style="bright_blue"
|
|
78
|
+
)
|
|
79
|
+
display(table)
|
esgvoc/cli/test_cv.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test CV CLI commands
|
|
3
|
+
|
|
4
|
+
Provides commands for testing project CVs and Universe CVs integrated with esgvoc CLI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
|
|
13
|
+
from esgvoc.apps.test_cv.cv_tester import CVTester
|
|
14
|
+
from esgvoc.core.service.configuration.setting import ServiceSettings
|
|
15
|
+
|
|
16
|
+
app = typer.Typer()
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def list_projects():
|
|
22
|
+
"""List all available CV projects that can be tested."""
|
|
23
|
+
tester = CVTester()
|
|
24
|
+
projects = tester.get_available_projects()
|
|
25
|
+
|
|
26
|
+
table = Table(title="Available CV Projects for Testing")
|
|
27
|
+
table.add_column("Project Name", style="cyan")
|
|
28
|
+
table.add_column("Repository", style="green")
|
|
29
|
+
table.add_column("Default Branch", style="yellow")
|
|
30
|
+
table.add_column("Local Path", style="blue")
|
|
31
|
+
|
|
32
|
+
default_configs = ServiceSettings._get_default_project_configs()
|
|
33
|
+
for project_name in projects:
|
|
34
|
+
config = default_configs[project_name]
|
|
35
|
+
table.add_row(project_name, config["github_repo"], config["branch"], config["local_path"])
|
|
36
|
+
|
|
37
|
+
console.print(table)
|
|
38
|
+
console.print(f"\n[blue]Total: {len(projects)} projects available for testing[/blue]")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.command()
|
|
42
|
+
def configure(
|
|
43
|
+
project: str = typer.Argument(..., help="Project name to configure for testing"),
|
|
44
|
+
repo_url: Optional[str] = typer.Option(None, "--repo", "-r", help="Custom repository URL"),
|
|
45
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Custom branch to test"),
|
|
46
|
+
universe_branch: Optional[str] = typer.Option(None, "--universe-branch", "-u", help="Custom universe branch"),
|
|
47
|
+
sync: bool = typer.Option(True, "--sync/--no-sync", help="Synchronize CVs after configuration"),
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Configure esgvoc with a specific project for testing.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
esgvoc test configure obs4mip
|
|
54
|
+
esgvoc test configure cmip6 --branch my-test-branch
|
|
55
|
+
esgvoc test configure cmip6 --universe-branch my-universe-branch
|
|
56
|
+
esgvoc test configure custom --repo https://github.com/me/my-cvs --branch main --universe-branch dev
|
|
57
|
+
"""
|
|
58
|
+
tester = CVTester()
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
# Configure
|
|
62
|
+
if not tester.configure_for_testing(project, repo_url, branch, None, universe_branch):
|
|
63
|
+
raise typer.Exit(1)
|
|
64
|
+
|
|
65
|
+
# Optionally synchronize
|
|
66
|
+
if sync:
|
|
67
|
+
if not tester.synchronize_cvs():
|
|
68
|
+
raise typer.Exit(1)
|
|
69
|
+
|
|
70
|
+
console.print(f"[green]✅ Successfully configured project '{project}' for testing[/green]")
|
|
71
|
+
if not sync:
|
|
72
|
+
console.print("[yellow]Note: CVs not synchronized. Run 'esgvoc test sync' to download.[/yellow]")
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
console.print(f"[red]❌ Configuration failed: {e}[/red]")
|
|
76
|
+
raise typer.Exit(1)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.command()
|
|
80
|
+
def sync():
|
|
81
|
+
"""Synchronize/download CVs for the currently configured project."""
|
|
82
|
+
tester = CVTester()
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
if not tester.synchronize_cvs():
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
console.print("[green]✅ CVs synchronized successfully[/green]")
|
|
88
|
+
except Exception as e:
|
|
89
|
+
console.print(f"[red]❌ Synchronization failed: {e}[/red]")
|
|
90
|
+
raise typer.Exit(1)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@app.command()
|
|
94
|
+
def structure(
|
|
95
|
+
path: str = typer.Argument(".", help="Path to CV repository to validate"),
|
|
96
|
+
):
|
|
97
|
+
"""
|
|
98
|
+
Test CV repository structure and file format compliance.
|
|
99
|
+
|
|
100
|
+
Validates:
|
|
101
|
+
- Collection directory structure
|
|
102
|
+
- JSONLD context files
|
|
103
|
+
- Element JSON files
|
|
104
|
+
- project_specs.json references
|
|
105
|
+
|
|
106
|
+
Examples:
|
|
107
|
+
esgvoc test structure .
|
|
108
|
+
esgvoc test structure /path/to/cv/repo
|
|
109
|
+
"""
|
|
110
|
+
tester = CVTester()
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
if not tester.test_repository_structure(path):
|
|
114
|
+
raise typer.Exit(1)
|
|
115
|
+
console.print("[green]✅ Repository structure validation passed[/green]")
|
|
116
|
+
except Exception as e:
|
|
117
|
+
console.print(f"[red]❌ Structure validation failed: {e}[/red]")
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@app.command()
|
|
122
|
+
def api(
|
|
123
|
+
project: str = typer.Argument(..., help="Project name to test API access for"),
|
|
124
|
+
path: str = typer.Argument(".", help="Path to CV repository"),
|
|
125
|
+
debug_terms: bool = typer.Option(True, "--debug-terms/--no-debug-terms", help="Show detailed debugging info for missing terms"),
|
|
126
|
+
):
|
|
127
|
+
"""
|
|
128
|
+
Test esgvoc API access for all repository collections and elements.
|
|
129
|
+
|
|
130
|
+
Validates:
|
|
131
|
+
- Project is accessible via esgvoc API
|
|
132
|
+
- All repository collections are queryable
|
|
133
|
+
- All repository elements are accessible
|
|
134
|
+
- API functions work correctly
|
|
135
|
+
|
|
136
|
+
Examples:
|
|
137
|
+
esgvoc test api obs4mip .
|
|
138
|
+
esgvoc test api cmip6 /path/to/cmip6/repo
|
|
139
|
+
"""
|
|
140
|
+
tester = CVTester(debug_missing_terms=debug_terms)
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
if not tester.test_esgvoc_api_access(project, path):
|
|
144
|
+
raise typer.Exit(1)
|
|
145
|
+
console.print("[green]✅ ESGVoc API access validation passed[/green]")
|
|
146
|
+
except Exception as e:
|
|
147
|
+
console.print(f"[red]❌ API validation failed: {e}[/red]")
|
|
148
|
+
raise typer.Exit(1)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@app.command()
|
|
152
|
+
def run(
|
|
153
|
+
project: str = typer.Argument(..., help="Project name to test"),
|
|
154
|
+
path: Optional[str] = typer.Argument(None, help="Path to CV repository (auto-detected if not provided)"),
|
|
155
|
+
repo_url: Optional[str] = typer.Option(None, "--repo", "-r", help="Custom repository URL"),
|
|
156
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Custom branch to test"),
|
|
157
|
+
universe_branch: Optional[str] = typer.Option(None, "--universe-branch", "-u", help="Custom universe branch"),
|
|
158
|
+
debug_terms: bool = typer.Option(True, "--debug-terms/--no-debug-terms", help="Show detailed debugging info for missing terms"),
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
Run complete CV test suite: configure, sync, structure, and API tests.
|
|
162
|
+
|
|
163
|
+
This is the comprehensive test that runs all validation steps:
|
|
164
|
+
1. Configure esgvoc with the specified project
|
|
165
|
+
2. Synchronize/download CVs
|
|
166
|
+
3. Validate repository structure
|
|
167
|
+
4. Test esgvoc API access
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
esgvoc test run obs4mip
|
|
171
|
+
esgvoc test run cmip6 --branch my-test-branch
|
|
172
|
+
esgvoc test run cmip6 --universe-branch my-universe-branch
|
|
173
|
+
esgvoc test run cmip6 /path/to/custom/repo --branch my-test-branch --universe-branch dev
|
|
174
|
+
esgvoc test run custom --repo https://github.com/me/cvs --branch main --universe-branch main
|
|
175
|
+
"""
|
|
176
|
+
tester = CVTester(debug_missing_terms=debug_terms)
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
success = tester.run_complete_test(project, repo_url, branch, path, None, universe_branch)
|
|
180
|
+
if success:
|
|
181
|
+
console.print(f"[bold green]🎉 All tests passed for project '{project}'![/bold green]")
|
|
182
|
+
else:
|
|
183
|
+
# The detailed failure information is already printed by cv_tester
|
|
184
|
+
raise typer.Exit(1)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
console.print(f"[red]❌ Test suite failed: {e}[/red]")
|
|
187
|
+
raise typer.Exit(1)
|
|
188
|
+
finally:
|
|
189
|
+
tester.cleanup()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@app.command()
|
|
193
|
+
def env(
|
|
194
|
+
command: str = typer.Argument(..., help="Environment mode command: 'configure' or 'test'"),
|
|
195
|
+
project: Optional[str] = typer.Option(None, "--project", "-p", help="Project name (auto-detected if not provided)"),
|
|
196
|
+
repo_url: Optional[str] = typer.Option(
|
|
197
|
+
None, "--repo-url", help="Repository URL (from REPO_URL env var if not provided)"
|
|
198
|
+
),
|
|
199
|
+
branch: Optional[str] = typer.Option(None, "--branch", help="Branch (from TEST_BRANCH env var if not provided)"),
|
|
200
|
+
universe_branch: Optional[str] = typer.Option(None, "--universe-branch", help="Universe branch (from UNIVERSE_BRANCH env var if not provided)"),
|
|
201
|
+
debug_terms: bool = typer.Option(True, "--debug-terms/--no-debug-terms", help="Show detailed debugging info for missing terms"),
|
|
202
|
+
):
|
|
203
|
+
"""
|
|
204
|
+
Environment variable mode for CI/CD integration and automated testing.
|
|
205
|
+
|
|
206
|
+
Reads configuration from environment variables:
|
|
207
|
+
- REPO_URL: Repository URL to test
|
|
208
|
+
- TEST_BRANCH: Branch to test
|
|
209
|
+
- PROJECT_NAME: Project name (auto-detected if not set)
|
|
210
|
+
- UNIVERSE_BRANCH: Universe branch to test (optional)
|
|
211
|
+
- ESGVOC_LIBRARY_BRANCH: ESGVoc library branch (informational)
|
|
212
|
+
|
|
213
|
+
Examples:
|
|
214
|
+
# Set environment and run
|
|
215
|
+
export REPO_URL=https://github.com/me/obs4MIPs_CVs
|
|
216
|
+
export TEST_BRANCH=test-branch
|
|
217
|
+
export UNIVERSE_BRANCH=my-universe-branch
|
|
218
|
+
esgvoc test env configure
|
|
219
|
+
esgvoc test env test
|
|
220
|
+
|
|
221
|
+
# Or use options
|
|
222
|
+
esgvoc test env configure --project obs4mip --repo-url https://github.com/me/repo --branch main --universe-branch dev
|
|
223
|
+
"""
|
|
224
|
+
import os
|
|
225
|
+
|
|
226
|
+
# Get config from environment or options
|
|
227
|
+
final_repo_url = repo_url or os.environ.get("REPO_URL")
|
|
228
|
+
final_branch = branch or os.environ.get("TEST_BRANCH")
|
|
229
|
+
final_universe_branch = universe_branch or os.environ.get("UNIVERSE_BRANCH")
|
|
230
|
+
final_project = project or os.environ.get("PROJECT_NAME")
|
|
231
|
+
|
|
232
|
+
# Auto-detect project if not provided
|
|
233
|
+
if not final_project:
|
|
234
|
+
from esgvoc.apps.test_cv.cv_tester import detect_project_name
|
|
235
|
+
|
|
236
|
+
final_project = detect_project_name()
|
|
237
|
+
|
|
238
|
+
if command == "configure":
|
|
239
|
+
if not final_repo_url or not final_branch:
|
|
240
|
+
console.print("[red]❌ REPO_URL and TEST_BRANCH are required for env configure[/red]")
|
|
241
|
+
console.print("Set environment variables or use --repo-url and --branch options")
|
|
242
|
+
raise typer.Exit(1)
|
|
243
|
+
|
|
244
|
+
# Use configure command
|
|
245
|
+
configure(final_project, final_repo_url, final_branch, final_universe_branch, sync=True)
|
|
246
|
+
|
|
247
|
+
elif command == "test":
|
|
248
|
+
# Use run command
|
|
249
|
+
run(final_project, None, final_repo_url, final_branch, final_universe_branch, debug_terms)
|
|
250
|
+
|
|
251
|
+
else:
|
|
252
|
+
console.print(f"[red]❌ Invalid env command '{command}'. Use 'configure' or 'test'[/red]")
|
|
253
|
+
raise typer.Exit(1)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
if __name__ == "__main__":
|
|
257
|
+
app()
|
|
258
|
+
|