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.
Files changed (157) hide show
  1. build_backend.py +93 -0
  2. dsgrid/__init__.py +22 -0
  3. dsgrid/api/__init__.py +0 -0
  4. dsgrid/api/api_manager.py +179 -0
  5. dsgrid/api/app.py +419 -0
  6. dsgrid/api/models.py +60 -0
  7. dsgrid/api/response_models.py +116 -0
  8. dsgrid/apps/__init__.py +0 -0
  9. dsgrid/apps/project_viewer/app.py +216 -0
  10. dsgrid/apps/registration_gui.py +444 -0
  11. dsgrid/chronify.py +32 -0
  12. dsgrid/cli/__init__.py +0 -0
  13. dsgrid/cli/common.py +120 -0
  14. dsgrid/cli/config.py +176 -0
  15. dsgrid/cli/download.py +13 -0
  16. dsgrid/cli/dsgrid.py +157 -0
  17. dsgrid/cli/dsgrid_admin.py +92 -0
  18. dsgrid/cli/install_notebooks.py +62 -0
  19. dsgrid/cli/query.py +729 -0
  20. dsgrid/cli/registry.py +1862 -0
  21. dsgrid/cloud/__init__.py +0 -0
  22. dsgrid/cloud/cloud_storage_interface.py +140 -0
  23. dsgrid/cloud/factory.py +31 -0
  24. dsgrid/cloud/fake_storage_interface.py +37 -0
  25. dsgrid/cloud/s3_storage_interface.py +156 -0
  26. dsgrid/common.py +36 -0
  27. dsgrid/config/__init__.py +0 -0
  28. dsgrid/config/annual_time_dimension_config.py +194 -0
  29. dsgrid/config/common.py +142 -0
  30. dsgrid/config/config_base.py +148 -0
  31. dsgrid/config/dataset_config.py +907 -0
  32. dsgrid/config/dataset_schema_handler_factory.py +46 -0
  33. dsgrid/config/date_time_dimension_config.py +136 -0
  34. dsgrid/config/dimension_config.py +54 -0
  35. dsgrid/config/dimension_config_factory.py +65 -0
  36. dsgrid/config/dimension_mapping_base.py +350 -0
  37. dsgrid/config/dimension_mappings_config.py +48 -0
  38. dsgrid/config/dimensions.py +1025 -0
  39. dsgrid/config/dimensions_config.py +71 -0
  40. dsgrid/config/file_schema.py +190 -0
  41. dsgrid/config/index_time_dimension_config.py +80 -0
  42. dsgrid/config/input_dataset_requirements.py +31 -0
  43. dsgrid/config/mapping_tables.py +209 -0
  44. dsgrid/config/noop_time_dimension_config.py +42 -0
  45. dsgrid/config/project_config.py +1462 -0
  46. dsgrid/config/registration_models.py +188 -0
  47. dsgrid/config/representative_period_time_dimension_config.py +194 -0
  48. dsgrid/config/simple_models.py +49 -0
  49. dsgrid/config/supplemental_dimension.py +29 -0
  50. dsgrid/config/time_dimension_base_config.py +192 -0
  51. dsgrid/data_models.py +155 -0
  52. dsgrid/dataset/__init__.py +0 -0
  53. dsgrid/dataset/dataset.py +123 -0
  54. dsgrid/dataset/dataset_expression_handler.py +86 -0
  55. dsgrid/dataset/dataset_mapping_manager.py +121 -0
  56. dsgrid/dataset/dataset_schema_handler_base.py +945 -0
  57. dsgrid/dataset/dataset_schema_handler_one_table.py +209 -0
  58. dsgrid/dataset/dataset_schema_handler_two_table.py +322 -0
  59. dsgrid/dataset/growth_rates.py +162 -0
  60. dsgrid/dataset/models.py +51 -0
  61. dsgrid/dataset/table_format_handler_base.py +257 -0
  62. dsgrid/dataset/table_format_handler_factory.py +17 -0
  63. dsgrid/dataset/unpivoted_table.py +121 -0
  64. dsgrid/dimension/__init__.py +0 -0
  65. dsgrid/dimension/base_models.py +230 -0
  66. dsgrid/dimension/dimension_filters.py +308 -0
  67. dsgrid/dimension/standard.py +252 -0
  68. dsgrid/dimension/time.py +352 -0
  69. dsgrid/dimension/time_utils.py +103 -0
  70. dsgrid/dsgrid_rc.py +88 -0
  71. dsgrid/exceptions.py +105 -0
  72. dsgrid/filesystem/__init__.py +0 -0
  73. dsgrid/filesystem/cloud_filesystem.py +32 -0
  74. dsgrid/filesystem/factory.py +32 -0
  75. dsgrid/filesystem/filesystem_interface.py +136 -0
  76. dsgrid/filesystem/local_filesystem.py +74 -0
  77. dsgrid/filesystem/s3_filesystem.py +118 -0
  78. dsgrid/loggers.py +132 -0
  79. dsgrid/minimal_patterns.cp313-win_amd64.pyd +0 -0
  80. dsgrid/notebooks/connect_to_dsgrid_registry.ipynb +949 -0
  81. dsgrid/notebooks/registration.ipynb +48 -0
  82. dsgrid/notebooks/start_notebook.sh +11 -0
  83. dsgrid/project.py +451 -0
  84. dsgrid/query/__init__.py +0 -0
  85. dsgrid/query/dataset_mapping_plan.py +142 -0
  86. dsgrid/query/derived_dataset.py +388 -0
  87. dsgrid/query/models.py +728 -0
  88. dsgrid/query/query_context.py +287 -0
  89. dsgrid/query/query_submitter.py +994 -0
  90. dsgrid/query/report_factory.py +19 -0
  91. dsgrid/query/report_peak_load.py +70 -0
  92. dsgrid/query/reports_base.py +20 -0
  93. dsgrid/registry/__init__.py +0 -0
  94. dsgrid/registry/bulk_register.py +165 -0
  95. dsgrid/registry/common.py +287 -0
  96. dsgrid/registry/config_update_checker_base.py +63 -0
  97. dsgrid/registry/data_store_factory.py +34 -0
  98. dsgrid/registry/data_store_interface.py +74 -0
  99. dsgrid/registry/dataset_config_generator.py +158 -0
  100. dsgrid/registry/dataset_registry_manager.py +950 -0
  101. dsgrid/registry/dataset_update_checker.py +16 -0
  102. dsgrid/registry/dimension_mapping_registry_manager.py +575 -0
  103. dsgrid/registry/dimension_mapping_update_checker.py +16 -0
  104. dsgrid/registry/dimension_registry_manager.py +413 -0
  105. dsgrid/registry/dimension_update_checker.py +16 -0
  106. dsgrid/registry/duckdb_data_store.py +207 -0
  107. dsgrid/registry/filesystem_data_store.py +150 -0
  108. dsgrid/registry/filter_registry_manager.py +123 -0
  109. dsgrid/registry/project_config_generator.py +57 -0
  110. dsgrid/registry/project_registry_manager.py +1623 -0
  111. dsgrid/registry/project_update_checker.py +48 -0
  112. dsgrid/registry/registration_context.py +223 -0
  113. dsgrid/registry/registry_auto_updater.py +316 -0
  114. dsgrid/registry/registry_database.py +667 -0
  115. dsgrid/registry/registry_interface.py +446 -0
  116. dsgrid/registry/registry_manager.py +558 -0
  117. dsgrid/registry/registry_manager_base.py +367 -0
  118. dsgrid/registry/versioning.py +92 -0
  119. dsgrid/rust_ext/__init__.py +14 -0
  120. dsgrid/rust_ext/find_minimal_patterns.py +129 -0
  121. dsgrid/spark/__init__.py +0 -0
  122. dsgrid/spark/functions.py +589 -0
  123. dsgrid/spark/types.py +110 -0
  124. dsgrid/tests/__init__.py +0 -0
  125. dsgrid/tests/common.py +140 -0
  126. dsgrid/tests/make_us_data_registry.py +265 -0
  127. dsgrid/tests/register_derived_datasets.py +103 -0
  128. dsgrid/tests/utils.py +25 -0
  129. dsgrid/time/__init__.py +0 -0
  130. dsgrid/time/time_conversions.py +80 -0
  131. dsgrid/time/types.py +67 -0
  132. dsgrid/units/__init__.py +0 -0
  133. dsgrid/units/constants.py +113 -0
  134. dsgrid/units/convert.py +71 -0
  135. dsgrid/units/energy.py +145 -0
  136. dsgrid/units/power.py +87 -0
  137. dsgrid/utils/__init__.py +0 -0
  138. dsgrid/utils/dataset.py +830 -0
  139. dsgrid/utils/files.py +179 -0
  140. dsgrid/utils/filters.py +125 -0
  141. dsgrid/utils/id_remappings.py +100 -0
  142. dsgrid/utils/py_expression_eval/LICENSE +19 -0
  143. dsgrid/utils/py_expression_eval/README.md +8 -0
  144. dsgrid/utils/py_expression_eval/__init__.py +847 -0
  145. dsgrid/utils/py_expression_eval/tests.py +283 -0
  146. dsgrid/utils/run_command.py +70 -0
  147. dsgrid/utils/scratch_dir_context.py +65 -0
  148. dsgrid/utils/spark.py +918 -0
  149. dsgrid/utils/spark_partition.py +98 -0
  150. dsgrid/utils/timing.py +239 -0
  151. dsgrid/utils/utilities.py +221 -0
  152. dsgrid/utils/versioning.py +36 -0
  153. dsgrid_toolkit-0.3.3.dist-info/METADATA +193 -0
  154. dsgrid_toolkit-0.3.3.dist-info/RECORD +157 -0
  155. dsgrid_toolkit-0.3.3.dist-info/WHEEL +4 -0
  156. dsgrid_toolkit-0.3.3.dist-info/entry_points.txt +4 -0
  157. dsgrid_toolkit-0.3.3.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,446 @@
