gammasimtools 0.22.0__py3-none-any.whl → 0.24.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/METADATA +2 -1
  2. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/RECORD +128 -125
  3. simtools/_version.py +2 -2
  4. simtools/application_control.py +118 -0
  5. simtools/applications/calculate_incident_angles.py +17 -22
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +28 -43
  7. simtools/applications/convert_geo_coordinates_of_array_elements.py +26 -45
  8. simtools/applications/convert_model_parameter_from_simtel.py +21 -41
  9. simtools/applications/db_add_file_to_db.py +13 -14
  10. simtools/applications/db_add_simulation_model_from_repository_to_db.py +20 -33
  11. simtools/applications/db_add_value_from_json_to_db.py +29 -24
  12. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +20 -35
  13. simtools/applications/db_generate_compound_indexes.py +11 -13
  14. simtools/applications/db_get_array_layouts_from_db.py +20 -40
  15. simtools/applications/db_get_file_from_db.py +15 -17
  16. simtools/applications/db_get_parameter_from_db.py +33 -35
  17. simtools/applications/db_inspect_databases.py +13 -12
  18. simtools/applications/db_upload_model_repository.py +13 -31
  19. simtools/applications/derive_ctao_array_layouts.py +16 -21
  20. simtools/applications/derive_mirror_rnda.py +9 -14
  21. simtools/applications/derive_photon_electron_spectrum.py +7 -10
  22. simtools/applications/derive_psf_parameters.py +13 -20
  23. simtools/applications/derive_trigger_rates.py +6 -9
  24. simtools/applications/docs_produce_array_element_report.py +22 -23
  25. simtools/applications/docs_produce_calibration_reports.py +26 -24
  26. simtools/applications/docs_produce_model_parameter_reports.py +15 -22
  27. simtools/applications/docs_produce_simulation_configuration_report.py +21 -22
  28. simtools/applications/generate_array_config.py +14 -33
  29. simtools/applications/generate_corsika_histograms.py +22 -43
  30. simtools/applications/generate_default_metadata.py +15 -36
  31. simtools/applications/generate_regular_arrays.py +11 -15
  32. simtools/applications/generate_simtel_event_data.py +23 -33
  33. simtools/applications/maintain_simulation_model_add_production.py +20 -37
  34. simtools/applications/maintain_simulation_model_compare_productions.py +10 -12
  35. simtools/applications/maintain_simulation_model_verify_production_tables.py +8 -11
  36. simtools/applications/merge_tables.py +14 -23
  37. simtools/applications/plot_array_layout.py +77 -54
  38. simtools/applications/plot_simtel_events.py +11 -13
  39. simtools/applications/plot_tabular_data.py +17 -38
  40. simtools/applications/plot_tabular_data_for_model_parameter.py +16 -23
  41. simtools/applications/print_version.py +14 -42
  42. simtools/applications/production_derive_corsika_limits.py +5 -9
  43. simtools/applications/production_derive_statistics.py +12 -25
  44. simtools/applications/production_generate_grid.py +20 -48
  45. simtools/applications/production_merge_corsika_limits.py +17 -21
  46. simtools/applications/run_application.py +12 -32
  47. simtools/applications/simulate_flasher.py +21 -25
  48. simtools/applications/simulate_illuminator.py +7 -14
  49. simtools/applications/simulate_pedestals.py +13 -13
  50. simtools/applications/simulate_prod.py +21 -33
  51. simtools/applications/simulate_prod_htcondor_generator.py +11 -25
  52. simtools/applications/submit_array_layouts.py +16 -19
  53. simtools/applications/submit_data_from_external.py +18 -34
  54. simtools/applications/submit_model_parameter_from_external.py +27 -40
  55. simtools/applications/validate_camera_efficiency.py +23 -21
  56. simtools/applications/validate_camera_fov.py +21 -26
  57. simtools/applications/validate_cumulative_psf.py +27 -35
  58. simtools/applications/validate_file_using_schema.py +15 -33
  59. simtools/applications/validate_optics.py +27 -33
  60. simtools/camera/camera_efficiency.py +0 -2
  61. simtools/configuration/commandline_parser.py +39 -13
  62. simtools/configuration/configurator.py +1 -6
  63. simtools/corsika/corsika_config.py +2 -9
  64. simtools/data_model/data_reader.py +0 -2
  65. simtools/data_model/metadata_collector.py +0 -2
  66. simtools/data_model/model_data_writer.py +1 -3
  67. simtools/data_model/schema.py +36 -34
  68. simtools/data_model/validate_data.py +0 -2
  69. simtools/db/db_handler.py +61 -296
  70. simtools/db/db_model_upload.py +1 -1
  71. simtools/db/mongo_db.py +535 -0
  72. simtools/dependencies.py +33 -8
  73. simtools/io/hdf5_handler.py +0 -5
  74. simtools/io/legacy_data_handler.py +0 -5
  75. simtools/job_execution/job_manager.py +0 -3
  76. simtools/layout/array_layout.py +7 -9
  77. simtools/layout/array_layout_utils.py +3 -3
  78. simtools/layout/telescope_position.py +0 -2
  79. simtools/model/array_model.py +38 -71
  80. simtools/model/calibration_model.py +12 -11
  81. simtools/model/camera.py +0 -2
  82. simtools/model/mirrors.py +0 -2
  83. simtools/model/model_parameter.py +200 -140
  84. simtools/model/model_repository.py +159 -35
  85. simtools/model/model_utils.py +3 -8
  86. simtools/model/site_model.py +59 -29
  87. simtools/model/telescope_model.py +21 -15
  88. simtools/production_configuration/calculate_statistical_uncertainties_grid_point.py +0 -2
  89. simtools/production_configuration/derive_production_statistics.py +0 -2
  90. simtools/production_configuration/interpolation_handler.py +0 -2
  91. simtools/ray_tracing/mirror_panel_psf.py +4 -4
  92. simtools/ray_tracing/psf_analysis.py +0 -2
  93. simtools/ray_tracing/psf_parameter_optimisation.py +1 -1
  94. simtools/ray_tracing/ray_tracing.py +0 -2
  95. simtools/reporting/docs_auto_report_generator.py +109 -1
  96. simtools/reporting/docs_read_parameters.py +4 -9
  97. simtools/runners/corsika_runner.py +0 -2
  98. simtools/runners/corsika_simtel_runner.py +0 -2
  99. simtools/runners/simtel_runner.py +0 -2
  100. simtools/schemas/model_parameters/transit_time_random.schema.yml +29 -0
  101. simtools/schemas/simulation_models_info.schema.yml +2 -1
  102. simtools/simtel/simtel_config_reader.py +0 -2
  103. simtools/simtel/simtel_config_writer.py +128 -33
  104. simtools/simtel/simtel_io_metadata.py +3 -3
  105. simtools/simtel/simulator_array.py +9 -21
  106. simtools/simtel/simulator_camera_efficiency.py +0 -2
  107. simtools/simtel/simulator_light_emission.py +1 -3
  108. simtools/simtel/simulator_ray_tracing.py +0 -2
  109. simtools/simulator.py +2 -6
  110. simtools/testing/assertions.py +52 -8
  111. simtools/testing/configuration.py +17 -4
  112. simtools/testing/validate_output.py +4 -8
  113. simtools/utils/general.py +5 -13
  114. simtools/utils/geometry.py +0 -5
  115. simtools/utils/names.py +1 -13
  116. simtools/utils/value_conversion.py +10 -5
  117. simtools/version.py +85 -0
  118. simtools/visualization/plot_array_layout.py +129 -23
  119. simtools/visualization/plot_incident_angles.py +0 -2
  120. simtools/visualization/plot_pixels.py +1 -1
  121. simtools/visualization/plot_psf.py +1 -1
  122. simtools/visualization/plot_simtel_events.py +0 -11
  123. simtools/visualization/plot_tables.py +1 -1
  124. simtools/visualization/visualize.py +0 -12
  125. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/WHEEL +0 -0
  126. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/entry_points.txt +0 -0
  127. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/licenses/LICENSE +0 -0
  128. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/top_level.txt +0 -0
