dsgrid-toolkit 0.3.3__cp313-cp313-win_amd64.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.
- build_backend.py +93 -0
- dsgrid/__init__.py +22 -0
- dsgrid/api/__init__.py +0 -0
- dsgrid/api/api_manager.py +179 -0
- dsgrid/api/app.py +419 -0
- dsgrid/api/models.py +60 -0
- dsgrid/api/response_models.py +116 -0
- dsgrid/apps/__init__.py +0 -0
- dsgrid/apps/project_viewer/app.py +216 -0
- dsgrid/apps/registration_gui.py +444 -0
- dsgrid/chronify.py +32 -0
- dsgrid/cli/__init__.py +0 -0
- dsgrid/cli/common.py +120 -0
- dsgrid/cli/config.py +176 -0
- dsgrid/cli/download.py +13 -0
- dsgrid/cli/dsgrid.py +157 -0
- dsgrid/cli/dsgrid_admin.py +92 -0
- dsgrid/cli/install_notebooks.py +62 -0
- dsgrid/cli/query.py +729 -0
- dsgrid/cli/registry.py +1862 -0
- dsgrid/cloud/__init__.py +0 -0
- dsgrid/cloud/cloud_storage_interface.py +140 -0
- dsgrid/cloud/factory.py +31 -0
- dsgrid/cloud/fake_storage_interface.py +37 -0
- dsgrid/cloud/s3_storage_interface.py +156 -0
- dsgrid/common.py +36 -0
- dsgrid/config/__init__.py +0 -0
- dsgrid/config/annual_time_dimension_config.py +194 -0
- dsgrid/config/common.py +142 -0
- dsgrid/config/config_base.py +148 -0
- dsgrid/config/dataset_config.py +907 -0
- dsgrid/config/dataset_schema_handler_factory.py +46 -0
- dsgrid/config/date_time_dimension_config.py +136 -0
- dsgrid/config/dimension_config.py +54 -0
- dsgrid/config/dimension_config_factory.py +65 -0
- dsgrid/config/dimension_mapping_base.py +350 -0
- dsgrid/config/dimension_mappings_config.py +48 -0
- dsgrid/config/dimensions.py +1025 -0
- dsgrid/config/dimensions_config.py +71 -0
- dsgrid/config/file_schema.py +190 -0
- dsgrid/config/index_time_dimension_config.py +80 -0
- dsgrid/config/input_dataset_requirements.py +31 -0
- dsgrid/config/mapping_tables.py +209 -0
- dsgrid/config/noop_time_dimension_config.py +42 -0
- dsgrid/config/project_config.py +1462 -0
- dsgrid/config/registration_models.py +188 -0
- dsgrid/config/representative_period_time_dimension_config.py +194 -0
- dsgrid/config/simple_models.py +49 -0
- dsgrid/config/supplemental_dimension.py +29 -0
- dsgrid/config/time_dimension_base_config.py +192 -0
- dsgrid/data_models.py +155 -0
- dsgrid/dataset/__init__.py +0 -0
- dsgrid/dataset/dataset.py +123 -0
- dsgrid/dataset/dataset_expression_handler.py +86 -0
- dsgrid/dataset/dataset_mapping_manager.py +121 -0
- dsgrid/dataset/dataset_schema_handler_base.py +945 -0
- dsgrid/dataset/dataset_schema_handler_one_table.py +209 -0
- dsgrid/dataset/dataset_schema_handler_two_table.py +322 -0
- dsgrid/dataset/growth_rates.py +162 -0
- dsgrid/dataset/models.py +51 -0
- dsgrid/dataset/table_format_handler_base.py +257 -0
- dsgrid/dataset/table_format_handler_factory.py +17 -0
- dsgrid/dataset/unpivoted_table.py +121 -0
- dsgrid/dimension/__init__.py +0 -0
- dsgrid/dimension/base_models.py +230 -0
- dsgrid/dimension/dimension_filters.py +308 -0
- dsgrid/dimension/standard.py +252 -0
- dsgrid/dimension/time.py +352 -0
- dsgrid/dimension/time_utils.py +103 -0
- dsgrid/dsgrid_rc.py +88 -0
- dsgrid/exceptions.py +105 -0
- dsgrid/filesystem/__init__.py +0 -0
- dsgrid/filesystem/cloud_filesystem.py +32 -0
- dsgrid/filesystem/factory.py +32 -0
- dsgrid/filesystem/filesystem_interface.py +136 -0
- dsgrid/filesystem/local_filesystem.py +74 -0
- dsgrid/filesystem/s3_filesystem.py +118 -0
- dsgrid/loggers.py +132 -0
- dsgrid/minimal_patterns.cp313-win_amd64.pyd +0 -0
- dsgrid/notebooks/connect_to_dsgrid_registry.ipynb +949 -0
- dsgrid/notebooks/registration.ipynb +48 -0
- dsgrid/notebooks/start_notebook.sh +11 -0
- dsgrid/project.py +451 -0
- dsgrid/query/__init__.py +0 -0
- dsgrid/query/dataset_mapping_plan.py +142 -0
- dsgrid/query/derived_dataset.py +388 -0
- dsgrid/query/models.py +728 -0
- dsgrid/query/query_context.py +287 -0
- dsgrid/query/query_submitter.py +994 -0
- dsgrid/query/report_factory.py +19 -0
- dsgrid/query/report_peak_load.py +70 -0
- dsgrid/query/reports_base.py +20 -0
- dsgrid/registry/__init__.py +0 -0
- dsgrid/registry/bulk_register.py +165 -0
- dsgrid/registry/common.py +287 -0
- dsgrid/registry/config_update_checker_base.py +63 -0
- dsgrid/registry/data_store_factory.py +34 -0
- dsgrid/registry/data_store_interface.py +74 -0
- dsgrid/registry/dataset_config_generator.py +158 -0
- dsgrid/registry/dataset_registry_manager.py +950 -0
- dsgrid/registry/dataset_update_checker.py +16 -0
- dsgrid/registry/dimension_mapping_registry_manager.py +575 -0
- dsgrid/registry/dimension_mapping_update_checker.py +16 -0
- dsgrid/registry/dimension_registry_manager.py +413 -0
- dsgrid/registry/dimension_update_checker.py +16 -0
- dsgrid/registry/duckdb_data_store.py +207 -0
- dsgrid/registry/filesystem_data_store.py +150 -0
- dsgrid/registry/filter_registry_manager.py +123 -0
- dsgrid/registry/project_config_generator.py +57 -0
- dsgrid/registry/project_registry_manager.py +1623 -0
- dsgrid/registry/project_update_checker.py +48 -0
- dsgrid/registry/registration_context.py +223 -0
- dsgrid/registry/registry_auto_updater.py +316 -0
- dsgrid/registry/registry_database.py +667 -0
- dsgrid/registry/registry_interface.py +446 -0
- dsgrid/registry/registry_manager.py +558 -0
- dsgrid/registry/registry_manager_base.py +367 -0
- dsgrid/registry/versioning.py +92 -0
- dsgrid/rust_ext/__init__.py +14 -0
- dsgrid/rust_ext/find_minimal_patterns.py +129 -0
- dsgrid/spark/__init__.py +0 -0
- dsgrid/spark/functions.py +589 -0
- dsgrid/spark/types.py +110 -0
- dsgrid/tests/__init__.py +0 -0
- dsgrid/tests/common.py +140 -0
- dsgrid/tests/make_us_data_registry.py +265 -0
- dsgrid/tests/register_derived_datasets.py +103 -0
- dsgrid/tests/utils.py +25 -0
- dsgrid/time/__init__.py +0 -0
- dsgrid/time/time_conversions.py +80 -0
- dsgrid/time/types.py +67 -0
- dsgrid/units/__init__.py +0 -0
- dsgrid/units/constants.py +113 -0
- dsgrid/units/convert.py +71 -0
- dsgrid/units/energy.py +145 -0
- dsgrid/units/power.py +87 -0
- dsgrid/utils/__init__.py +0 -0
- dsgrid/utils/dataset.py +830 -0
- dsgrid/utils/files.py +179 -0
- dsgrid/utils/filters.py +125 -0
- dsgrid/utils/id_remappings.py +100 -0
- dsgrid/utils/py_expression_eval/LICENSE +19 -0
- dsgrid/utils/py_expression_eval/README.md +8 -0
- dsgrid/utils/py_expression_eval/__init__.py +847 -0
- dsgrid/utils/py_expression_eval/tests.py +283 -0
- dsgrid/utils/run_command.py +70 -0
- dsgrid/utils/scratch_dir_context.py +65 -0
- dsgrid/utils/spark.py +918 -0
- dsgrid/utils/spark_partition.py +98 -0
- dsgrid/utils/timing.py +239 -0
- dsgrid/utils/utilities.py +221 -0
- dsgrid/utils/versioning.py +36 -0
- dsgrid_toolkit-0.3.3.dist-info/METADATA +193 -0
- dsgrid_toolkit-0.3.3.dist-info/RECORD +157 -0
- dsgrid_toolkit-0.3.3.dist-info/WHEEL +4 -0
- dsgrid_toolkit-0.3.3.dist-info/entry_points.txt +4 -0
- dsgrid_toolkit-0.3.3.dist-info/licenses/LICENSE +29 -0
dsgrid/cli/registry.py
ADDED
|
@@ -0,0 +1,1862 @@
|
|
|
1
|
+
"""Manages a dsgrid registry."""
|
|
2
|
+
|
|
3
|
+
import getpass
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import rich_click as click
|
|
9
|
+
from rich import print
|
|
10
|
+
from semver import VersionInfo
|
|
11
|
+
|
|
12
|
+
from dsgrid.cli.common import (
|
|
13
|
+
get_value_from_context,
|
|
14
|
+
handle_dsgrid_exception,
|
|
15
|
+
path_callback,
|
|
16
|
+
)
|
|
17
|
+
from dsgrid.common import REMOTE_REGISTRY
|
|
18
|
+
from dsgrid.dataset.models import TableFormat
|
|
19
|
+
from dsgrid.dimension.base_models import DimensionType
|
|
20
|
+
from dsgrid.dimension.time import TimeDimensionType
|
|
21
|
+
from dsgrid.config.common import SUPPORTED_METRIC_TYPES
|
|
22
|
+
from dsgrid.config.project_config import ProjectConfig
|
|
23
|
+
from dsgrid.registry.bulk_register import bulk_register
|
|
24
|
+
from dsgrid.registry.common import (
|
|
25
|
+
DatabaseConnection,
|
|
26
|
+
DatasetRegistryStatus,
|
|
27
|
+
VersionUpdateType,
|
|
28
|
+
)
|
|
29
|
+
from dsgrid.registry.dataset_config_generator import generate_config_from_dataset
|
|
30
|
+
from dsgrid.registry.registry_manager import RegistryManager
|
|
31
|
+
from dsgrid.registry.project_config_generator import generate_project_config
|
|
32
|
+
from dsgrid.utils.filters import ACCEPTED_OPS
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _version_info_callback(*args) -> VersionInfo | None:
|
|
39
|
+
val = args[2]
|
|
40
|
+
if val is None:
|
|
41
|
+
return val
|
|
42
|
+
return VersionInfo.parse(val)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _version_info_required_callback(*args) -> VersionInfo:
|
|
46
|
+
val = args[2]
|
|
47
|
+
return VersionInfo.parse(val)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _version_update_callback(*args) -> VersionUpdateType:
|
|
51
|
+
val = args[2]
|
|
52
|
+
return VersionUpdateType(val)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
Click Group Definitions
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@click.group()
|
|
61
|
+
@click.pass_context
|
|
62
|
+
def registry(ctx):
|
|
63
|
+
"""Manage a registry."""
|
|
64
|
+
conn = DatabaseConnection(
|
|
65
|
+
url=get_value_from_context(ctx, "url"),
|
|
66
|
+
)
|
|
67
|
+
scratch_dir = get_value_from_context(ctx, "scratch_dir")
|
|
68
|
+
no_prompts = ctx.parent.params["no_prompts"]
|
|
69
|
+
if "--help" in sys.argv:
|
|
70
|
+
ctx.obj = None
|
|
71
|
+
else:
|
|
72
|
+
ctx.obj = RegistryManager.load(
|
|
73
|
+
conn,
|
|
74
|
+
REMOTE_REGISTRY,
|
|
75
|
+
offline_mode=True,
|
|
76
|
+
no_prompts=no_prompts,
|
|
77
|
+
scratch_dir=scratch_dir,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@registry.result_callback()
|
|
82
|
+
@click.pass_context
|
|
83
|
+
def cleanup_registry_manager(ctx, result, **kwargs):
|
|
84
|
+
"""Cleanup the registry manager after the command finishes."""
|
|
85
|
+
if ctx.obj is not None:
|
|
86
|
+
ctx.obj.dispose()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@click.group()
|
|
90
|
+
@click.pass_obj
|
|
91
|
+
def dimensions(registry_manager: RegistryManager):
|
|
92
|
+
"""Dimension subcommands"""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@click.group()
|
|
96
|
+
@click.pass_obj
|
|
97
|
+
def dimension_mappings(registry_manager: RegistryManager):
|
|
98
|
+
"""Dimension mapping subcommands"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@click.group()
|
|
102
|
+
@click.pass_obj
|
|
103
|
+
def projects(registry_manager: RegistryManager):
|
|
104
|
+
"""Project subcommands"""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@click.group()
|
|
108
|
+
@click.pass_obj
|
|
109
|
+
def datasets(registry_manager: RegistryManager):
|
|
110
|
+
"""Dataset subcommands"""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
Registry Commands
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# TODO: Support registry file reads without syncing using something like sfs3
|
|
119
|
+
@click.command(name="list")
|
|
120
|
+
@click.pass_obj
|
|
121
|
+
def list_(registry_manager):
|
|
122
|
+
"""List the contents of a registry."""
|
|
123
|
+
print(f"Registry: {registry_manager.path}")
|
|
124
|
+
registry_manager.show()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
Dimension Commands
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
_list_dimensions_epilog = """
|
|
133
|
+
Examples:\n
|
|
134
|
+
$ dsgrid registry dimensions list\n
|
|
135
|
+
$ dsgrid registry dimensions list -f "Type == sector"\n
|
|
136
|
+
$ dsgrid registry dimensions list -f "Submitter == username"\n
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@click.command(name="list", epilog=_list_dimensions_epilog)
|
|
141
|
+
@click.option(
|
|
142
|
+
"-f",
|
|
143
|
+
"--filter",
|
|
144
|
+
multiple=True,
|
|
145
|
+
type=str,
|
|
146
|
+
help=f"""
|
|
147
|
+
Filter table with a case-insensitive expression in the format 'column operation value',
|
|
148
|
+
accepts multiple flags\b\n
|
|
149
|
+
valid operations: {ACCEPTED_OPS}\n
|
|
150
|
+
""",
|
|
151
|
+
)
|
|
152
|
+
@click.pass_obj
|
|
153
|
+
def list_dimensions(registry_manager, filter):
|
|
154
|
+
"""List the registered dimensions."""
|
|
155
|
+
registry_manager.dimension_manager.show(filters=filter)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
_register_dimensions_epilog = """
|
|
159
|
+
Examples:\n
|
|
160
|
+
$ dsgrid registry dimensions register -l "Register dimensions for my-project" dimensions.json5\n
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@click.command(name="register", epilog=_register_dimensions_epilog)
|
|
165
|
+
@click.argument("dimension-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
166
|
+
@click.option(
|
|
167
|
+
"-l",
|
|
168
|
+
"--log-message",
|
|
169
|
+
required=True,
|
|
170
|
+
help="reason for submission",
|
|
171
|
+
)
|
|
172
|
+
@click.pass_obj
|
|
173
|
+
@click.pass_context
|
|
174
|
+
def register_dimensions(
|
|
175
|
+
ctx,
|
|
176
|
+
registry_manager: RegistryManager,
|
|
177
|
+
dimension_config_file: Path,
|
|
178
|
+
log_message: str,
|
|
179
|
+
):
|
|
180
|
+
"""Register new dimensions with the dsgrid repository. The contents of the JSON/JSON5 file
|
|
181
|
+
must match the data model defined by this documentation:
|
|
182
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/dimension.html#dsgrid.config.dimensions.DimensionsConfigModel
|
|
183
|
+
"""
|
|
184
|
+
manager = registry_manager.dimension_manager
|
|
185
|
+
submitter = getpass.getuser()
|
|
186
|
+
res = handle_dsgrid_exception(
|
|
187
|
+
ctx, manager.register, dimension_config_file, submitter, log_message
|
|
188
|
+
)
|
|
189
|
+
if res[1] != 0:
|
|
190
|
+
ctx.exit(res[1])
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
_dump_dimension_epilog = """
|
|
194
|
+
Examples:\n
|
|
195
|
+
$ dsgrid registry dimensions dump 8c575746-18fa-4b65-bf1f-516079d634a5\n
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@click.command(name="dump", epilog=_dump_dimension_epilog)
|
|
200
|
+
@click.argument("dimension-id")
|
|
201
|
+
@click.option(
|
|
202
|
+
"-v",
|
|
203
|
+
"--version",
|
|
204
|
+
callback=_version_info_callback,
|
|
205
|
+
help="Version to dump; defaults to latest",
|
|
206
|
+
)
|
|
207
|
+
@click.option(
|
|
208
|
+
"-d",
|
|
209
|
+
"--directory",
|
|
210
|
+
default=".",
|
|
211
|
+
type=click.Path(exists=True),
|
|
212
|
+
help="Directory in which to create config and data files",
|
|
213
|
+
)
|
|
214
|
+
@click.option(
|
|
215
|
+
"--force",
|
|
216
|
+
is_flag=True,
|
|
217
|
+
default=False,
|
|
218
|
+
show_default=True,
|
|
219
|
+
help="Overwrite files if they exist.",
|
|
220
|
+
)
|
|
221
|
+
@click.pass_obj
|
|
222
|
+
@click.pass_context
|
|
223
|
+
def dump_dimension(ctx, registry_manager, dimension_id, version, directory, force):
|
|
224
|
+
"""Dump a dimension config file (and any related data) from the registry."""
|
|
225
|
+
manager = registry_manager.dimension_manager
|
|
226
|
+
res = handle_dsgrid_exception(
|
|
227
|
+
ctx, manager.dump, dimension_id, Path(directory), version=version, force=force
|
|
228
|
+
)
|
|
229
|
+
if res[1] != 0:
|
|
230
|
+
ctx.exit(res[1])
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
_show_dimension_epilog = """
|
|
234
|
+
Examples:\n
|
|
235
|
+
$ dsgrid registry dimensions show 8c575746-18fa-4b65-bf1f-516079d634a5
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@click.command(name="show", epilog=_show_dimension_epilog)
|
|
240
|
+
@click.argument("dimension-id", type=str)
|
|
241
|
+
@click.option(
|
|
242
|
+
"-v",
|
|
243
|
+
"--version",
|
|
244
|
+
default="1.0.0",
|
|
245
|
+
show_default=True,
|
|
246
|
+
callback=_version_info_required_callback,
|
|
247
|
+
help="Version to show.",
|
|
248
|
+
)
|
|
249
|
+
@click.pass_obj
|
|
250
|
+
@click.pass_context
|
|
251
|
+
def show_dimension(
|
|
252
|
+
ctx,
|
|
253
|
+
registry_manager: RegistryManager,
|
|
254
|
+
dimension_id: str,
|
|
255
|
+
version: str,
|
|
256
|
+
):
|
|
257
|
+
"""Show an existing dimension in the registry."""
|
|
258
|
+
manager = registry_manager.dimension_manager
|
|
259
|
+
res = handle_dsgrid_exception(
|
|
260
|
+
ctx,
|
|
261
|
+
manager.get_by_id,
|
|
262
|
+
dimension_id,
|
|
263
|
+
version,
|
|
264
|
+
)
|
|
265
|
+
if res[1] != 0:
|
|
266
|
+
ctx.exit(res[1])
|
|
267
|
+
dim = res[0]
|
|
268
|
+
print(
|
|
269
|
+
f"""id={dim.model.dimension_id}
|
|
270
|
+
type={dim.model.dimension_type.value}
|
|
271
|
+
name={dim.model.name}
|
|
272
|
+
description={dim.model.description}
|
|
273
|
+
"""
|
|
274
|
+
)
|
|
275
|
+
if dim.model.dimension_type != DimensionType.TIME:
|
|
276
|
+
records = dim.get_records_dataframe()
|
|
277
|
+
records.show(n=4000)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
_update_dimension_epilog = """
|
|
281
|
+
Examples:\n
|
|
282
|
+
$ dsgrid registry dimensions update -d 8c575746-18fa-4b65-bf1f-516079d634a5 -l "Update county dimension" -u major -v 1.0.0 dimension.json5\n
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@click.command(name="update", epilog=_update_dimension_epilog)
|
|
287
|
+
@click.argument("dimension-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
288
|
+
@click.option(
|
|
289
|
+
"-d",
|
|
290
|
+
"--dimension-id",
|
|
291
|
+
required=True,
|
|
292
|
+
type=str,
|
|
293
|
+
help="dimension ID",
|
|
294
|
+
)
|
|
295
|
+
@click.option(
|
|
296
|
+
"-l",
|
|
297
|
+
"--log-message",
|
|
298
|
+
required=True,
|
|
299
|
+
type=str,
|
|
300
|
+
help="reason for submission",
|
|
301
|
+
)
|
|
302
|
+
@click.option(
|
|
303
|
+
"-t",
|
|
304
|
+
"--update-type",
|
|
305
|
+
required=True,
|
|
306
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
307
|
+
callback=_version_update_callback,
|
|
308
|
+
)
|
|
309
|
+
@click.option(
|
|
310
|
+
"-v",
|
|
311
|
+
"--version",
|
|
312
|
+
required=True,
|
|
313
|
+
callback=_version_info_required_callback,
|
|
314
|
+
help="Version to update; must be the current version.",
|
|
315
|
+
)
|
|
316
|
+
@click.pass_obj
|
|
317
|
+
@click.pass_context
|
|
318
|
+
def update_dimension(
|
|
319
|
+
ctx,
|
|
320
|
+
registry_manager: RegistryManager,
|
|
321
|
+
dimension_config_file: Path,
|
|
322
|
+
dimension_id: str,
|
|
323
|
+
log_message: str,
|
|
324
|
+
update_type: VersionUpdateType,
|
|
325
|
+
version: str,
|
|
326
|
+
):
|
|
327
|
+
"""Update an existing dimension in the registry."""
|
|
328
|
+
manager = registry_manager.dimension_manager
|
|
329
|
+
submitter = getpass.getuser()
|
|
330
|
+
res = handle_dsgrid_exception(
|
|
331
|
+
ctx,
|
|
332
|
+
manager.update_from_file,
|
|
333
|
+
dimension_config_file,
|
|
334
|
+
dimension_id,
|
|
335
|
+
submitter,
|
|
336
|
+
update_type,
|
|
337
|
+
log_message,
|
|
338
|
+
version,
|
|
339
|
+
)
|
|
340
|
+
if res[1] != 0:
|
|
341
|
+
ctx.exit(res[1])
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@click.command(name="remove")
|
|
345
|
+
@click.argument("dimension-id")
|
|
346
|
+
@click.pass_obj
|
|
347
|
+
def remove_dimension(registry_manager: RegistryManager, dimension_id: str):
|
|
348
|
+
"""Remove a dimension from the dsgrid repository."""
|
|
349
|
+
registry_manager.dimension_manager.remove(dimension_id)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
"""
|
|
353
|
+
Dimension Mapping Commands
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
_list_dimension_mappings_epilog = """
|
|
358
|
+
Examples:\n
|
|
359
|
+
$ dsgrid registry dimension-mappings list\n
|
|
360
|
+
$ dsgrid registry dimension-mappings list -f "Type [From, To] contains geography" -f "Submitter == username"\n
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
@click.command(name="list", epilog=_list_dimension_mappings_epilog)
|
|
365
|
+
@click.option(
|
|
366
|
+
"-f",
|
|
367
|
+
"--filter",
|
|
368
|
+
multiple=True,
|
|
369
|
+
type=str,
|
|
370
|
+
help=f"""
|
|
371
|
+
Filter table with a case-insensitive expression in the format 'column operation value',
|
|
372
|
+
accepts multiple flags\b\n
|
|
373
|
+
valid operations: {ACCEPTED_OPS}\n
|
|
374
|
+
""",
|
|
375
|
+
)
|
|
376
|
+
@click.pass_obj
|
|
377
|
+
def list_dimension_mappings(registry_manager, filter):
|
|
378
|
+
"""List the registered dimension mappings."""
|
|
379
|
+
registry_manager.dimension_mapping_manager.show(filters=filter)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
_register_dimension_mappings_epilog = """
|
|
383
|
+
Examples:\\
|
|
384
|
+
$ dsgrid registry dimension-mappings register -l "Register dimension mappings for my-project" dimension_mappings.json5\n
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@click.command(name="register", epilog=_register_dimension_mappings_epilog)
|
|
389
|
+
@click.argument(
|
|
390
|
+
"dimension-mapping-config-file",
|
|
391
|
+
type=click.Path(exists=True),
|
|
392
|
+
callback=path_callback,
|
|
393
|
+
)
|
|
394
|
+
@click.option(
|
|
395
|
+
"-l",
|
|
396
|
+
"--log-message",
|
|
397
|
+
required=True,
|
|
398
|
+
help="reason for submission",
|
|
399
|
+
)
|
|
400
|
+
@click.pass_obj
|
|
401
|
+
@click.pass_context
|
|
402
|
+
def register_dimension_mappings(
|
|
403
|
+
ctx,
|
|
404
|
+
registry_manager: RegistryManager,
|
|
405
|
+
dimension_mapping_config_file: Path,
|
|
406
|
+
log_message: str,
|
|
407
|
+
):
|
|
408
|
+
"""Register new dimension mappings with the dsgrid repository. The contents of the JSON/JSON5
|
|
409
|
+
file must match the data model defined by this documentation:
|
|
410
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.dimension_mappings_config.DimensionMappingsConfigModel
|
|
411
|
+
"""
|
|
412
|
+
submitter = getpass.getuser()
|
|
413
|
+
manager = registry_manager.dimension_mapping_manager
|
|
414
|
+
res = handle_dsgrid_exception(
|
|
415
|
+
ctx, manager.register, dimension_mapping_config_file, submitter, log_message
|
|
416
|
+
)
|
|
417
|
+
if res[1] != 0:
|
|
418
|
+
ctx.exit(res[1])
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
_dump_dimension_mapping_epilog = """
|
|
422
|
+
Examples:\n
|
|
423
|
+
$ dsgrid registry dimension-mappings dump 8c575746-18fa-4b65-bf1f-516079d634a5\n
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
@click.command(name="dump", epilog=_dump_dimension_mapping_epilog)
|
|
428
|
+
@click.argument("dimension-mapping-id")
|
|
429
|
+
@click.option(
|
|
430
|
+
"-v",
|
|
431
|
+
"--version",
|
|
432
|
+
callback=_version_info_callback,
|
|
433
|
+
help="Version to dump; defaults to latest",
|
|
434
|
+
)
|
|
435
|
+
@click.option(
|
|
436
|
+
"-d",
|
|
437
|
+
"--directory",
|
|
438
|
+
default=".",
|
|
439
|
+
type=click.Path(exists=True),
|
|
440
|
+
help="Directory in which to create config and data files",
|
|
441
|
+
)
|
|
442
|
+
@click.option(
|
|
443
|
+
"--force",
|
|
444
|
+
is_flag=True,
|
|
445
|
+
default=False,
|
|
446
|
+
show_default=True,
|
|
447
|
+
help="Overwrite files if they exist.",
|
|
448
|
+
)
|
|
449
|
+
@click.pass_obj
|
|
450
|
+
@click.pass_context
|
|
451
|
+
def dump_dimension_mapping(
|
|
452
|
+
ctx,
|
|
453
|
+
registry_manager: RegistryManager,
|
|
454
|
+
dimension_mapping_id: str,
|
|
455
|
+
version: str,
|
|
456
|
+
directory: Path,
|
|
457
|
+
force: bool,
|
|
458
|
+
):
|
|
459
|
+
"""Dump a dimension mapping config file (and any related data) from the registry."""
|
|
460
|
+
manager = registry_manager.dimension_mapping_manager
|
|
461
|
+
res = handle_dsgrid_exception(
|
|
462
|
+
ctx,
|
|
463
|
+
manager.dump,
|
|
464
|
+
dimension_mapping_id,
|
|
465
|
+
Path(directory),
|
|
466
|
+
version=version,
|
|
467
|
+
force=force,
|
|
468
|
+
)
|
|
469
|
+
if res[1] != 0:
|
|
470
|
+
ctx.exit(res[1])
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
_show_dimension_mapping_epilog = """
|
|
474
|
+
Examples:\n
|
|
475
|
+
$ dsgrid registry dimension-mappings show 8c575746-18fa-4b65-bf1f-516079d634a5
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
@click.command(name="show", epilog=_show_dimension_mapping_epilog)
|
|
480
|
+
@click.argument("mapping-id", type=str)
|
|
481
|
+
@click.option(
|
|
482
|
+
"-v",
|
|
483
|
+
"--version",
|
|
484
|
+
default="1.0.0",
|
|
485
|
+
show_default=True,
|
|
486
|
+
callback=_version_info_required_callback,
|
|
487
|
+
help="Version to show.",
|
|
488
|
+
)
|
|
489
|
+
@click.pass_obj
|
|
490
|
+
@click.pass_context
|
|
491
|
+
def show_dimension_mapping(
|
|
492
|
+
ctx,
|
|
493
|
+
registry_manager: RegistryManager,
|
|
494
|
+
mapping_id: str,
|
|
495
|
+
version: str,
|
|
496
|
+
):
|
|
497
|
+
"""Show an existing dimension mapping in the registry."""
|
|
498
|
+
manager = registry_manager.dimension_mapping_manager
|
|
499
|
+
res = handle_dsgrid_exception(
|
|
500
|
+
ctx,
|
|
501
|
+
manager.get_by_id,
|
|
502
|
+
mapping_id,
|
|
503
|
+
version,
|
|
504
|
+
)
|
|
505
|
+
if res[1] != 0:
|
|
506
|
+
ctx.exit(res[1])
|
|
507
|
+
mapping = res[0]
|
|
508
|
+
from_dim = registry_manager.dimension_manager.get_by_id(
|
|
509
|
+
mapping.model.from_dimension.dimension_id
|
|
510
|
+
)
|
|
511
|
+
to_dim = registry_manager.dimension_manager.get_by_id(mapping.model.to_dimension.dimension_id)
|
|
512
|
+
print(
|
|
513
|
+
f"""
|
|
514
|
+
type={mapping.model.from_dimension.dimension_type}
|
|
515
|
+
from_id={mapping.model.from_dimension.dimension_id}
|
|
516
|
+
from_name={from_dim.model.name}
|
|
517
|
+
from_description={from_dim.model.description}
|
|
518
|
+
to_id={mapping.model.to_dimension.dimension_id}
|
|
519
|
+
to_name={to_dim.model.name}
|
|
520
|
+
to_description={to_dim.model.description}
|
|
521
|
+
"""
|
|
522
|
+
)
|
|
523
|
+
records = mapping.get_records_dataframe()
|
|
524
|
+
records.show(n=4000)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
_update_dimension_mapping_epilog = """
|
|
528
|
+
Examples:\n
|
|
529
|
+
$ dsgrid registry dimension-mappings update \\ \n
|
|
530
|
+
-d 8c575746-18fa-4b65-bf1f-516079d634a5 \\ \n
|
|
531
|
+
-l "Swap out the state to county mapping for my-dataset to that-project" \\ \n
|
|
532
|
+
-u major \\ \n
|
|
533
|
+
-v 1.0.0 dimension_mappings.json5"
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
@click.command(name="update", epilog=_update_dimension_mapping_epilog)
|
|
538
|
+
@click.argument(
|
|
539
|
+
"dimension-mapping-config-file",
|
|
540
|
+
type=click.Path(exists=True),
|
|
541
|
+
callback=path_callback,
|
|
542
|
+
)
|
|
543
|
+
@click.option(
|
|
544
|
+
"-d",
|
|
545
|
+
"--dimension-mapping-id",
|
|
546
|
+
required=True,
|
|
547
|
+
type=str,
|
|
548
|
+
help="dimension mapping ID",
|
|
549
|
+
)
|
|
550
|
+
@click.option(
|
|
551
|
+
"-l",
|
|
552
|
+
"--log-message",
|
|
553
|
+
required=True,
|
|
554
|
+
type=str,
|
|
555
|
+
help="reason for submission",
|
|
556
|
+
)
|
|
557
|
+
@click.option(
|
|
558
|
+
"-t",
|
|
559
|
+
"--update-type",
|
|
560
|
+
required=True,
|
|
561
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
562
|
+
callback=_version_update_callback,
|
|
563
|
+
)
|
|
564
|
+
@click.option(
|
|
565
|
+
"-v",
|
|
566
|
+
"--version",
|
|
567
|
+
required=True,
|
|
568
|
+
callback=_version_info_required_callback,
|
|
569
|
+
help="Version to update; must be the current version.",
|
|
570
|
+
)
|
|
571
|
+
@click.pass_obj
|
|
572
|
+
def update_dimension_mapping(
|
|
573
|
+
registry_manager: RegistryManager,
|
|
574
|
+
dimension_mapping_config_file: Path,
|
|
575
|
+
dimension_mapping_id: str,
|
|
576
|
+
log_message: str,
|
|
577
|
+
update_type: VersionUpdateType,
|
|
578
|
+
version: str,
|
|
579
|
+
):
|
|
580
|
+
"""Update an existing dimension mapping registry. The contents of the JSON/JSON5 file must
|
|
581
|
+
match the data model defined by this documentation:
|
|
582
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.mapping_tables.MappingTableModel
|
|
583
|
+
"""
|
|
584
|
+
manager = registry_manager.dimension_mapping_manager
|
|
585
|
+
submitter = getpass.getuser()
|
|
586
|
+
manager.update_from_file(
|
|
587
|
+
dimension_mapping_config_file,
|
|
588
|
+
dimension_mapping_id,
|
|
589
|
+
submitter,
|
|
590
|
+
update_type,
|
|
591
|
+
log_message,
|
|
592
|
+
version,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@click.command(name="remove")
|
|
597
|
+
@click.argument("dimension-mapping-id")
|
|
598
|
+
@click.pass_obj
|
|
599
|
+
def remove_dimension_mapping(registry_manager: RegistryManager, dimension_mapping_id: str):
|
|
600
|
+
"""Remove a dimension mapping from the dsgrid repository."""
|
|
601
|
+
registry_manager.dimension_mapping_manager.remove(dimension_mapping_id)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
"""
|
|
605
|
+
Project Commands
|
|
606
|
+
"""
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
_list_projects_epilog = """
|
|
610
|
+
Examples:\n
|
|
611
|
+
$ dsgrid registry projects list\n
|
|
612
|
+
$ dsgrid registry projects list -f "ID contains efs"\n
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
@click.command(name="list", epilog=_list_projects_epilog)
|
|
617
|
+
@click.option(
|
|
618
|
+
"-f",
|
|
619
|
+
"--filter",
|
|
620
|
+
multiple=True,
|
|
621
|
+
type=str,
|
|
622
|
+
help=f"""
|
|
623
|
+
Filter table with a case-insensitive expression in the format 'column operation value',
|
|
624
|
+
accepts multiple flags\b\n
|
|
625
|
+
valid operations: {ACCEPTED_OPS}\n
|
|
626
|
+
""",
|
|
627
|
+
)
|
|
628
|
+
@click.pass_obj
|
|
629
|
+
@click.pass_context
|
|
630
|
+
def list_projects(ctx, registry_manager, filter):
|
|
631
|
+
"""List the registered projects."""
|
|
632
|
+
res = handle_dsgrid_exception(ctx, registry_manager.project_manager.show, filters=filter)
|
|
633
|
+
if res[1] != 0:
|
|
634
|
+
ctx.exit(res[1])
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
_register_project_epilog = """
|
|
638
|
+
Examples:\n
|
|
639
|
+
$ dsgrid registry projects register -l "Register project my-project" project.json5\n
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
@click.command(name="register", epilog=_register_project_epilog)
|
|
644
|
+
@click.argument("project-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
645
|
+
@click.option(
|
|
646
|
+
"-l",
|
|
647
|
+
"--log-message",
|
|
648
|
+
required=True,
|
|
649
|
+
help="reason for submission",
|
|
650
|
+
)
|
|
651
|
+
@click.pass_obj
|
|
652
|
+
@click.pass_context
|
|
653
|
+
def register_project(
|
|
654
|
+
ctx,
|
|
655
|
+
registry_manager,
|
|
656
|
+
project_config_file,
|
|
657
|
+
log_message,
|
|
658
|
+
):
|
|
659
|
+
"""Register a new project with the dsgrid repository. The contents of the JSON/JSON5 file must
|
|
660
|
+
match the data model defined by this documentation:
|
|
661
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.project_config.ProjectConfigModel
|
|
662
|
+
"""
|
|
663
|
+
submitter = getpass.getuser()
|
|
664
|
+
res = handle_dsgrid_exception(
|
|
665
|
+
ctx,
|
|
666
|
+
registry_manager.project_manager.register,
|
|
667
|
+
project_config_file,
|
|
668
|
+
submitter,
|
|
669
|
+
log_message,
|
|
670
|
+
)
|
|
671
|
+
if res[1] != 0:
|
|
672
|
+
ctx.exit(res[1])
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
_submit_dataset_epilog = """
|
|
676
|
+
Examples:\n
|
|
677
|
+
$ dsgrid registry projects submit-dataset \\ \n
|
|
678
|
+
-p my-project-id \\ \n
|
|
679
|
+
-d my-dataset-id \\ \n
|
|
680
|
+
-m dimension_mappings.json5 \\ \n
|
|
681
|
+
-l "Submit dataset my-dataset to project my-project."\n
|
|
682
|
+
"""
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
@click.command(epilog=_submit_dataset_epilog)
|
|
686
|
+
@click.option(
|
|
687
|
+
"-d",
|
|
688
|
+
"--dataset-id",
|
|
689
|
+
required=True,
|
|
690
|
+
type=str,
|
|
691
|
+
help="dataset identifier",
|
|
692
|
+
)
|
|
693
|
+
@click.option(
|
|
694
|
+
"-p",
|
|
695
|
+
"--project-id",
|
|
696
|
+
required=True,
|
|
697
|
+
type=str,
|
|
698
|
+
help="project identifier",
|
|
699
|
+
)
|
|
700
|
+
@click.option(
|
|
701
|
+
"-m",
|
|
702
|
+
"--dimension-mapping-file",
|
|
703
|
+
type=click.Path(exists=True),
|
|
704
|
+
show_default=True,
|
|
705
|
+
help="Dimension mapping file. Must match the data model defined by "
|
|
706
|
+
"https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.dimension_mappings_config.DimensionMappingsConfigModel",
|
|
707
|
+
callback=path_callback,
|
|
708
|
+
)
|
|
709
|
+
@click.option(
|
|
710
|
+
"-r",
|
|
711
|
+
"--dimension-mapping-references-file",
|
|
712
|
+
type=click.Path(exists=True),
|
|
713
|
+
show_default=True,
|
|
714
|
+
help="dimension mapping references file. Mutually exclusive with dimension_mapping_file. "
|
|
715
|
+
"Use it when the mappings are already registered. Must mach the data model defined by "
|
|
716
|
+
"https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.dimension_mapping_base.DimensionMappingReferenceListModel",
|
|
717
|
+
callback=path_callback,
|
|
718
|
+
)
|
|
719
|
+
@click.option(
|
|
720
|
+
"-a",
|
|
721
|
+
"--autogen-reverse-supplemental-mappings",
|
|
722
|
+
type=click.Choice([x.value for x in DimensionType]),
|
|
723
|
+
callback=lambda _, __, x: [DimensionType(y) for y in x],
|
|
724
|
+
multiple=True,
|
|
725
|
+
help="For any dimension listed here, if the dataset's dimension is a project's supplemental "
|
|
726
|
+
"dimension and no mapping is provided, create a reverse mapping from that supplemental "
|
|
727
|
+
"dimension.",
|
|
728
|
+
)
|
|
729
|
+
@click.option(
|
|
730
|
+
"-l",
|
|
731
|
+
"--log-message",
|
|
732
|
+
required=True,
|
|
733
|
+
type=str,
|
|
734
|
+
help="reason for submission",
|
|
735
|
+
)
|
|
736
|
+
@click.pass_obj
|
|
737
|
+
def submit_dataset(
|
|
738
|
+
registry_manager: RegistryManager,
|
|
739
|
+
dataset_id: str,
|
|
740
|
+
project_id: str,
|
|
741
|
+
dimension_mapping_file: Path,
|
|
742
|
+
dimension_mapping_references_file: Path,
|
|
743
|
+
autogen_reverse_supplemental_mappings: list[DimensionType],
|
|
744
|
+
log_message: str,
|
|
745
|
+
):
|
|
746
|
+
"""Submit a dataset to a dsgrid project."""
|
|
747
|
+
submitter = getpass.getuser()
|
|
748
|
+
manager = registry_manager.project_manager
|
|
749
|
+
manager.submit_dataset(
|
|
750
|
+
project_id,
|
|
751
|
+
dataset_id,
|
|
752
|
+
submitter,
|
|
753
|
+
log_message,
|
|
754
|
+
dimension_mapping_file=dimension_mapping_file,
|
|
755
|
+
dimension_mapping_references_file=dimension_mapping_references_file,
|
|
756
|
+
autogen_reverse_supplemental_mappings=autogen_reverse_supplemental_mappings,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
_register_and_submit_dataset_epilog = """
|
|
761
|
+
Examples:\n
|
|
762
|
+
$ dsgrid registry projects register-and-submit-dataset \\ \n
|
|
763
|
+
-c dataset.json5 \\ \n
|
|
764
|
+
-p my-project-id \\ \n
|
|
765
|
+
-m dimension_mappings.json5 \\ \n
|
|
766
|
+
-l "Register and submit dataset my-dataset to project my-project." \n
|
|
767
|
+
|
|
768
|
+
$ dsgrid registry projects register-and-submit-dataset \\ \n
|
|
769
|
+
-c dataset.json5 \\ \n
|
|
770
|
+
--data-base-dir /path/to/data \\ \n
|
|
771
|
+
-p my-project-id \\ \n
|
|
772
|
+
-m dimension_mappings.json5 \\ \n
|
|
773
|
+
-l "Register and submit dataset my-dataset to project my-project." \n
|
|
774
|
+
"""
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
@click.command(epilog=_register_and_submit_dataset_epilog)
|
|
778
|
+
@click.option(
|
|
779
|
+
"-c",
|
|
780
|
+
"--dataset-config-file",
|
|
781
|
+
required=True,
|
|
782
|
+
type=click.Path(exists=True),
|
|
783
|
+
callback=path_callback,
|
|
784
|
+
help="Dataset config file (must include data_layout with data_file paths)",
|
|
785
|
+
)
|
|
786
|
+
@click.option(
|
|
787
|
+
"-m",
|
|
788
|
+
"--dimension-mapping-file",
|
|
789
|
+
type=click.Path(exists=True),
|
|
790
|
+
help="Dimension mapping file. Must match the data model defined by "
|
|
791
|
+
"https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.dimension_mappings_config.DimensionMappingsConfigModel",
|
|
792
|
+
callback=path_callback,
|
|
793
|
+
)
|
|
794
|
+
@click.option(
|
|
795
|
+
"-r",
|
|
796
|
+
"--dimension-mapping-references-file",
|
|
797
|
+
type=click.Path(exists=True),
|
|
798
|
+
show_default=True,
|
|
799
|
+
help="dimension mapping references file. Mutually exclusive with dimension_mapping_file. "
|
|
800
|
+
"Use it when the mappings are already registered. Must mach the data model defined by "
|
|
801
|
+
"https://dsgrid.github.io/dsgrid/reference/data_models/dimension_mapping.html#dsgrid.config.dimension_mapping_base.DimensionMappingReferenceListModel",
|
|
802
|
+
callback=path_callback,
|
|
803
|
+
)
|
|
804
|
+
@click.option(
|
|
805
|
+
"-a",
|
|
806
|
+
"--autogen-reverse-supplemental-mappings",
|
|
807
|
+
type=click.Choice([x.value for x in DimensionType]),
|
|
808
|
+
callback=lambda _, __, x: [DimensionType(y) for y in x],
|
|
809
|
+
multiple=True,
|
|
810
|
+
help="For any dimension listed here, if the dataset's dimension is a project's supplemental "
|
|
811
|
+
"dimension and no mapping is provided, create a reverse mapping from that supplemental "
|
|
812
|
+
"dimension.",
|
|
813
|
+
)
|
|
814
|
+
@click.option(
|
|
815
|
+
"-p",
|
|
816
|
+
"--project-id",
|
|
817
|
+
required=True,
|
|
818
|
+
type=str,
|
|
819
|
+
help="project identifier",
|
|
820
|
+
)
|
|
821
|
+
@click.option(
|
|
822
|
+
"-l",
|
|
823
|
+
"--log-message",
|
|
824
|
+
required=True,
|
|
825
|
+
type=str,
|
|
826
|
+
help="reason for submission",
|
|
827
|
+
)
|
|
828
|
+
@click.option(
|
|
829
|
+
"-D",
|
|
830
|
+
"--data-base-dir",
|
|
831
|
+
type=click.Path(exists=True),
|
|
832
|
+
callback=path_callback,
|
|
833
|
+
help="Base directory for data files. If set and data file paths are relative, "
|
|
834
|
+
"prepend them with this path.",
|
|
835
|
+
)
|
|
836
|
+
@click.option(
|
|
837
|
+
"-M",
|
|
838
|
+
"--missing-associations-base-dir",
|
|
839
|
+
type=click.Path(exists=True),
|
|
840
|
+
callback=path_callback,
|
|
841
|
+
help="Base directory for missing associations files. If set and missing associations "
|
|
842
|
+
"paths are relative, prepend them with this path.",
|
|
843
|
+
)
|
|
844
|
+
@click.pass_obj
|
|
845
|
+
@click.pass_context
|
|
846
|
+
def register_and_submit_dataset(
|
|
847
|
+
ctx,
|
|
848
|
+
registry_manager,
|
|
849
|
+
dataset_config_file,
|
|
850
|
+
dimension_mapping_file,
|
|
851
|
+
dimension_mapping_references_file,
|
|
852
|
+
autogen_reverse_supplemental_mappings,
|
|
853
|
+
project_id,
|
|
854
|
+
log_message,
|
|
855
|
+
data_base_dir,
|
|
856
|
+
missing_associations_base_dir,
|
|
857
|
+
):
|
|
858
|
+
"""Register a dataset and then submit it to a dsgrid project.
|
|
859
|
+
|
|
860
|
+
The dataset config file must include a data_layout with data_file and optional
|
|
861
|
+
lookup_data_file paths pointing to the dataset files.
|
|
862
|
+
"""
|
|
863
|
+
submitter = getpass.getuser()
|
|
864
|
+
manager = registry_manager.project_manager
|
|
865
|
+
res = handle_dsgrid_exception(
|
|
866
|
+
ctx,
|
|
867
|
+
manager.register_and_submit_dataset,
|
|
868
|
+
dataset_config_file,
|
|
869
|
+
project_id,
|
|
870
|
+
submitter,
|
|
871
|
+
log_message,
|
|
872
|
+
dimension_mapping_file=dimension_mapping_file,
|
|
873
|
+
dimension_mapping_references_file=dimension_mapping_references_file,
|
|
874
|
+
autogen_reverse_supplemental_mappings=autogen_reverse_supplemental_mappings,
|
|
875
|
+
data_base_dir=data_base_dir,
|
|
876
|
+
missing_associations_base_dir=missing_associations_base_dir,
|
|
877
|
+
)
|
|
878
|
+
if res[1] != 0:
|
|
879
|
+
ctx.exit(res[1])
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
_dump_project_epilog = """
|
|
883
|
+
Examples:\n
|
|
884
|
+
$ dsgrid registry projects dump my-project-id\n
|
|
885
|
+
"""
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
@click.command(name="dump", epilog=_dump_project_epilog)
|
|
889
|
+
@click.argument("project-id")
|
|
890
|
+
@click.option(
|
|
891
|
+
"-v",
|
|
892
|
+
"--version",
|
|
893
|
+
callback=_version_info_callback,
|
|
894
|
+
help="Version to dump; defaults to latest",
|
|
895
|
+
)
|
|
896
|
+
@click.option(
|
|
897
|
+
"-d",
|
|
898
|
+
"--directory",
|
|
899
|
+
default=".",
|
|
900
|
+
type=click.Path(exists=True),
|
|
901
|
+
help="Directory in which to create the config file",
|
|
902
|
+
)
|
|
903
|
+
@click.option(
|
|
904
|
+
"--force",
|
|
905
|
+
is_flag=True,
|
|
906
|
+
default=False,
|
|
907
|
+
show_default=True,
|
|
908
|
+
help="Overwrite files if they exist.",
|
|
909
|
+
)
|
|
910
|
+
@click.pass_obj
|
|
911
|
+
@click.pass_context
|
|
912
|
+
def dump_project(
|
|
913
|
+
ctx,
|
|
914
|
+
registry_manager: RegistryManager,
|
|
915
|
+
project_id: str,
|
|
916
|
+
version: str,
|
|
917
|
+
directory: Path,
|
|
918
|
+
force: bool,
|
|
919
|
+
):
|
|
920
|
+
"""Dump a project config file from the registry."""
|
|
921
|
+
manager = registry_manager.project_manager
|
|
922
|
+
res = handle_dsgrid_exception(
|
|
923
|
+
ctx, manager.dump, project_id, directory, version=version, force=force
|
|
924
|
+
)
|
|
925
|
+
if res[1] != 0:
|
|
926
|
+
ctx.exit(res[1])
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
_update_project_epilog = """
|
|
930
|
+
Examples: \n
|
|
931
|
+
$ dsgrid registry projects update \\ \n
|
|
932
|
+
-p my-project-id \\ \n
|
|
933
|
+
-u patch \\ \n
|
|
934
|
+
-v 1.5.0 \\ \n
|
|
935
|
+
-l "Update description for project my-project-id." \n
|
|
936
|
+
"""
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
@click.command(name="update", epilog=_update_project_epilog)
|
|
940
|
+
@click.argument("project-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
941
|
+
@click.option(
|
|
942
|
+
"-p",
|
|
943
|
+
"--project-id",
|
|
944
|
+
required=True,
|
|
945
|
+
type=str,
|
|
946
|
+
help="project ID",
|
|
947
|
+
)
|
|
948
|
+
@click.option(
|
|
949
|
+
"-l",
|
|
950
|
+
"--log-message",
|
|
951
|
+
required=True,
|
|
952
|
+
type=str,
|
|
953
|
+
help="reason for submission",
|
|
954
|
+
)
|
|
955
|
+
@click.option(
|
|
956
|
+
"-t",
|
|
957
|
+
"--update-type",
|
|
958
|
+
required=True,
|
|
959
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
960
|
+
callback=_version_update_callback,
|
|
961
|
+
)
|
|
962
|
+
@click.option(
|
|
963
|
+
"-v",
|
|
964
|
+
"--version",
|
|
965
|
+
required=True,
|
|
966
|
+
callback=_version_info_required_callback,
|
|
967
|
+
help="Version to update; must be the current version.",
|
|
968
|
+
)
|
|
969
|
+
@click.pass_obj
|
|
970
|
+
@click.pass_context
|
|
971
|
+
def update_project(
|
|
972
|
+
ctx,
|
|
973
|
+
registry_manager: RegistryManager,
|
|
974
|
+
project_config_file: Path,
|
|
975
|
+
project_id: str,
|
|
976
|
+
log_message: str,
|
|
977
|
+
update_type: VersionUpdateType,
|
|
978
|
+
version: str,
|
|
979
|
+
):
|
|
980
|
+
"""Update an existing project in the registry."""
|
|
981
|
+
manager = registry_manager.project_manager
|
|
982
|
+
submitter = getpass.getuser()
|
|
983
|
+
res = handle_dsgrid_exception(
|
|
984
|
+
ctx,
|
|
985
|
+
manager.update_from_file,
|
|
986
|
+
project_config_file,
|
|
987
|
+
project_id,
|
|
988
|
+
submitter,
|
|
989
|
+
update_type,
|
|
990
|
+
log_message,
|
|
991
|
+
version,
|
|
992
|
+
)
|
|
993
|
+
if res[1] != 0:
|
|
994
|
+
ctx.exit(res[1])
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
_register_subset_dimensions_epilog = """
|
|
998
|
+
Examples:\n
|
|
999
|
+
$ dsgrid registry projects register-subset-dimensions \\ \n
|
|
1000
|
+
-l "Register subset dimensions for end uses by fuel type for my-project-id." \\ \n
|
|
1001
|
+
my-project-id \\ \n
|
|
1002
|
+
subset_dimensions.json5 \n
|
|
1003
|
+
"""
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
@click.command(epilog=_register_subset_dimensions_epilog)
|
|
1007
|
+
@click.pass_obj
|
|
1008
|
+
@click.pass_context
|
|
1009
|
+
@click.argument("project_id")
|
|
1010
|
+
@click.argument("filename", callback=path_callback)
|
|
1011
|
+
@click.option(
|
|
1012
|
+
"-l",
|
|
1013
|
+
"--log-message",
|
|
1014
|
+
required=True,
|
|
1015
|
+
type=str,
|
|
1016
|
+
help="Please specify the reason for this addition.",
|
|
1017
|
+
)
|
|
1018
|
+
@click.option(
|
|
1019
|
+
"-t",
|
|
1020
|
+
"--update-type",
|
|
1021
|
+
default="patch",
|
|
1022
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
1023
|
+
callback=_version_update_callback,
|
|
1024
|
+
)
|
|
1025
|
+
def register_subset_dimensions(
|
|
1026
|
+
ctx,
|
|
1027
|
+
registry_manager: RegistryManager,
|
|
1028
|
+
project_id: str,
|
|
1029
|
+
filename: Path,
|
|
1030
|
+
log_message: str,
|
|
1031
|
+
update_type: VersionUpdateType,
|
|
1032
|
+
):
|
|
1033
|
+
"""Register new subset dimensions with a project. The contents of the JSON/JSON5 file must
|
|
1034
|
+
match the data model defined by this documentation:
|
|
1035
|
+
|
|
1036
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.project_config.SubsetDimensionGroupListModel
|
|
1037
|
+
"""
|
|
1038
|
+
|
|
1039
|
+
submitter = getpass.getuser()
|
|
1040
|
+
project_mgr = registry_manager.project_manager
|
|
1041
|
+
res = handle_dsgrid_exception(
|
|
1042
|
+
ctx,
|
|
1043
|
+
project_mgr.register_subset_dimensions,
|
|
1044
|
+
project_id,
|
|
1045
|
+
filename,
|
|
1046
|
+
submitter,
|
|
1047
|
+
log_message,
|
|
1048
|
+
update_type,
|
|
1049
|
+
)
|
|
1050
|
+
if res[1] != 0:
|
|
1051
|
+
ctx.exit(res[1])
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
_register_supplemental_dimensions_epilog = """
|
|
1055
|
+
Examples:\n
|
|
1056
|
+
$ dsgrid registry projects register-supplemental-dimensions \\ \n
|
|
1057
|
+
-l "Register states supplemental dimension for my-project-id" \\ \n
|
|
1058
|
+
my-project-id \\ \n
|
|
1059
|
+
supplemental_dimensions.json5\n
|
|
1060
|
+
"""
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
@click.command(epilog=_register_supplemental_dimensions_epilog)
|
|
1064
|
+
@click.pass_obj
|
|
1065
|
+
@click.pass_context
|
|
1066
|
+
@click.argument("project_id")
|
|
1067
|
+
@click.argument("filename", callback=path_callback)
|
|
1068
|
+
@click.option(
|
|
1069
|
+
"-l",
|
|
1070
|
+
"--log-message",
|
|
1071
|
+
required=True,
|
|
1072
|
+
type=str,
|
|
1073
|
+
help="Please specify the reason for this addition.",
|
|
1074
|
+
)
|
|
1075
|
+
@click.option(
|
|
1076
|
+
"-t",
|
|
1077
|
+
"--update-type",
|
|
1078
|
+
default="patch",
|
|
1079
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
1080
|
+
callback=_version_update_callback,
|
|
1081
|
+
)
|
|
1082
|
+
def register_supplemental_dimensions(
|
|
1083
|
+
ctx, registry_manager, project_id, filename: Path, log_message, update_type
|
|
1084
|
+
):
|
|
1085
|
+
"""Register new supplemental dimensions with a project. The contents of the JSON/JSON5 file
|
|
1086
|
+
must match the data model defined by this documentation:
|
|
1087
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.supplemental_dimension.SupplementalDimensionsListModel
|
|
1088
|
+
"""
|
|
1089
|
+
|
|
1090
|
+
submitter = getpass.getuser()
|
|
1091
|
+
project_mgr = registry_manager.project_manager
|
|
1092
|
+
res = handle_dsgrid_exception(
|
|
1093
|
+
ctx,
|
|
1094
|
+
project_mgr.register_supplemental_dimensions,
|
|
1095
|
+
project_id,
|
|
1096
|
+
filename,
|
|
1097
|
+
submitter,
|
|
1098
|
+
log_message,
|
|
1099
|
+
update_type,
|
|
1100
|
+
)
|
|
1101
|
+
if res[1] != 0:
|
|
1102
|
+
ctx.exit(res[1])
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
_add_dataset_requirements_epilog = """
|
|
1106
|
+
Examples:\n
|
|
1107
|
+
$ dsgrid registry projects add-dataset-requirements \\ \n
|
|
1108
|
+
-l "Add requirements for dataset my-dataset-id to my-project-id." \\ \n
|
|
1109
|
+
my-project-id \\ \n
|
|
1110
|
+
dataset_requirements.json5\n
|
|
1111
|
+
"""
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
@click.command(epilog=_add_dataset_requirements_epilog)
|
|
1115
|
+
@click.pass_obj
|
|
1116
|
+
@click.pass_context
|
|
1117
|
+
@click.argument("project_id")
|
|
1118
|
+
@click.argument("filename", callback=path_callback)
|
|
1119
|
+
@click.option(
|
|
1120
|
+
"-l",
|
|
1121
|
+
"--log-message",
|
|
1122
|
+
required=True,
|
|
1123
|
+
type=str,
|
|
1124
|
+
help="Please specify the reason for the new datasets.",
|
|
1125
|
+
)
|
|
1126
|
+
@click.option(
|
|
1127
|
+
"-t",
|
|
1128
|
+
"--update-type",
|
|
1129
|
+
default="patch",
|
|
1130
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
1131
|
+
callback=_version_update_callback,
|
|
1132
|
+
)
|
|
1133
|
+
def add_dataset_requirements(
|
|
1134
|
+
ctx, registry_manager, project_id, filename: Path, log_message, update_type
|
|
1135
|
+
):
|
|
1136
|
+
"""Add requirements for one or more datasets to a project. The contents of the JSON/JSON5 file
|
|
1137
|
+
must match the data model defined by this documentation:
|
|
1138
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.input_dataset_requirements.InputDatasetListModel
|
|
1139
|
+
"""
|
|
1140
|
+
submitter = getpass.getuser()
|
|
1141
|
+
project_mgr = registry_manager.project_manager
|
|
1142
|
+
res = handle_dsgrid_exception(
|
|
1143
|
+
ctx,
|
|
1144
|
+
project_mgr.add_dataset_requirements,
|
|
1145
|
+
project_id,
|
|
1146
|
+
filename,
|
|
1147
|
+
submitter,
|
|
1148
|
+
log_message,
|
|
1149
|
+
update_type,
|
|
1150
|
+
)
|
|
1151
|
+
if res[1] != 0:
|
|
1152
|
+
ctx.exit(res[1])
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
_replace_dataset_dimension_requirements_epilog = """
|
|
1156
|
+
Examples:\n
|
|
1157
|
+
$ dsgrid registry projects replace-dataset-dimension-requirements \\ \n
|
|
1158
|
+
-l "Replace dimension requirements for dataset my-dataset-id in my-project-id." \\ \n
|
|
1159
|
+
project_id \\ \n
|
|
1160
|
+
dataset_dimension_requirements.json5\n
|
|
1161
|
+
"""
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
@click.command(epilog=_replace_dataset_dimension_requirements_epilog)
|
|
1165
|
+
@click.pass_obj
|
|
1166
|
+
@click.pass_context
|
|
1167
|
+
@click.argument("project_id")
|
|
1168
|
+
@click.argument("filename", callback=path_callback)
|
|
1169
|
+
@click.option(
|
|
1170
|
+
"-l",
|
|
1171
|
+
"--log-message",
|
|
1172
|
+
required=True,
|
|
1173
|
+
type=str,
|
|
1174
|
+
help="Please specify the reason for the new requirements.",
|
|
1175
|
+
)
|
|
1176
|
+
@click.option(
|
|
1177
|
+
"-t",
|
|
1178
|
+
"--update-type",
|
|
1179
|
+
default="major",
|
|
1180
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
1181
|
+
callback=_version_update_callback,
|
|
1182
|
+
)
|
|
1183
|
+
def replace_dataset_dimension_requirements(
|
|
1184
|
+
ctx, registry_manager, project_id, filename: Path, log_message, update_type
|
|
1185
|
+
):
|
|
1186
|
+
"""Replace dimension requirements for one or more datasets in a project. The contents of the
|
|
1187
|
+
JSON/JSON5 file must match the data model defined by this documentation:
|
|
1188
|
+
|
|
1189
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.input_dataset_requirements.InputDatasetDimensionRequirementsListModel
|
|
1190
|
+
"""
|
|
1191
|
+
submitter = getpass.getuser()
|
|
1192
|
+
project_mgr = registry_manager.project_manager
|
|
1193
|
+
res = handle_dsgrid_exception(
|
|
1194
|
+
ctx,
|
|
1195
|
+
project_mgr.replace_dataset_dimension_requirements,
|
|
1196
|
+
project_id,
|
|
1197
|
+
filename,
|
|
1198
|
+
submitter,
|
|
1199
|
+
log_message,
|
|
1200
|
+
update_type,
|
|
1201
|
+
)
|
|
1202
|
+
if res[1] != 0:
|
|
1203
|
+
ctx.exit(res[1])
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
_list_project_dimension_names_epilog = """
|
|
1207
|
+
Examples:\n
|
|
1208
|
+
$ dsgrid registry projects list-dimension-names my_project_id\n
|
|
1209
|
+
$ dsgrid registry projects list-dimension-names --exclude-subset my_project_id\n
|
|
1210
|
+
$ dsgrid registry projects list-dimension-names --exclude-supplemental my_project_id\n
|
|
1211
|
+
"""
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
@click.command(name="list-dimension-names", epilog=_list_project_dimension_names_epilog)
|
|
1215
|
+
@click.argument("project-id")
|
|
1216
|
+
@click.option(
|
|
1217
|
+
"-b",
|
|
1218
|
+
"--exclude-base",
|
|
1219
|
+
is_flag=True,
|
|
1220
|
+
default=False,
|
|
1221
|
+
show_default=True,
|
|
1222
|
+
help="Exclude base dimension names.",
|
|
1223
|
+
)
|
|
1224
|
+
@click.option(
|
|
1225
|
+
"-S",
|
|
1226
|
+
"--exclude-subset",
|
|
1227
|
+
is_flag=True,
|
|
1228
|
+
default=False,
|
|
1229
|
+
show_default=True,
|
|
1230
|
+
help="Exclude subset dimension names.",
|
|
1231
|
+
)
|
|
1232
|
+
@click.option(
|
|
1233
|
+
"-s",
|
|
1234
|
+
"--exclude-supplemental",
|
|
1235
|
+
is_flag=True,
|
|
1236
|
+
default=False,
|
|
1237
|
+
show_default=True,
|
|
1238
|
+
help="Exclude supplemental dimension names.",
|
|
1239
|
+
)
|
|
1240
|
+
@click.pass_obj
|
|
1241
|
+
@click.pass_context
|
|
1242
|
+
def list_project_dimension_names(
|
|
1243
|
+
ctx,
|
|
1244
|
+
registry_manager: RegistryManager,
|
|
1245
|
+
project_id,
|
|
1246
|
+
exclude_base,
|
|
1247
|
+
exclude_subset,
|
|
1248
|
+
exclude_supplemental,
|
|
1249
|
+
):
|
|
1250
|
+
"""List the project's dimension names."""
|
|
1251
|
+
if exclude_base and exclude_subset and exclude_supplemental:
|
|
1252
|
+
print(
|
|
1253
|
+
"exclude_base, exclude_subset, and exclude_supplemental cannot all be set",
|
|
1254
|
+
file=sys.stderr,
|
|
1255
|
+
)
|
|
1256
|
+
ctx.exit(1)
|
|
1257
|
+
|
|
1258
|
+
manager = registry_manager.project_manager
|
|
1259
|
+
res = handle_dsgrid_exception(ctx, manager.get_by_id, project_id)
|
|
1260
|
+
if res[1] != 0:
|
|
1261
|
+
ctx.exit(res[1])
|
|
1262
|
+
|
|
1263
|
+
project_config = res[0]
|
|
1264
|
+
assert isinstance(project_config, ProjectConfig)
|
|
1265
|
+
base = None if exclude_base else project_config.get_dimension_type_to_base_name_mapping()
|
|
1266
|
+
sub = None if exclude_subset else project_config.get_subset_dimension_to_name_mapping()
|
|
1267
|
+
supp = (
|
|
1268
|
+
None
|
|
1269
|
+
if exclude_supplemental
|
|
1270
|
+
else project_config.get_supplemental_dimension_to_name_mapping()
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
dimensions = sorted(DimensionType, key=lambda x: x.value)
|
|
1274
|
+
print("Dimension names:")
|
|
1275
|
+
for dim_type in dimensions:
|
|
1276
|
+
print(f" {dim_type.value}:")
|
|
1277
|
+
if base:
|
|
1278
|
+
base_str = " ".join(base[dim_type])
|
|
1279
|
+
print(f" base: {base_str}")
|
|
1280
|
+
if sub:
|
|
1281
|
+
print(" subset: " + " ".join(sub[dim_type]))
|
|
1282
|
+
if supp:
|
|
1283
|
+
print(" supplemental: " + " ".join(supp[dim_type]))
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
_generate_project_config_epilog = """
|
|
1287
|
+
Examples:\n
|
|
1288
|
+
$ dsgrid registry projects generate-config \\ \n
|
|
1289
|
+
-o "./my-project-dir" \\ \n
|
|
1290
|
+
my-project-id dataset-id1 dataset-id2 dataset-id3\n
|
|
1291
|
+
"""
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
@click.command(name="generate-config", epilog=_generate_project_config_epilog)
|
|
1295
|
+
@click.argument("project_id")
|
|
1296
|
+
@click.argument("dataset_ids", nargs=-1)
|
|
1297
|
+
@click.option(
|
|
1298
|
+
"-m",
|
|
1299
|
+
"--metric-type",
|
|
1300
|
+
multiple=True,
|
|
1301
|
+
type=click.Choice(sorted(SUPPORTED_METRIC_TYPES)),
|
|
1302
|
+
help="Metric types available in the project",
|
|
1303
|
+
)
|
|
1304
|
+
@click.option(
|
|
1305
|
+
"-n",
|
|
1306
|
+
"--name",
|
|
1307
|
+
type=str,
|
|
1308
|
+
help="Project name, optional",
|
|
1309
|
+
)
|
|
1310
|
+
@click.option(
|
|
1311
|
+
"-d",
|
|
1312
|
+
"--description",
|
|
1313
|
+
type=str,
|
|
1314
|
+
help="Project description, optional",
|
|
1315
|
+
)
|
|
1316
|
+
@click.option(
|
|
1317
|
+
"-t",
|
|
1318
|
+
"--time-type",
|
|
1319
|
+
type=click.Choice([x.value for x in TimeDimensionType]),
|
|
1320
|
+
default=TimeDimensionType.DATETIME.value,
|
|
1321
|
+
show_default=True,
|
|
1322
|
+
help="Type of the time dimension",
|
|
1323
|
+
callback=lambda *x: TimeDimensionType(x[2]),
|
|
1324
|
+
)
|
|
1325
|
+
@click.option(
|
|
1326
|
+
"-o",
|
|
1327
|
+
"--output",
|
|
1328
|
+
type=click.Path(),
|
|
1329
|
+
default=".",
|
|
1330
|
+
show_default=True,
|
|
1331
|
+
callback=path_callback,
|
|
1332
|
+
help="Path in which to generate project config files.",
|
|
1333
|
+
)
|
|
1334
|
+
@click.option(
|
|
1335
|
+
"-O",
|
|
1336
|
+
"--overwrite",
|
|
1337
|
+
is_flag=True,
|
|
1338
|
+
default=False,
|
|
1339
|
+
show_default=True,
|
|
1340
|
+
help="Overwrite files if they exist.",
|
|
1341
|
+
)
|
|
1342
|
+
@click.pass_context
|
|
1343
|
+
def generate_project_config_from_ids(
|
|
1344
|
+
ctx: click.Context,
|
|
1345
|
+
project_id: str,
|
|
1346
|
+
dataset_ids: tuple[str],
|
|
1347
|
+
metric_type: tuple[str],
|
|
1348
|
+
name: str | None,
|
|
1349
|
+
description: str | None,
|
|
1350
|
+
time_type: TimeDimensionType,
|
|
1351
|
+
output: Path | None,
|
|
1352
|
+
overwrite: bool,
|
|
1353
|
+
):
|
|
1354
|
+
"""Generate project config files from a project ID and one or more dataset IDs."""
|
|
1355
|
+
res = handle_dsgrid_exception(
|
|
1356
|
+
ctx,
|
|
1357
|
+
generate_project_config,
|
|
1358
|
+
project_id,
|
|
1359
|
+
dataset_ids,
|
|
1360
|
+
metric_type,
|
|
1361
|
+
name=name,
|
|
1362
|
+
description=description,
|
|
1363
|
+
time_type=time_type,
|
|
1364
|
+
output_directory=output,
|
|
1365
|
+
overwrite=overwrite,
|
|
1366
|
+
)
|
|
1367
|
+
if res[1] != 0:
|
|
1368
|
+
ctx.exit(res[1])
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
@click.command(name="remove")
|
|
1372
|
+
@click.argument("project-id")
|
|
1373
|
+
@click.pass_obj
|
|
1374
|
+
def remove_project(registry_manager: RegistryManager, project_id: str):
|
|
1375
|
+
"""Remove a project from the dsgrid repository."""
|
|
1376
|
+
registry_manager.project_manager.remove(project_id)
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
"""
|
|
1380
|
+
Dataset Commands
|
|
1381
|
+
"""
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
_list_datasets_epilog = """
|
|
1385
|
+
Examples:\n
|
|
1386
|
+
$ dsgrid registry datasets list\n
|
|
1387
|
+
$ dsgrid registry datasets list -f "ID contains com" -f "Submitter == username"\n
|
|
1388
|
+
"""
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
@click.command(name="list", epilog=_list_datasets_epilog)
|
|
1392
|
+
@click.option(
|
|
1393
|
+
"-f",
|
|
1394
|
+
"--filter",
|
|
1395
|
+
multiple=True,
|
|
1396
|
+
type=str,
|
|
1397
|
+
help=f"""
|
|
1398
|
+
Filter table with a case-insensitive expression in the format 'column operation value',
|
|
1399
|
+
accepts multiple flags\b\n
|
|
1400
|
+
valid operations: {ACCEPTED_OPS}\n
|
|
1401
|
+
""",
|
|
1402
|
+
)
|
|
1403
|
+
@click.pass_obj
|
|
1404
|
+
def list_datasets(registry_manager, filter):
|
|
1405
|
+
"""List the registered dimensions."""
|
|
1406
|
+
registry_manager.dataset_manager.show(filters=filter)
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
_register_dataset_epilog = """
|
|
1410
|
+
Examples:\n
|
|
1411
|
+
$ dsgrid registry datasets register dataset.json5 -l "Register dataset my-dataset-id."\n
|
|
1412
|
+
$ dsgrid registry datasets register dataset.json5 --data-base-dir /path/to/data -l "Register dataset my-dataset-id."\n
|
|
1413
|
+
"""
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
@click.command(name="register", epilog=_register_dataset_epilog)
|
|
1417
|
+
@click.argument("dataset-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
1418
|
+
@click.option(
|
|
1419
|
+
"-l",
|
|
1420
|
+
"--log-message",
|
|
1421
|
+
required=True,
|
|
1422
|
+
help="reason for submission",
|
|
1423
|
+
)
|
|
1424
|
+
@click.option(
|
|
1425
|
+
"-D",
|
|
1426
|
+
"--data-base-dir",
|
|
1427
|
+
type=click.Path(exists=True),
|
|
1428
|
+
callback=path_callback,
|
|
1429
|
+
help="Base directory for data files. If set and data file paths are relative, "
|
|
1430
|
+
"prepend them with this path.",
|
|
1431
|
+
)
|
|
1432
|
+
@click.option(
|
|
1433
|
+
"-M",
|
|
1434
|
+
"--missing-associations-base-dir",
|
|
1435
|
+
type=click.Path(exists=True),
|
|
1436
|
+
callback=path_callback,
|
|
1437
|
+
help="Base directory for missing associations files. If set and missing associations "
|
|
1438
|
+
"paths are relative, prepend them with this path.",
|
|
1439
|
+
)
|
|
1440
|
+
@click.pass_obj
|
|
1441
|
+
@click.pass_context
|
|
1442
|
+
def register_dataset(
|
|
1443
|
+
ctx: click.Context,
|
|
1444
|
+
registry_manager: RegistryManager,
|
|
1445
|
+
dataset_config_file: Path,
|
|
1446
|
+
log_message: str,
|
|
1447
|
+
data_base_dir: Path | None,
|
|
1448
|
+
missing_associations_base_dir: Path | None,
|
|
1449
|
+
):
|
|
1450
|
+
"""Register a new dataset with the registry. The contents of the JSON/JSON5 file
|
|
1451
|
+
must match the data model defined by this documentation:
|
|
1452
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/dataset.html#dsgrid.config.dataset_config.DatasetConfigModel
|
|
1453
|
+
|
|
1454
|
+
The config file must include a data_layout with data_file and optional
|
|
1455
|
+
lookup_data_file paths pointing to the dataset files.
|
|
1456
|
+
"""
|
|
1457
|
+
manager = registry_manager.dataset_manager
|
|
1458
|
+
submitter = getpass.getuser()
|
|
1459
|
+
res = handle_dsgrid_exception(
|
|
1460
|
+
ctx,
|
|
1461
|
+
manager.register,
|
|
1462
|
+
dataset_config_file,
|
|
1463
|
+
submitter,
|
|
1464
|
+
log_message,
|
|
1465
|
+
data_base_dir=data_base_dir,
|
|
1466
|
+
missing_associations_base_dir=missing_associations_base_dir,
|
|
1467
|
+
)
|
|
1468
|
+
if res[1] != 0:
|
|
1469
|
+
ctx.exit(res[1])
|
|
1470
|
+
|
|
1471
|
+
|
|
1472
|
+
_dump_dataset_epilog = """
|
|
1473
|
+
Examples:\n
|
|
1474
|
+
$ dsgrid registry datasets dump my-dataset-id\n
|
|
1475
|
+
"""
|
|
1476
|
+
|
|
1477
|
+
|
|
1478
|
+
@click.command(name="dump", epilog=_dump_dataset_epilog)
|
|
1479
|
+
@click.argument("dataset-id")
|
|
1480
|
+
@click.option(
|
|
1481
|
+
"-v",
|
|
1482
|
+
"--version",
|
|
1483
|
+
callback=_version_info_callback,
|
|
1484
|
+
help="Version to dump; defaults to latest",
|
|
1485
|
+
)
|
|
1486
|
+
@click.option(
|
|
1487
|
+
"-d",
|
|
1488
|
+
"--directory",
|
|
1489
|
+
default=".",
|
|
1490
|
+
type=click.Path(exists=True),
|
|
1491
|
+
help="Directory in which to create the config file",
|
|
1492
|
+
)
|
|
1493
|
+
@click.option(
|
|
1494
|
+
"--force",
|
|
1495
|
+
is_flag=True,
|
|
1496
|
+
default=False,
|
|
1497
|
+
show_default=True,
|
|
1498
|
+
help="Overwrite files if they exist.",
|
|
1499
|
+
)
|
|
1500
|
+
@click.pass_obj
|
|
1501
|
+
def dump_dataset(registry_manager, dataset_id, version, directory, force):
|
|
1502
|
+
"""Dump a dataset config file from the registry."""
|
|
1503
|
+
manager = registry_manager.dataset_manager
|
|
1504
|
+
manager.dump(dataset_id, directory, version=version, force=force)
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
_update_dataset_epilog = """
|
|
1508
|
+
Examples:\n
|
|
1509
|
+
$ dsgrid registry datasets update \\ \n
|
|
1510
|
+
-l "Update the description for dataset my-dataset-id." \\ \n
|
|
1511
|
+
-u patch \\ \n
|
|
1512
|
+
-v 1.0.0 \\ \n
|
|
1513
|
+
dataset.json5\n
|
|
1514
|
+
"""
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
@click.command(name="update", epilog=_update_dataset_epilog)
|
|
1518
|
+
@click.argument("dataset-config-file", type=click.Path(exists=True), callback=path_callback)
|
|
1519
|
+
@click.option(
|
|
1520
|
+
"-d",
|
|
1521
|
+
"--dataset-id",
|
|
1522
|
+
required=True,
|
|
1523
|
+
type=str,
|
|
1524
|
+
help="dataset ID",
|
|
1525
|
+
)
|
|
1526
|
+
@click.option(
|
|
1527
|
+
"-l",
|
|
1528
|
+
"--log-message",
|
|
1529
|
+
required=True,
|
|
1530
|
+
type=str,
|
|
1531
|
+
help="reason for submission",
|
|
1532
|
+
)
|
|
1533
|
+
@click.option(
|
|
1534
|
+
"-t",
|
|
1535
|
+
"--update-type",
|
|
1536
|
+
required=True,
|
|
1537
|
+
type=click.Choice([x.value for x in VersionUpdateType]),
|
|
1538
|
+
callback=_version_update_callback,
|
|
1539
|
+
)
|
|
1540
|
+
@click.option(
|
|
1541
|
+
"-v",
|
|
1542
|
+
"--version",
|
|
1543
|
+
required=True,
|
|
1544
|
+
callback=_version_info_required_callback,
|
|
1545
|
+
help="Version to update; must be the current version.",
|
|
1546
|
+
)
|
|
1547
|
+
@click.pass_obj
|
|
1548
|
+
@click.pass_context
|
|
1549
|
+
def update_dataset(
|
|
1550
|
+
ctx: click.Context,
|
|
1551
|
+
registry_manager: RegistryManager,
|
|
1552
|
+
dataset_config_file: Path,
|
|
1553
|
+
dataset_id: str,
|
|
1554
|
+
log_message: str,
|
|
1555
|
+
update_type: VersionUpdateType,
|
|
1556
|
+
version: str,
|
|
1557
|
+
):
|
|
1558
|
+
"""Update an existing dataset in the registry. The contents of the JSON/JSON5 file
|
|
1559
|
+
must match the data model defined by this documentation:
|
|
1560
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/dataset.html#dsgrid.config.dataset_config.DatasetConfigModel
|
|
1561
|
+
|
|
1562
|
+
If the config file includes a UserDatasetSchema with file paths, the data will be
|
|
1563
|
+
re-read from those paths. Otherwise, the existing data in the registry is used.
|
|
1564
|
+
"""
|
|
1565
|
+
manager = registry_manager.dataset_manager
|
|
1566
|
+
submitter = getpass.getuser()
|
|
1567
|
+
res = handle_dsgrid_exception(
|
|
1568
|
+
ctx,
|
|
1569
|
+
manager.update_from_file,
|
|
1570
|
+
dataset_config_file,
|
|
1571
|
+
dataset_id,
|
|
1572
|
+
submitter,
|
|
1573
|
+
update_type,
|
|
1574
|
+
log_message,
|
|
1575
|
+
version,
|
|
1576
|
+
)
|
|
1577
|
+
if res[1] != 0:
|
|
1578
|
+
ctx.exit(res[1])
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
_generate_dataset_config_from_dataset_epilog = """
|
|
1582
|
+
Examples:\n
|
|
1583
|
+
$ dsgrid registry datasets generate-config-from-dataset \\ \n
|
|
1584
|
+
-o "./my-dataset-dir" \\ \n
|
|
1585
|
+
-P my-project-id \\ \n
|
|
1586
|
+
my-dataset-id \\ \n
|
|
1587
|
+
/path/to/table.parquet\n
|
|
1588
|
+
"""
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
@click.command(name="generate-config", epilog=_generate_dataset_config_from_dataset_epilog)
|
|
1592
|
+
@click.argument("dataset-id")
|
|
1593
|
+
@click.argument("dataset-path")
|
|
1594
|
+
@click.option(
|
|
1595
|
+
"-s",
|
|
1596
|
+
"--table-format",
|
|
1597
|
+
type=click.Choice([x.value for x in TableFormat]),
|
|
1598
|
+
default=TableFormat.ONE_TABLE.value,
|
|
1599
|
+
show_default=True,
|
|
1600
|
+
callback=lambda *x: TableFormat(x[2]),
|
|
1601
|
+
)
|
|
1602
|
+
@click.option(
|
|
1603
|
+
"-m",
|
|
1604
|
+
"--metric-type",
|
|
1605
|
+
type=click.Choice(sorted(SUPPORTED_METRIC_TYPES)),
|
|
1606
|
+
default="EnergyEndUse",
|
|
1607
|
+
show_default=True,
|
|
1608
|
+
)
|
|
1609
|
+
@click.option(
|
|
1610
|
+
"-p",
|
|
1611
|
+
"--pivoted-dimension-type",
|
|
1612
|
+
type=click.Choice([x.value for x in DimensionType if x != DimensionType.TIME]),
|
|
1613
|
+
default=None,
|
|
1614
|
+
callback=lambda *x: None if x[2] is None else DimensionType(x[2]),
|
|
1615
|
+
help="Optional, if one dimension has its records pivoted as columns, its type.",
|
|
1616
|
+
)
|
|
1617
|
+
@click.option(
|
|
1618
|
+
"-t",
|
|
1619
|
+
"--time-type",
|
|
1620
|
+
type=click.Choice([x.value for x in TimeDimensionType]),
|
|
1621
|
+
default=TimeDimensionType.DATETIME.value,
|
|
1622
|
+
show_default=True,
|
|
1623
|
+
help="Type of the time dimension",
|
|
1624
|
+
callback=lambda *x: TimeDimensionType(x[2]),
|
|
1625
|
+
)
|
|
1626
|
+
@click.option(
|
|
1627
|
+
"-T",
|
|
1628
|
+
"--time-columns",
|
|
1629
|
+
multiple=True,
|
|
1630
|
+
help="Names of time columns in the table. Required if pivoted_dimension_type is set "
|
|
1631
|
+
"and the time column is not 'timestamp'.",
|
|
1632
|
+
)
|
|
1633
|
+
@click.option(
|
|
1634
|
+
"-o",
|
|
1635
|
+
"--output",
|
|
1636
|
+
type=click.Path(),
|
|
1637
|
+
default=".",
|
|
1638
|
+
show_default=True,
|
|
1639
|
+
callback=path_callback,
|
|
1640
|
+
help="Path in which to create dataset config files.",
|
|
1641
|
+
)
|
|
1642
|
+
@click.option(
|
|
1643
|
+
"-P",
|
|
1644
|
+
"--project-id",
|
|
1645
|
+
required=False,
|
|
1646
|
+
type=str,
|
|
1647
|
+
help="Project ID, optional. If provided, prioritize project base dimensions when "
|
|
1648
|
+
"searching for matching dimensions.",
|
|
1649
|
+
)
|
|
1650
|
+
@click.option(
|
|
1651
|
+
"-n",
|
|
1652
|
+
"--no-prompts",
|
|
1653
|
+
is_flag=True,
|
|
1654
|
+
default=False,
|
|
1655
|
+
show_default=True,
|
|
1656
|
+
help="Do not prompt for matches. Automatically accept the first one.",
|
|
1657
|
+
)
|
|
1658
|
+
@click.option(
|
|
1659
|
+
"-O",
|
|
1660
|
+
"--overwrite",
|
|
1661
|
+
is_flag=True,
|
|
1662
|
+
default=False,
|
|
1663
|
+
show_default=True,
|
|
1664
|
+
help="Overwrite files if they exist.",
|
|
1665
|
+
)
|
|
1666
|
+
@click.pass_obj
|
|
1667
|
+
@click.pass_context
|
|
1668
|
+
def generate_dataset_config_from_dataset(
|
|
1669
|
+
ctx: click.Context,
|
|
1670
|
+
registry_manager: RegistryManager,
|
|
1671
|
+
dataset_id: str,
|
|
1672
|
+
dataset_path: Path,
|
|
1673
|
+
table_format: TableFormat,
|
|
1674
|
+
metric_type: str,
|
|
1675
|
+
pivoted_dimension_type: DimensionType | None,
|
|
1676
|
+
time_type: TimeDimensionType,
|
|
1677
|
+
time_columns: tuple[str],
|
|
1678
|
+
output: Path | None,
|
|
1679
|
+
project_id: str | None,
|
|
1680
|
+
no_prompts: bool,
|
|
1681
|
+
overwrite: bool,
|
|
1682
|
+
):
|
|
1683
|
+
"""Generate dataset config files from a dataset table.
|
|
1684
|
+
|
|
1685
|
+
Fill out the dimension record files based on the unique values in the dataset.
|
|
1686
|
+
|
|
1687
|
+
Look for matches for dimensions in the registry. Prompt the user for confirmation unless
|
|
1688
|
+
--no-prompts is set. If --no-prompts is set, the first match is automatically accepted.
|
|
1689
|
+
"""
|
|
1690
|
+
res = handle_dsgrid_exception(
|
|
1691
|
+
ctx,
|
|
1692
|
+
generate_config_from_dataset,
|
|
1693
|
+
registry_manager,
|
|
1694
|
+
dataset_id,
|
|
1695
|
+
dataset_path,
|
|
1696
|
+
table_format,
|
|
1697
|
+
metric_type,
|
|
1698
|
+
pivoted_dimension_type=pivoted_dimension_type,
|
|
1699
|
+
time_type=time_type,
|
|
1700
|
+
time_columns=set(time_columns),
|
|
1701
|
+
output_directory=output,
|
|
1702
|
+
project_id=project_id,
|
|
1703
|
+
no_prompts=no_prompts,
|
|
1704
|
+
overwrite=overwrite,
|
|
1705
|
+
)
|
|
1706
|
+
if res[1] != 0:
|
|
1707
|
+
ctx.exit(res[1])
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
@click.command(name="remove")
|
|
1711
|
+
@click.argument("dataset-ids", nargs=-1)
|
|
1712
|
+
@click.pass_obj
|
|
1713
|
+
def remove_datasets(registry_manager: RegistryManager, dataset_ids: list[str]):
|
|
1714
|
+
"""Remove one or more datasets from the dsgrid repository."""
|
|
1715
|
+
dataset_mgr = registry_manager.dataset_manager
|
|
1716
|
+
project_mgr = registry_manager.project_manager
|
|
1717
|
+
|
|
1718
|
+
# Ensure that all dataset IDs are valid before removing any of them.
|
|
1719
|
+
for dataset_id in dataset_ids:
|
|
1720
|
+
dataset_mgr.get_by_id(dataset_id)
|
|
1721
|
+
|
|
1722
|
+
for dataset_id in dataset_ids:
|
|
1723
|
+
registry_manager.dataset_manager.remove(dataset_id)
|
|
1724
|
+
|
|
1725
|
+
dataset_ids_set = set(dataset_ids)
|
|
1726
|
+
for project_id in project_mgr.list_ids():
|
|
1727
|
+
config = project_mgr.get_by_id(project_id)
|
|
1728
|
+
removed_dataset_ids = []
|
|
1729
|
+
for dataset in config.iter_datasets():
|
|
1730
|
+
if (
|
|
1731
|
+
dataset.dataset_id in dataset_ids_set
|
|
1732
|
+
and dataset.status == DatasetRegistryStatus.REGISTERED
|
|
1733
|
+
):
|
|
1734
|
+
dataset.status = DatasetRegistryStatus.UNREGISTERED
|
|
1735
|
+
dataset.mapping_references.clear()
|
|
1736
|
+
removed_dataset_ids.append(dataset.dataset_id)
|
|
1737
|
+
if removed_dataset_ids:
|
|
1738
|
+
ids = ", ".join(removed_dataset_ids)
|
|
1739
|
+
msg = (
|
|
1740
|
+
f"Set status for datasets {ids} to unregistered in project {project_id} "
|
|
1741
|
+
"after removal."
|
|
1742
|
+
)
|
|
1743
|
+
project_mgr.update(config, VersionUpdateType.MAJOR, msg)
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
_bulk_register_epilog = """
|
|
1747
|
+
Examples:\n
|
|
1748
|
+
$ dsgrid registry bulk-register registration.json5
|
|
1749
|
+
$ dsgrid registry bulk-register registration.json5 -j journal__11f733f6-ac9b-4f70-ad4b-df75b291f150.json5
|
|
1750
|
+
"""
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
@click.command(name="bulk-register", epilog=_bulk_register_epilog)
|
|
1754
|
+
@click.argument("registration_file", type=click.Path(exists=True))
|
|
1755
|
+
@click.option(
|
|
1756
|
+
"-D",
|
|
1757
|
+
"--data-base-dir",
|
|
1758
|
+
type=click.Path(exists=True),
|
|
1759
|
+
callback=path_callback,
|
|
1760
|
+
help="Base directory for data files. If set and data file paths are relative, "
|
|
1761
|
+
"prepend them with this path.",
|
|
1762
|
+
)
|
|
1763
|
+
@click.option(
|
|
1764
|
+
"-M",
|
|
1765
|
+
"--missing-associations-base-dir",
|
|
1766
|
+
type=click.Path(exists=True),
|
|
1767
|
+
callback=path_callback,
|
|
1768
|
+
help="Base directory for missing associations files. If set and missing associations "
|
|
1769
|
+
"paths are relative, prepend them with this path.",
|
|
1770
|
+
)
|
|
1771
|
+
@click.option(
|
|
1772
|
+
"-r",
|
|
1773
|
+
"--repo-base-dir",
|
|
1774
|
+
type=click.Path(exists=True),
|
|
1775
|
+
callback=path_callback,
|
|
1776
|
+
help="Base directory for dsgrid project/dataset repository. If set, and if the config file "
|
|
1777
|
+
"paths are relative, prepend them with this path.",
|
|
1778
|
+
)
|
|
1779
|
+
@click.option(
|
|
1780
|
+
"-j",
|
|
1781
|
+
"--journal-file",
|
|
1782
|
+
type=click.Path(exists=True),
|
|
1783
|
+
callback=path_callback,
|
|
1784
|
+
help="Journal file created by a previous bulk register operation. If passed, the code will "
|
|
1785
|
+
"read it and skip all projects and datasets that were successfully registered. "
|
|
1786
|
+
"The file will be updated with IDs that are successfully registered.",
|
|
1787
|
+
)
|
|
1788
|
+
@click.pass_obj
|
|
1789
|
+
@click.pass_context
|
|
1790
|
+
def bulk_register_cli(
|
|
1791
|
+
ctx,
|
|
1792
|
+
registry_manager: RegistryManager,
|
|
1793
|
+
registration_file: Path,
|
|
1794
|
+
data_base_dir: Path | None,
|
|
1795
|
+
missing_associations_base_dir: Path | None,
|
|
1796
|
+
repo_base_dir: Path | None,
|
|
1797
|
+
journal_file: Path | None,
|
|
1798
|
+
):
|
|
1799
|
+
"""Bulk register projects, datasets, and their dimensions. If any failure occurs, the code
|
|
1800
|
+
records successfully registered project and dataset IDs to a journal file and prints its
|
|
1801
|
+
filename to the console. Users can pass that filename with the --journal-file option to
|
|
1802
|
+
avoid re-registering those projects and datasets on subsequent attempts.
|
|
1803
|
+
|
|
1804
|
+
The JSON/JSON5 filename must match the data model defined by this documentation:
|
|
1805
|
+
|
|
1806
|
+
https://dsgrid.github.io/dsgrid/reference/data_models/project.html#dsgrid.config.registration_models.RegistrationModel
|
|
1807
|
+
"""
|
|
1808
|
+
res = handle_dsgrid_exception(
|
|
1809
|
+
ctx,
|
|
1810
|
+
bulk_register,
|
|
1811
|
+
registry_manager,
|
|
1812
|
+
registration_file,
|
|
1813
|
+
data_base_dir=data_base_dir,
|
|
1814
|
+
missing_associations_base_dir=missing_associations_base_dir,
|
|
1815
|
+
repo_base_dir=repo_base_dir,
|
|
1816
|
+
journal_file=journal_file,
|
|
1817
|
+
)
|
|
1818
|
+
if res[1] != 0:
|
|
1819
|
+
ctx.exit(res[1])
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
dimensions.add_command(list_dimensions)
|
|
1823
|
+
dimensions.add_command(register_dimensions)
|
|
1824
|
+
dimensions.add_command(dump_dimension)
|
|
1825
|
+
dimensions.add_command(show_dimension)
|
|
1826
|
+
dimensions.add_command(update_dimension)
|
|
1827
|
+
dimensions.add_command(remove_dimension)
|
|
1828
|
+
|
|
1829
|
+
dimension_mappings.add_command(list_dimension_mappings)
|
|
1830
|
+
dimension_mappings.add_command(register_dimension_mappings)
|
|
1831
|
+
dimension_mappings.add_command(dump_dimension_mapping)
|
|
1832
|
+
dimension_mappings.add_command(show_dimension_mapping)
|
|
1833
|
+
dimension_mappings.add_command(update_dimension_mapping)
|
|
1834
|
+
dimension_mappings.add_command(remove_dimension_mapping)
|
|
1835
|
+
|
|
1836
|
+
projects.add_command(list_projects)
|
|
1837
|
+
projects.add_command(register_project)
|
|
1838
|
+
projects.add_command(submit_dataset)
|
|
1839
|
+
projects.add_command(register_and_submit_dataset)
|
|
1840
|
+
projects.add_command(dump_project)
|
|
1841
|
+
projects.add_command(update_project)
|
|
1842
|
+
projects.add_command(register_subset_dimensions)
|
|
1843
|
+
projects.add_command(register_supplemental_dimensions)
|
|
1844
|
+
projects.add_command(add_dataset_requirements)
|
|
1845
|
+
projects.add_command(replace_dataset_dimension_requirements)
|
|
1846
|
+
projects.add_command(list_project_dimension_names)
|
|
1847
|
+
projects.add_command(generate_project_config_from_ids)
|
|
1848
|
+
projects.add_command(remove_project)
|
|
1849
|
+
|
|
1850
|
+
datasets.add_command(list_datasets)
|
|
1851
|
+
datasets.add_command(register_dataset)
|
|
1852
|
+
datasets.add_command(dump_dataset)
|
|
1853
|
+
datasets.add_command(update_dataset)
|
|
1854
|
+
datasets.add_command(generate_dataset_config_from_dataset)
|
|
1855
|
+
datasets.add_command(remove_datasets)
|
|
1856
|
+
|
|
1857
|
+
registry.add_command(list_)
|
|
1858
|
+
registry.add_command(dimensions)
|
|
1859
|
+
registry.add_command(dimension_mappings)
|
|
1860
|
+
registry.add_command(projects)
|
|
1861
|
+
registry.add_command(datasets)
|
|
1862
|
+
registry.add_command(bulk_register_cli)
|