1
+ import abc
2
+ import itertools
3
+ import logging
4
+ from typing import Any, Generator
5
+
6
+ import pandas as pd
7
+ from sqlalchemy import Connection, Engine
8
+
9
+ from dsgrid.config.dataset_config import DatasetConfigModel
10
+ from dsgrid.config.dimension_mapping_base import DimensionMappingBaseModel
11
+ from dsgrid.config.dimensions import DimensionBaseModel, handle_dimension_union
12
+ from dsgrid.config.mapping_tables import MappingTableModel
13
+ from dsgrid.config.project_config import ProjectConfigModel
14
+ from dsgrid.data_models import DSGBaseDatabaseModel, DSGBaseModel
15
+ from dsgrid.registry.common import (
16
+ DatasetRegistryStatus,
17
+ MODEL_TYPE_TO_ID_FIELD_MAPPING,
18
+ RegistrationModel,
19
+ RegistryType,
20
+ )
21
+ from dsgrid.registry.registry_database import RegistryDatabase
22
+
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class RegistryInterfaceBase(abc.ABC):
28
+ """Interface base class"""
29
+
30
+ def __init__(self, db: RegistryDatabase):
31
+ self._db = db
32
+
33
+ @property
34
+ def engine(self) -> Engine:
35
+ """Return the sqlalchemy engine."""
36
+ return self._db.engine
37
+
38
+ @staticmethod
39
+ @abc.abstractmethod
40
+ def _model_type() -> RegistryType:
41
+ """Return the model type."""
42
+
43
+ @abc.abstractmethod
44
+ def _get_model_id(self, model) -> str:
45
+ """Return the dsgrid ID for the model."""
46
+
47
+ @abc.abstractmethod
48
+ def _get_model_id_field(self):
49
+ """Return the field representing the model ID for the model."""
50
+
51
+ def _insert_contains_edges(self, conn: Connection, model) -> None:
52
+ """Add contains edges to any dependent documents."""
53
+
54
+ @staticmethod
55
+ @abc.abstractmethod
56
+ def _make_dsgrid_model(db_data: dict) -> Any:
57
+ """Convert the database object into a dsgrid model."""
58
+
59
+ def delete_all(self, conn: Connection | None, model_id: str) -> None:
60
+ """Delete all database instances with model_id."""
61
+ if conn is None:
62
+ with self._db.engine.begin() as conn:
63
+ return self._db.delete_models(conn, self._model_type(), model_id)
64
+ return self._db.delete_models(conn, self._model_type(), model_id)
65
+
66
+ def get_by_version(self, conn: Connection | None, model_id, version) -> DSGBaseDatabaseModel:
67
+ """Return the model by version"""
68
+ return self._make_dsgrid_model(self._get_by_version(conn, model_id, version))
69
+
70
+ def get_latest(self, conn: Connection | None, model_id) -> DSGBaseModel:
71
+ """Return the model with the latest version."""
72
+ return self._make_dsgrid_model(self._get_latest(conn, model_id))
73
+
74
+ def get_latest_version(self, conn: Connection | None, model_id: str) -> str:
75
+ """Return the latest version of the model_id.
76
+
77
+ Parameters
78
+ ----------
79
+ model_id : str
80
+ dsgrid identifier for a model item
81
+
82
+ Returns
83
+ -------
84
+ str
85
+ """
86
+ if conn is None:
87
+ with self._db.engine.connect() as conn:
88
+ return self._db.get_latest_version(conn, self._model_type(), model_id)
89
+ return self._db.get_latest_version(conn, self._model_type(), model_id)
90
+
91
+ def get_registration(
92
+ self, conn: Connection | None, model: DSGBaseDatabaseModel
93
+ ) -> RegistrationModel:
94
+ """Return the registration information for the model."""
95
+ if conn is None:
96
+ with self._db.engine.connect() as conn:
97
+ return self._db.get_registration(conn, model.id)
98
+ return self._db.get_registration(conn, model.id)
99
+
100
+ def get_initial_registration(
101
+ self, conn: Connection | None, model_id: str
102
+ ) -> RegistrationModel:
103
+ """Return the initial registration information for the model."""
104
+ if conn is None:
105
+ with self._db.engine.connect() as conn:
106
+ return self._db.get_initial_registration(conn, self._model_type(), model_id)
107
+ return self._db.get_initial_registration(conn, self._model_type(), model_id)
108
+
109
+ def has(self, conn: Connection | None, model_id: str, version: str | None = None) -> bool:
110
+ """Return True if the model_id is stored in the database."""
111
+ if conn is None:
112
+ with self._db.engine.connect() as conn:
113
+ return self._db.has(conn, self._model_type(), model_id, version=version)
114
+ return self._db.has(conn, self._model_type(), model_id, version=version)
115
+
116
+ def insert(
117
+ self,
118
+ conn: Connection | None,
119
+ model: DSGBaseDatabaseModel,
120
+ registration: RegistrationModel,
121
+ ) -> DSGBaseModel:
122
+ """Add a new model in the database."""
123
+ if conn is None:
124
+ with self._db.engine.begin() as conn:
125
+ return self._insert(conn, model, registration)
126
+ return self._insert(conn, model, registration)
127
+
128
+ def _insert(
129
+ self, conn: Connection, model: DSGBaseDatabaseModel, registration: RegistrationModel
130
+ ) -> DSGBaseModel:
131
+ new_model = self._make_dsgrid_model(
132
+ self._db.insert_model(
133
+ conn, self._model_type(), model.model_dump(mode="json"), registration
134
+ )
135
+ )
136
+ self._insert_contains_edges(conn, new_model)
137
+ return new_model
138
+
139
+ def insert_registration(
140
+ self, conn: Connection | None, registration: RegistrationModel
141
+ ) -> RegistrationModel:
142
+ """Add the registration to the database.
143
+
144
+ Returns
145
+ -------
146
+ database ID of registration entry
147
+ """
148
+ if conn is None:
149
+ with self._db.engine.begin() as conn:
150
+ return self._db.insert_registration(conn, registration)
151
+ return self._db.insert_registration(conn, registration)
152
+
153
+ def list_model_ids(self, conn: Connection | None = None) -> list[str]:
154
+ """Return a list of all distinct dsgrid model IDs."""
155
+ if conn is None:
156
+ with self._db.engine.connect() as conn:
157
+ return self._db.list_model_ids(conn, self._model_type())
158
+ return self._db.list_model_ids(conn, self._model_type())
159
+
160
+ def iter_models(
161
+ self,
162
+ conn: Connection | None = None,
163
+ all_versions: bool = False,
164
+ filter_config: dict[str, Any] | None = None,
165
+ ) -> Generator[DSGBaseDatabaseModel, None, None]:
166
+ """Return a generator of dsgrid models converted from database objects.
167
+
168
+ Parameters
169
+ ----------
170
+ all_versions : bool
171
+ If False, return only the latest version of each model in the registry of this type.
172
+ If True, return all versions of each model (such as all versions of every project).
173
+ filter_config : None | dict
174
+ If set, it must be a dict of model field names to values. Only return models that
175
+ match the filter
176
+ """
177
+ if conn is None:
178
+ with self._db.engine.connect() as conn:
179
+ yield from self._iter_models(
180
+ conn, all_versions=all_versions, filter_config=filter_config
181
+ )
182
+ else:
183
+ yield from self._iter_models(
184
+ conn, all_versions=all_versions, filter_config=filter_config
185
+ )
186
+
187
+ def _iter_models(
188
+ self,
189
+ conn: Connection,
190
+ all_versions: bool = False,
191
+ filter_config: dict[str, Any] | None = None,
192
+ ) -> Generator[DSGBaseDatabaseModel, None, None]:
193
+ for data in self._db.iter_models(conn, self._model_type(), all_versions=all_versions):
194
+ model = self._make_dsgrid_model(data)
195
+ if filter_config is None or self._does_filter_match(model, filter_config):
196
+ yield self._make_dsgrid_model(data)
197
+
198
+ def get_containing_models(
199
+ self,
200
+ conn: Connection | None,
201
+ model: DSGBaseDatabaseModel,
202
+ version: str | None = None,
203
+ parent_model_type: RegistryType | None = None,
204
+ ) -> dict[RegistryType, list[DSGBaseDatabaseModel]]:
205
+ """Return all models that contain the given model. If version is not set, use the current
206
+ version of model. Only looks at the latest version of each parent model.
207
+ """
208
+ version_ = version or model.version
209
+ if isinstance(model, ProjectConfigModel):
210
+ model_type = RegistryType.PROJECT
211
+ elif isinstance(model, DatasetConfigModel):
212
+ model_type = RegistryType.DATASET
213
+ elif isinstance(model, DimensionBaseModel):
214
+ model_type = RegistryType.DIMENSION
215
+ elif isinstance(model, DimensionMappingBaseModel):
216
+ model_type = RegistryType.DIMENSION_MAPPING
217
+ else:
218
+ msg = str(type(model))
219
+ raise NotImplementedError(msg)
220
+ model_id = getattr(model, MODEL_TYPE_TO_ID_FIELD_MAPPING[model_type])
221
+ if conn is None:
222
+ with self._db.engine.connect() as conn:
223
+ return self._get_containing_models(
224
+ conn, model_type, model_id, version_, parent_model_type=parent_model_type
225
+ )
226
+ return self._get_containing_models(
227
+ conn, model_type, model_id, version_, parent_model_type=parent_model_type
228
+ )
229
+
230
+ def _get_containing_models(
231
+ self,
232
+ conn: Connection,
233
+ child_model_type: RegistryType,
234
+ model_id: str,
235
+ version: str,
236
+ parent_model_type: RegistryType | None = None,
237
+ ) -> dict[RegistryType, list[DSGBaseDatabaseModel]]:
238
+ results = {x: [] for x in RegistryType}
239
+ for model_type, data in self._db.get_containing_models(
240
+ conn, child_model_type, model_id, version, parent_model_type=parent_model_type
241
+ ):
242
+ model = _INTERFACE_BY_TYPE[model_type]._make_dsgrid_model(data)
243
+ results[model_type].append(model)
244
+ return results
245
+
246
+ @staticmethod
247
+ def _does_filter_match(model: DSGBaseDatabaseModel, filter_config):
248
+ for key, val in filter_config.items():
249
+ if getattr(model, key) != val:
250
+ return False
251
+ return True
252
+
253
+ def replace(self, conn: Connection | None, model: DSGBaseDatabaseModel):
254
+ """Replace an existing model in the database."""
255
+ if conn is None:
256
+ with self._db.engine.begin() as conn:
257
+ return self._replace(conn, model)
258
+ return self._replace(conn, model)
259
+
260
+ def _replace(self, conn: Connection, model: DSGBaseDatabaseModel):
261
+ return self._db.replace_model(conn, model.model_dump(mode="json"))
262
+
263
+ def update(
264
+ self,
265
+ conn: Connection | None,
266
+ model: DSGBaseDatabaseModel,
267
+ registration: RegistrationModel,
268
+ ) -> DSGBaseDatabaseModel:
269
+ """Update an existing model in the database."""
270
+ if conn is None:
271
+ with self._db.engine.begin() as conn:
272
+ return self._update(conn, model, registration)
273
+ return self._update(conn, model, registration)
274
+
275
+ def _update(
276
+ self, conn: Connection, model: DSGBaseDatabaseModel, registration: RegistrationModel
277
+ ) -> DSGBaseDatabaseModel:
278
+ new_model = self._make_dsgrid_model(
279
+ self._db.update_model(
280
+ conn, self._model_type(), model.model_dump(mode="json"), registration
281
+ )
282
+ )
283
+ self._insert_contains_edges(conn, new_model)
284
+ return new_model
285
+
286
+ def _get_by_version(self, conn: Connection | None, model_id, version) -> dict[str, Any]:
287
+ if conn is None:
288
+ with self._db.engine.connect() as conn:
289
+ return self._db._get_by_version(conn, self._model_type(), model_id, version)
290
+ return self._db._get_by_version(conn, self._model_type(), model_id, version)
291
+
292
+ def _get_latest(self, conn: Connection | None, model_id: str) -> dict[str, Any]:
293
+ if conn is None:
294
+ with self._db.engine.connect() as conn:
295
+ return self._db.get_latest(conn, self._model_type(), model_id)
296
+ return self._db.get_latest(conn, self._model_type(), model_id)
297
+
298
+ @property
299
+ def datasets(self) -> Generator[dict[str, Any], None, None]:
300
+ with self.engine.connect() as conn:
301
+ yield from self._db.iter_models(conn, RegistryType.DATASET)
302
+
303
+ @property
304
+ def dimensions(self) -> Generator[dict[str, Any], None, None]:
305
+ with self.engine.connect() as conn:
306
+ yield from self._db.iter_models(conn, RegistryType.DIMENSION)
307
+
308
+ @property
309
+ def dimension_mappings(self) -> Generator[dict[str, Any], None, None]:
310
+ with self.engine.connect() as conn:
311
+ yield from self._db.iter_models(conn, RegistryType.DIMENSION_MAPPING)
312
+
313
+ @property
314
+ def projects(self) -> Generator[dict[str, Any], None, None]:
315
+ with self.engine.connect() as conn:
316
+ yield from self._db.iter_models(conn, RegistryType.PROJECT)
317
+
318
+ def sql(
319
+ self, query: str, params: Any | None = None, conn: Connection | None = None
320
+ ) -> pd.DataFrame:
321
+ """Return the results of a query as a Pandas DataFrame."""
322
+ if conn is None:
323
+ with self.engine.connect() as conn:
324
+ return pd.read_sql(query, con=conn, params=params)
325
+ return pd.read_sql(query, con=conn, params=params)
326
+
327
+
328
+ class DatasetRegistryInterface(RegistryInterfaceBase):
329
+ """Interface to database for datasets"""
330
+
331
+ @staticmethod
332
+ def _model_type() -> RegistryType:
333
+ return RegistryType.DATASET
334
+
335
+ def _get_model_id(self, model):
336
+ return model.dataset_id
337
+
338
+ def _get_model_id_field(self):
339
+ return "dataset_id"
340
+
341
+ @staticmethod
342
+ def _make_dsgrid_model(db_data: dict):
343
+ return DatasetConfigModel(**db_data)
344
+
345
+ def _insert_contains_edges(self, conn: Connection, model):
346
+ dim_intf = DimensionRegistryInterface(self._db)
347
+ for ref in model.dimension_references:
348
+ dim = dim_intf.get_by_version(conn, ref.dimension_id, ref.version)
349
+ self._db.insert_contains_edge(conn, model.id, dim.id)
350
+
351
+
352
+ class DimensionRegistryInterface(RegistryInterfaceBase):
353
+ """Interface to database for dimensions"""
354
+
355
+ @staticmethod
356
+ def _model_type() -> RegistryType:
357
+ return RegistryType.DIMENSION
358
+
359
+ def _get_model_id(self, model):
360
+ return model.dimension_id
361
+
362
+ def _get_model_id_field(self):
363
+ return "dimension_id"
364
+
365
+ @staticmethod
366
+ def _make_dsgrid_model(db_data: dict):
367
+ return handle_dimension_union([db_data])[0]
368
+
369
+
370
+ class DimensionMappingRegistryInterface(RegistryInterfaceBase):
371
+ """Interface to database for dimension mappings"""
372
+
373
+ @staticmethod
374
+ def _model_type() -> RegistryType:
375
+ return RegistryType.DIMENSION_MAPPING
376
+
377
+ def _get_model_id(self, model):
378
+ return model.mapping_id
379
+
380
+ def _get_model_id_field(self):
381
+ return "mapping_id"
382
+
383
+ @staticmethod
384
+ def _make_dsgrid_model(db_data: dict[str, Any]):
385
+ return MappingTableModel(**db_data)
386
+
387
+ def _insert_contains_edges(self, conn, model):
388
+ dim_intf = DimensionRegistryInterface(self._db)
389
+ for ref in (model.from_dimension, model.to_dimension):
390
+ dim = dim_intf.get_by_version(conn, ref.dimension_id, ref.version)
391
+ self._db.insert_contains_edge(conn, model.id, dim.id)
392
+
393
+
394
+ class ProjectRegistryInterface(RegistryInterfaceBase):
395
+ """Interface to database for projects"""
396
+
397
+ @staticmethod
398
+ def _model_type() -> RegistryType:
399
+ return RegistryType.PROJECT
400
+
401
+ def _get_model_id(self, model):
402
+ return model.project_id
403
+
404
+ def _get_model_id_field(self):
405
+ return "project_id"
406
+
407
+ @staticmethod
408
+ def _make_dsgrid_model(db_data: dict):
409
+ return ProjectConfigModel(**db_data)
410
+
411
+ def _insert_contains_edges(self, conn: Connection, model):
412
+ dim_intf = DimensionRegistryInterface(self._db)
413
+ dim_mapping_intf = DimensionMappingRegistryInterface(self._db)
414
+ dataset_intf = DatasetRegistryInterface(self._db)
415
+
416
+ for ref in itertools.chain(
417
+ model.dimensions.base_dimension_references,
418
+ model.dimensions.supplemental_dimension_references,
419
+ ):
420
+ dim = dim_intf.get_by_version(conn, ref.dimension_id, ref.version)
421
+ self._db.insert_contains_edge(conn, model.id, dim.id)
422
+
423
+ for ref in model.dimension_mappings.base_to_supplemental_references:
424
+ mapping = dim_mapping_intf.get_by_version(conn, ref.mapping_id, ref.version)
425
+ self._db.insert_contains_edge(conn, model.id, mapping.id)
426
+
427
+ for dataset in model.datasets:
428
+ if dataset.status == DatasetRegistryStatus.REGISTERED.value:
429
+ dset = dataset_intf.get_by_version(conn, dataset.dataset_id, dataset.version)
430
+ self._db.insert_contains_edge(conn, model.id, dset.id)
431
+
432
+ def add_contains_dataset(
433
+ self, conn: Connection, project: ProjectConfigModel, dataset: DatasetConfigModel
434
+ ) -> None:
435
+ assert project.id is not None
436
+ assert dataset.id is not None
437
+ self._db.insert_contains_edge(conn, project.id, dataset.id)
438
+
439
+
440
+ _INTERFACE_BY_TYPE = {
441
+ RegistryType.PROJECT: ProjectRegistryInterface,
442
+ RegistryType.DATASET: DatasetRegistryInterface,
443
+ RegistryType.DIMENSION: DimensionRegistryInterface,
444
+ RegistryType.DIMENSION_MAPPING: DimensionMappingRegistryInterface,
445
+ }
446
+ assert len(_INTERFACE_BY_TYPE) == len(RegistryType)