simtools/db/db_handler.py CHANGED
@@ -1,72 +1,16 @@
1
1
  """Module to handle interaction with DB."""
2
2
 
3
- import io
4
3
  import logging
5
- import re
6
4
  from collections import defaultdict
7
5
  from pathlib import Path
8
- from threading import Lock
9
-
10
- import gridfs
11
- import jsonschema
12
- from astropy.table import Table
13
- from bson.objectid import ObjectId
14
- from pymongo import MongoClient
15
6
 
16
7
  from simtools.data_model import validate_data
17
- from simtools.io import ascii_handler, io_handler
8
+ from simtools.db.mongo_db import MongoDBHandler
9
+ from simtools.io import io_handler
18
10
  from simtools.simtel import simtel_table_reader
19
11
  from simtools.utils import names, value_conversion
20
12
  from simtools.version import resolve_version_to_latest_patch
21
13
 
22
- __all__ = ["DatabaseHandler"]
23
-
24
- logging.getLogger("pymongo").setLevel(logging.WARNING)
25
-
26
-
27
- # pylint: disable=unsubscriptable-object
28
- # The above comment is because pylint does not know that DatabaseHandler.db_client is subscriptable
29
-
30
-
31
- jsonschema_db_dict = {
32
- "$schema": "https://json-schema.org/draft/2020-12/schema#",
33
- "type": "object",
34
- "description": "MongoDB configuration",
35
- "properties": {
36
- "db_server": {"type": "string", "description": "DB server address"},
37
- "db_api_port": {
38
- "type": "integer",
39
- "minimum": 1,
40
- "maximum": 65535,
41
- "default": 27017,
42
- "description": "Port to use",
43
- },
44
- "db_api_user": {"type": "string", "description": "API username"},
45
- "db_api_pw": {"type": "string", "description": "Password for the API user"},
46
- "db_api_authentication_database": {
47
- "type": ["string", "null"],
48
- "default": "admin",
49
- "description": "DB with user info (optional)",
50
- },
51
- "db_simulation_model": {
52
- "type": "string",
53
- "description": "Name of simulation model database",
54
- },
55
- "db_simulation_model_version": {
56
- "type": "string",
57
- "description": "Version of simulation model database",
58
- },
59
- },
60
- "required": [
61
- "db_server",
62
- "db_api_port",
63
- "db_api_user",
64
- "db_api_pw",
65
- "db_simulation_model",
66
- "db_simulation_model_version",
67
- ],
68
- }
69
-
70
14
 
71
15
  class DatabaseHandler:
72
16
  """
@@ -74,67 +18,57 @@ class DatabaseHandler:
74
18
 
75
19
  Note the two types of version variables used in this class:
76
20
 
77
- - db_simulation_model_version (from mongo_db_config): version of the simulation model database
21
+ - db_simulation_model_version (from db_config): version of the simulation model database
78
22
  - model_version (from production_tables): version of the model contained in the database
79
23
 
80
24
  Parameters
81
25
  ----------
82
- mongo_db_config: dict
83
- Dictionary with the MongoDB configuration (see jsonschema_db_dict for details).
26
+ db_config: dict
27
+ Dictionary with the DB configuration.
84
28
  """
85
29
 
86
30
  ALLOWED_FILE_EXTENSIONS = [".dat", ".txt", ".lis", ".cfg", ".yml", ".yaml", ".ecsv"]
87
31
 
88
- db_client = None
89
32
  production_table_cached = {}
90
33
  model_parameters_cached = {}
91
34
  model_versions_cached = {}
92
35
 
93
- def __init__(self, mongo_db_config=None):
36
+ def __init__(self, db_config=None):
94
37
  """Initialize the DatabaseHandler class."""
95
38
  self._logger = logging.getLogger(__name__)
96
39
 
97
- self.mongo_db_config = self._validate_mongo_db_config(mongo_db_config)
40
+ self.db_config = MongoDBHandler.validate_db_config(db_config)
98
41
  self.io_handler = io_handler.IOHandler()
99
- self.list_of_collections = {}
42
+ self.mongo_db_handler = MongoDBHandler(db_config) if self.db_config else None
100
43
 
101
- self._set_up_connection()
102
44
  self.db_name = (
103
- self.get_db_name(
104
- db_simulation_model_version=self.mongo_db_config.get("db_simulation_model_version"),
105
- model_name=self.mongo_db_config.get("db_simulation_model"),
45
+ MongoDBHandler.get_db_name(
46
+ db_simulation_model_version=self.db_config.get("db_simulation_model_version"),
47
+ model_name=self.db_config.get("db_simulation_model"),
106
48
  )
107
- if self.mongo_db_config
49
+ if self.db_config
108
50
  else None
109
51
  )
110
52
 
111
- def _set_up_connection(self):
112
- """Open the connection to MongoDB."""
113
- if self.mongo_db_config and DatabaseHandler.db_client is None:
114
- lock = Lock()
115
- with lock:
116
- DatabaseHandler.db_client = self._open_mongo_db()
117
-
118
53
  def get_db_name(self, db_name=None, db_simulation_model_version=None, model_name=None):
119
54
  """Build DB name from configuration."""
120
55
  if db_name:
121
56
  return db_name
122
57
  if db_simulation_model_version and model_name:
123
- return f"{model_name}-{db_simulation_model_version.replace('.', '-')}"
124
- if db_simulation_model_version or model_name:
125
- return None
126
- return None if (db_simulation_model_version or model_name) else self.db_name
58
+ return MongoDBHandler.get_db_name(
59
+ db_simulation_model_version=db_simulation_model_version,
60
+ model_name=model_name,
61
+ )
62
+ if not (db_simulation_model_version or model_name):
63
+ return self.db_name
64
+ return None
127
65
 
128
66
  def print_connection_info(self):
129
67
  """Print the connection information."""
130
- if self.mongo_db_config:
131
- self._logger.info(
132
- f"Connected to MongoDB at {self.mongo_db_config['db_server']}:"
133
- f"{self.mongo_db_config['db_api_port']} "
134
- f"using database: {self.db_name}"
135
- )
68
+ if self.mongo_db_handler:
69
+ self.mongo_db_handler.print_connection_info(self.db_name)
136
70
  else:
137
- self._logger.info("No MongoDB configuration provided.")
71
+ self._logger.info("No database defined.")
138
72
 
139
73
  def is_remote_database(self):
140
74
  """
@@ -147,55 +81,7 @@ class DatabaseHandler:
147
81
  bool
148
82
  True if the database is remote, False otherwise.
149
83
  """
150
- if self.mongo_db_config:
151
- db_server = self.mongo_db_config["db_server"]
152
- domain_pattern = r"^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$"
153
- return bool(re.match(domain_pattern, db_server))
154
- return False
155
-
156
- def _validate_mongo_db_config(self, mongo_db_config):
157
- """Validate the MongoDB configuration."""
158
- if mongo_db_config is None or all(value is None for value in mongo_db_config.values()):
159
- return None
160
- try:
161
- jsonschema.validate(instance=mongo_db_config, schema=jsonschema_db_dict)
162
- return mongo_db_config
163
- except jsonschema.exceptions.ValidationError as err:
164
- raise ValueError("Invalid MongoDB configuration") from err
165
-
166
- def _open_mongo_db(self):
167
- """
168
- Open a connection to MongoDB and return the client to read/write to the DB with.
169
-
170
- Returns
171
- -------
172
- A PyMongo DB client
173
-
174
- Raises
175
- ------
176
- KeyError
177
- If the DB configuration is invalid
178
- """
179
- direct_connection = self.mongo_db_config["db_server"] in (
180
- "localhost",
181
- "simtools-mongodb",
182
- "mongodb",
183
- )
184
- return MongoClient(
185
- self.mongo_db_config["db_server"],
186
- port=self.mongo_db_config["db_api_port"],
187
- username=self.mongo_db_config["db_api_user"],
188
- password=self.mongo_db_config["db_api_pw"],
189
- authSource=(
190
- self.mongo_db_config.get("db_api_authentication_database")
191
- if self.mongo_db_config.get("db_api_authentication_database")
192
- else "admin"
193
- ),
194
- directConnection=direct_connection,
195
- ssl=not direct_connection,
196
- tlsallowinvalidhostnames=True,
197
- tlsallowinvalidcertificates=True,
198
- )
84
+ return bool(self.mongo_db_handler and self.mongo_db_handler.is_remote_database())
199
85
 
200
86
  def generate_compound_indexes_for_databases(
201
87
  self, db_name, db_simulation_model, db_simulation_model_version
@@ -211,53 +97,10 @@ class DatabaseHandler:
211
97
  Name of the simulation model.
212
98
  db_simulation_model_version: str
213
99
  Version of the simulation model.
214
-
215
- Raises
216
- ------
217
- ValueError
218
- If the requested database is not found.
219
-
220
100
  """
221
- databases = [
222
- d for d in self.db_client.list_database_names() if d not in ("config", "admin", "local")
223
- ]
224
- requested = self.get_db_name(
225
- db_name=db_name,
226
- db_simulation_model_version=db_simulation_model_version,
227
- model_name=db_simulation_model,
101
+ self.mongo_db_handler.generate_compound_indexes_for_databases(
102
+ db_name, db_simulation_model, db_simulation_model_version
228
103
  )
229
- if requested != "all" and requested not in databases:
230
- raise ValueError(
231
- f"Requested database '{requested}' not found. "
232
- f"Following databases are available: {', '.join(databases)}"
233
- )
234
-
235
- databases = databases if requested == "all" else [requested]
236
- for dbs in databases:
237
- self._logger.info(f"Generating compound indexes for database: {dbs}")
238
- self.generate_compound_indexes(db_name=dbs)
239
-
240
- def generate_compound_indexes(self, db_name=None):
241
- """
242
- Generate compound indexes for the MongoDB collections.
243
-
244
- Indexes based on the typical query patterns.
245
- """
246
- db_name = db_name or self.db_name
247
- collection_names = [
248
- "telescopes",
249
- "sites",
250
- "configuration_sim_telarray",
251
- "configuration_corsika",
252
- "calibration_devices",
253
- ]
254
- for collection_name in collection_names:
255
- db_collection = self.get_collection(collection_name, db_name=db_name)
256
- db_collection.create_index(
257
- [("instrument", 1), ("site", 1), ("parameter", 1), ("parameter_version", 1)]
258
- )
259
- db_collection = self.get_collection("production_tables", db_name=db_name)
260
- db_collection.create_index([("collection", 1), ("model_version", 1)])
261
104
 
262
105
  def get_model_parameter(
263
106
  self,
@@ -300,9 +143,7 @@ class DatabaseHandler:
300
143
  model_version = resolve_version_to_latest_patch(
301
144
  model_version, self.get_model_versions(collection_name)
302
145
  )
303
- production_table = self.read_production_table_from_mongo_db(
304
- collection_name, model_version
305
- )
146
+ production_table = self.read_production_table_from_db(collection_name, model_version)
306
147
  array_element_list = self._get_array_element_list(
307
148
  array_element_name, site, production_table, collection_name
308
149
  )
@@ -322,7 +163,7 @@ class DatabaseHandler:
322
163
  query["instrument"] = array_element_name
323
164
  if site:
324
165
  query["site"] = site
325
- return self._read_mongo_db(query=query, collection_name=collection_name)
166
+ return self._read_db(query=query, collection_name=collection_name)
326
167
 
327
168
  def get_model_parameters(self, site, array_element_name, collection, model_version):
328
169
  """
@@ -349,7 +190,7 @@ class DatabaseHandler:
349
190
  model_version = resolve_version_to_latest_patch(
350
191
  model_version, self.get_model_versions(collection)
351
192
  )
352
- production_table = self.read_production_table_from_mongo_db(collection, model_version)
193
+ production_table = self.read_production_table_from_db(collection, model_version)
353
194
  array_element_list = self._get_array_element_list(
354
195
  array_element_name, site, production_table, collection
355
196
  )
@@ -416,7 +257,7 @@ class DatabaseHandler:
416
257
  parameter_version_table = production_table["parameters"][array_element]
417
258
  except KeyError: # allow missing array elements (parameter dict is checked later)
418
259
  return {}
419
- DatabaseHandler.model_parameters_cached[cache_key] = self._read_mongo_db(
260
+ DatabaseHandler.model_parameters_cached[cache_key] = self._read_db(
420
261
  query=self._get_query_from_parameter_version_table(
421
262
  parameter_version_table, array_element, site
422
263
  ),
@@ -441,7 +282,7 @@ class DatabaseHandler:
441
282
  The collection from the DB.
442
283
  """
443
284
  db_name = db_name or self.db_name
444
- return DatabaseHandler.db_client[db_name][collection_name]
285
+ return self.mongo_db_handler.get_collection(collection_name, db_name)
445
286
 
446
287
  def get_collections(self, db_name=None, model_collections_only=False):
447
288
  """
@@ -458,17 +299,10 @@ class DatabaseHandler:
458
299
  -------
459
300
  list
460
301
  List of collection names
461
-
462
302
  """
463
- db_name = db_name or self.db_name
464
- if db_name not in self.list_of_collections:
465
- self.list_of_collections[db_name] = DatabaseHandler.db_client[
466
- db_name
467
- ].list_collection_names()
468
- collections = self.list_of_collections[db_name]
469
- if model_collections_only:
470
- return [collection for collection in collections if not collection.startswith("fs.")]
471
- return collections
303
+ return self.mongo_db_handler.get_collections(
304
+ db_name or self.db_name, model_collections_only
305
+ )
472
306
 
473
307
  def export_model_file(
474
308
  self,
@@ -557,8 +391,8 @@ class DatabaseHandler:
557
391
  if Path(dest).joinpath(file_name).exists():
558
392
  instance_ids[file_name] = "file exists"
559
393
  else:
560
- file_path_instance = self._get_file_mongo_db(db_name, file_name)
561
- self._write_file_from_mongo_to_disk(db_name, dest, file_path_instance)
394
+ file_path_instance = self.mongo_db_handler.get_file_from_db(db_name, file_name)
395
+ self._write_file_from_db_to_disk(db_name, dest, file_path_instance)
562
396
  instance_ids[file_name] = file_path_instance._id # pylint: disable=protected-access
563
397
  return instance_ids
564
398
 
@@ -579,7 +413,7 @@ class DatabaseHandler:
579
413
  query_dict["site"] = site
580
414
  return query_dict
581
415
 
582
- def _read_mongo_db(self, query, collection_name):
416
+ def _read_db(self, query, collection_name):
583
417
  """
584
418
  Query MongoDB.
585
419
 
@@ -599,20 +433,17 @@ class DatabaseHandler:
599
433
  ValueError
600
434
  if query returned no results.
601
435
  """
602
- collection = self.get_collection(collection_name, db_name=self.db_name)
603
- posts = list(collection.find(query))
604
- if not posts:
605
- raise ValueError(
606
- f"The following query for {collection_name} returned zero results: {query} "
607
- )
436
+ posts = self.mongo_db_handler.query_db(query, collection_name, self.db_name)
608
437
  parameters = {}
609
438
  for post in posts:
610
439
  par_now = post["parameter"]
611
440
  parameters[par_now] = post
612
- parameters[par_now]["entry_date"] = ObjectId(post["_id"]).generation_time
441
+ parameters[par_now]["entry_date"] = self.mongo_db_handler.get_entry_date_from_document(
442
+ post
443
+ )
613
444
  return {k: parameters[k] for k in sorted(parameters)}
614
445
 
615
- def read_production_table_from_mongo_db(self, collection_name, model_version):
446
+ def read_production_table_from_db(self, collection_name, model_version):
616
447
  """
617
448
  Read production table for the given collection from MongoDB.
618
449
 
@@ -639,8 +470,7 @@ class DatabaseHandler:
639
470
  pass
640
471
 
641
472
  query = {"model_version": model_version, "collection": collection_name}
642
- collection = self.get_collection("production_tables", db_name=self.db_name)
643
- post = collection.find_one(query)
473
+ post = self.mongo_db_handler.find_one(query, "production_tables", self.db_name)
644
474
  if not post:
645
475
  raise ValueError(f"The following query returned zero results: {query}")
646
476
 
@@ -649,7 +479,7 @@ class DatabaseHandler:
649
479
  "model_version": post["model_version"],
650
480
  "parameters": post["parameters"],
651
481
  "design_model": post.get("design_model", {}),
652
- "entry_date": ObjectId(post["_id"]).generation_time,
482
+ "entry_date": self.mongo_db_handler.get_entry_date_from_document(post),
653
483
  }
654
484
 
655
485
  def get_model_versions(self, collection_name="telescopes"):
@@ -693,7 +523,7 @@ class DatabaseHandler:
693
523
  model_version = resolve_version_to_latest_patch(
694
524
  model_version, self.get_model_versions(collection)
695
525
  )
696
- production_table = self.read_production_table_from_mongo_db(collection, model_version)
526
+ production_table = self.read_production_table_from_db(collection, model_version)
697
527
  return sorted([entry for entry in production_table["parameters"] if "-design" not in entry])
698
528
 
699
529
  def get_design_model(self, model_version, array_element_name, collection="telescopes"):
@@ -718,7 +548,7 @@ class DatabaseHandler:
718
548
  model_version = resolve_version_to_latest_patch(
719
549
  model_version, self.get_model_versions(collection)
720
550
  )
721
- production_table = self.read_production_table_from_mongo_db(collection, model_version)
551
+ production_table = self.read_production_table_from_db(collection, model_version)
722
552
  try:
723
553
  return production_table["design_model"][array_element_name]
724
554
  except KeyError:
@@ -749,7 +579,7 @@ class DatabaseHandler:
749
579
  model_version = resolve_version_to_latest_patch(
750
580
  model_version, self.get_model_versions(collection)
751
581
  )
752
- production_table = self.read_production_table_from_mongo_db(collection, model_version)
582
+ production_table = self.read_production_table_from_db(collection, model_version)
753
583
  all_array_elements = production_table["parameters"]
754
584
  return sorted(
755
585
  [
@@ -805,38 +635,7 @@ class DatabaseHandler:
805
635
  )
806
636
  raise ValueError(f"Unknown simulation software: {simulation_software}")
807
637
 
808
- @staticmethod
809
- def _get_file_mongo_db(db_name, file_name):
810
- """
811
- Extract a file from MongoDB and return GridFS file instance.
812
-
813
- Parameters
814
- ----------
815
- db_name: str
816
- the name of the DB with files of tabulated data
817
- file_name: str
818
- The name of the file requested
819
-
820
- Returns
821
- -------
822
- GridOut
823
- A file instance returned by GridFS find_one
824
-
825
- Raises
826
- ------
827
- FileNotFoundError
828
- If the desired file is not found.
829
-
830
- """
831
- db = DatabaseHandler.db_client[db_name]
832
- file_system = gridfs.GridFS(db)
833
- if file_system.exists({"filename": file_name}):
834
- return file_system.find_one({"filename": file_name})
835
-
836
- raise FileNotFoundError(f"The file {file_name} does not exist in the database {db_name}")
837
-
838
- @staticmethod
839
- def _write_file_from_mongo_to_disk(db_name, path, file):
638
+ def _write_file_from_db_to_disk(self, db_name, path, file):
840
639
  """
841
640
  Extract a file from MongoDB and write it to disk.
842
641
 
@@ -849,10 +648,7 @@ class DatabaseHandler:
849
648
  file: GridOut
850
649
  A file instance returned by GridFS find_one
851
650
  """
852
- db = DatabaseHandler.db_client[db_name]
853
- fs_output = gridfs.GridFSBucket(db)
854
- with open(Path(path).joinpath(file.filename), "wb") as output_file:
855
- fs_output.download_to_stream_by_name(file.filename, output_file)
651
+ self.mongo_db_handler.write_file_from_db_to_disk(db_name, path, file)
856
652
 
857
653
  def get_ecsv_file_as_astropy_table(self, file_name, db_name=None):
858
654
  """
@@ -872,16 +668,9 @@ class DatabaseHandler:
872
668
  astropy.table.Table
873
669
  The contents of the ECSV file as an Astropy Table.
874
670
  """
875
- db = DatabaseHandler.db_client[db_name or self.db_name]
876
- fs = gridfs.GridFSBucket(db)
877
-
878
- buf = io.BytesIO()
879
- try:
880
- fs.download_to_stream_by_name(file_name, buf)
881
- except gridfs.errors.NoFile as exc:
882
- raise FileNotFoundError(f"ECSV file '{file_name}' not found in DB.") from exc
883
- buf.seek(0)
884
- return Table.read(buf.getvalue().decode("utf-8"), format="ascii.ecsv")
671
+ return self.mongo_db_handler.get_ecsv_file_as_astropy_table(
672
+ file_name, db_name or self.db_name
673
+ )
885
674
 
886
675
  def add_production_table(self, production_table, db_name=None):
887
676
  """
@@ -894,10 +683,10 @@ class DatabaseHandler:
894
683
  db_name: str
895
684
  the name of the DB.
896
685
  """
897
- db_name = db_name or self.db_name
898
- collection = self.get_collection("production_tables", db_name=db_name or self.db_name)
899
686
  self._logger.debug(f"Adding production for {production_table.get('collection')} to the DB")
900
- collection.insert_one(production_table)
687
+ self.mongo_db_handler.insert_one(
688
+ production_table, "production_tables", db_name or self.db_name
689
+ )
901
690
  DatabaseHandler.production_table_cached.clear()
902
691
  DatabaseHandler.model_versions_cached.clear()
903
692
 
@@ -928,7 +717,6 @@ class DatabaseHandler:
928
717
  par_dict = validate_data.DataValidator.validate_model_parameter(par_dict)
929
718
 
930
719
  db_name = db_name or self.db_name
931
- collection = self.get_collection(collection_name, db_name=db_name)
932
720
 
933
721
  par_dict["value"], _base_unit, _ = value_conversion.get_value_unit_type(
934
722
  value=par_dict["value"], unit_str=par_dict.get("unit", None)
@@ -943,14 +731,12 @@ class DatabaseHandler:
943
731
  f"corresponding to the {par_dict['parameter']} parameter, must be provided."
944
732
  )
945
733
  file_path = Path(file_prefix).joinpath(par_dict["value"])
946
- if not ascii_handler.is_utf8_file(file_path):
947
- raise ValueError(f"File is not UTF-8 encoded: {file_path}")
948
734
  files_to_add_to_db.add(f"{file_path}")
949
735
 
950
736
  self._logger.debug(
951
737
  f"Adding a new entry to DB {db_name} and collection {collection_name}:\n{par_dict}"
952
738
  )
953
- collection.insert_one(par_dict)
739
+ self.mongo_db_handler.insert_one(par_dict, collection_name, db_name)
954
740
 
955
741
  for file_to_insert_now in files_to_add_to_db:
956
742
  self._logger.debug(f"Will also add the file {file_to_insert_now} to the DB")
@@ -958,7 +744,7 @@ class DatabaseHandler:
958
744
 
959
745
  self._reset_parameter_cache()
960
746
 
961
- def insert_file_to_db(self, file_name, db_name=None, **kwargs):
747
+ def insert_file_to_db(self, file_name, db_name=None):
962
748
  """
963
749
  Insert a file to the DB.
964
750
 
@@ -968,35 +754,14 @@ class DatabaseHandler:
968
754
  The name of the file to insert (full path).
969
755
  db_name: str
970
756
  the name of the DB
971
- **kwargs (optional): keyword arguments for file creation.
972
- The full list of arguments can be found in, \
973
- https://www.mongodb.com/docs/manual/core/gridfs/
974
- mostly these are unnecessary though.
975
757
 
976
758
  Returns
977
759
  -------
978
- file_iD: GridOut._id
979
- If the file exists, return its GridOut._id, otherwise insert the file and return its"
980
- "newly created DB GridOut._id.
981
-
760
+ file_id: GridOut._id
761
+ If the file exists, return its GridOut._id, otherwise insert the file and return
762
+ its newly created DB GridOut._id.
982
763
  """
983
- db_name = db_name or self.db_name
984
- db = DatabaseHandler.db_client[db_name]
985
- file_system = gridfs.GridFS(db)
986
-
987
- kwargs.setdefault("content_type", "ascii/dat")
988
- kwargs.setdefault("filename", Path(file_name).name)
989
-
990
- if file_system.exists({"filename": kwargs["filename"]}):
991
- self._logger.warning(
992
- f"The file {kwargs['filename']} exists in the DB. Returning its ID"
993
- )
994
- return file_system.find_one( # pylint: disable=protected-access
995
- {"filename": kwargs["filename"]}
996
- )._id
997
- self._logger.debug(f"Writing file to DB: {file_name}")
998
- with open(file_name, "rb") as data_file:
999
- return file_system.put(data_file, **kwargs)
764
+ return self.mongo_db_handler.insert_file_to_db(file_name, db_name or self.db_name)
1000
765
 
1001
766
  def _cache_key(self, site=None, array_element_name=None, model_version=None, collection=None):
1002
767
  """
@@ -1087,7 +852,7 @@ class DatabaseHandler:
1087
852
  return [array_element_name]
1088
853
  if collection == "configuration_sim_telarray":
1089
854
  # get design model from 'telescope' or 'calibration_device' production tables
1090
- production_table = self.read_production_table_from_mongo_db(
855
+ production_table = self.read_production_table_from_db(
1091
856
  names.get_collection_name_from_array_element_name(array_element_name),
1092
857
  production_table["model_version"],
1093
858
  )
@@ -288,7 +288,7 @@ def _confirm_remote_database_upload(db):
288
288
  if not db.is_remote_database():
289
289
  return True
290
290
 
291
- db_config = db.mongo_db_config
291
+ db_config = db.db_config
292
292
  db_server = db_config.get("db_server", "unknown server") if db_config else "unknown server"
293
293
 
294
294
  try: