ctao-calibpipe 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ctao-calibpipe might be problematic. Click here for more details.
- calibpipe/__init__.py +5 -0
- calibpipe/_dev_version/__init__.py +9 -0
- calibpipe/_version.py +21 -0
- calibpipe/atmosphere/__init__.py +1 -0
- calibpipe/atmosphere/atmosphere_containers.py +109 -0
- calibpipe/atmosphere/meteo_data_handlers.py +485 -0
- calibpipe/atmosphere/models/README.md +14 -0
- calibpipe/atmosphere/models/__init__.py +1 -0
- calibpipe/atmosphere/models/macobac.ecsv +23 -0
- calibpipe/atmosphere/models/reference_MDPs/__init__.py +1 -0
- calibpipe/atmosphere/models/reference_MDPs/ref_density_at_15km_ctao-north_intermediate.ecsv +8 -0
- calibpipe/atmosphere/models/reference_MDPs/ref_density_at_15km_ctao-north_summer.ecsv +8 -0
- calibpipe/atmosphere/models/reference_MDPs/ref_density_at_15km_ctao-north_winter.ecsv +8 -0
- calibpipe/atmosphere/models/reference_MDPs/ref_density_at_15km_ctao-south_summer.ecsv +8 -0
- calibpipe/atmosphere/models/reference_MDPs/ref_density_at_15km_ctao-south_winter.ecsv +8 -0
- calibpipe/atmosphere/models/reference_atmospheres/__init__.py +1 -0
- calibpipe/atmosphere/models/reference_atmospheres/reference_atmo_model_v0_ctao-north_intermediate.ecsv +73 -0
- calibpipe/atmosphere/models/reference_atmospheres/reference_atmo_model_v0_ctao-north_summer.ecsv +73 -0
- calibpipe/atmosphere/models/reference_atmospheres/reference_atmo_model_v0_ctao-north_winter.ecsv +73 -0
- calibpipe/atmosphere/models/reference_atmospheres/reference_atmo_model_v0_ctao-south_summer.ecsv +73 -0
- calibpipe/atmosphere/models/reference_atmospheres/reference_atmo_model_v0_ctao-south_winter.ecsv +73 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/__init__.py +1 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/reference_rayleigh_extinction_profile_v0_ctao-north_intermediate.ecsv +857 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/reference_rayleigh_extinction_profile_v0_ctao-north_summer.ecsv +857 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/reference_rayleigh_extinction_profile_v0_ctao-north_winter.ecsv +857 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/reference_rayleigh_extinction_profile_v0_ctao-south_summer.ecsv +857 -0
- calibpipe/atmosphere/models/reference_rayleigh_scattering_profiles/reference_rayleigh_extinction_profile_v0_ctao-south_winter.ecsv +857 -0
- calibpipe/atmosphere/templates/request_templates/__init__.py +1 -0
- calibpipe/atmosphere/templates/request_templates/copernicus.json +11 -0
- calibpipe/atmosphere/templates/request_templates/gdas.json +12 -0
- calibpipe/core/__init__.py +39 -0
- calibpipe/core/common_metadata_containers.py +195 -0
- calibpipe/core/exceptions.py +87 -0
- calibpipe/database/__init__.py +24 -0
- calibpipe/database/adapter/__init__.py +23 -0
- calibpipe/database/adapter/adapter.py +80 -0
- calibpipe/database/adapter/database_containers/__init__.py +61 -0
- calibpipe/database/adapter/database_containers/atmosphere.py +199 -0
- calibpipe/database/adapter/database_containers/common_metadata.py +148 -0
- calibpipe/database/adapter/database_containers/container_map.py +59 -0
- calibpipe/database/adapter/database_containers/observatory.py +61 -0
- calibpipe/database/adapter/database_containers/table_version_manager.py +39 -0
- calibpipe/database/adapter/database_containers/version_control.py +17 -0
- calibpipe/database/connections/__init__.py +28 -0
- calibpipe/database/connections/calibpipe_database.py +60 -0
- calibpipe/database/connections/postgres_utils.py +97 -0
- calibpipe/database/connections/sql_connection.py +103 -0
- calibpipe/database/connections/user_confirmation.py +19 -0
- calibpipe/database/interfaces/__init__.py +71 -0
- calibpipe/database/interfaces/hashable_row_data.py +54 -0
- calibpipe/database/interfaces/queries.py +180 -0
- calibpipe/database/interfaces/sql_column_info.py +67 -0
- calibpipe/database/interfaces/sql_metadata.py +6 -0
- calibpipe/database/interfaces/sql_table_info.py +131 -0
- calibpipe/database/interfaces/table_handler.py +351 -0
- calibpipe/database/interfaces/types.py +96 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/__init__.py +0 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/contemporary_MDP.ecsv +34 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/macobac.csv +852 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/macobac.ecsv +23 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/merged_file.ecsv +1082 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/meteo_data_copernicus.ecsv +1082 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/meteo_data_gdas.ecsv +66 -0
- calibpipe/tests/data/atmosphere/molecular_atmosphere/observatory_configurations.json +71 -0
- calibpipe/tests/data/utils/__init__.py +0 -0
- calibpipe/tests/data/utils/meteo_data_winter_and_summer.ecsv +12992 -0
- calibpipe/tests/unittests/atmosphere/astral_testing.py +107 -0
- calibpipe/tests/unittests/atmosphere/test_meteo_data_handler.py +775 -0
- calibpipe/tests/unittests/atmosphere/test_molecular_atmosphere.py +327 -0
- calibpipe/tests/unittests/database/test_table_handler.py +66 -0
- calibpipe/tests/unittests/database/test_types.py +38 -0
- calibpipe/tests/unittests/test_bootstrap_db.py +79 -0
- calibpipe/tests/unittests/utils/test_observatory.py +309 -0
- calibpipe/tools/atmospheric_base_tool.py +78 -0
- calibpipe/tools/atmospheric_model_db_loader.py +181 -0
- calibpipe/tools/basic_tool_with_db.py +38 -0
- calibpipe/tools/contemporary_mdp_producer.py +87 -0
- calibpipe/tools/init_db.py +37 -0
- calibpipe/tools/macobac_calculator.py +82 -0
- calibpipe/tools/molecular_atmospheric_model_producer.py +197 -0
- calibpipe/tools/observatory_data_db_loader.py +71 -0
- calibpipe/tools/reference_atmospheric_model_selector.py +201 -0
- calibpipe/utils/__init__.py +10 -0
- calibpipe/utils/observatory.py +486 -0
- calibpipe/utils/observatory_containers.py +26 -0
- calibpipe/version.py +24 -0
- ctao_calibpipe-0.1.0.dist-info/METADATA +86 -0
- ctao_calibpipe-0.1.0.dist-info/RECORD +93 -0
- ctao_calibpipe-0.1.0.dist-info/WHEEL +5 -0
- ctao_calibpipe-0.1.0.dist-info/entry_points.txt +8 -0
- ctao_calibpipe-0.1.0.dist-info/licenses/AUTHORS.md +13 -0
- ctao_calibpipe-0.1.0.dist-info/licenses/LICENSE +21 -0
- ctao_calibpipe-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Set up logging
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
import yaml
|
|
8
|
+
from astropy.table import QTable
|
|
9
|
+
from astropy.time import Time
|
|
10
|
+
from calibpipe.core.exceptions import IntermittentError, MissingInputDataError
|
|
11
|
+
from calibpipe.tools.contemporary_mdp_producer import CreateMolecularDensityProfile
|
|
12
|
+
from calibpipe.tools.macobac_calculator import CalculateMACOBAC
|
|
13
|
+
from calibpipe.tools.molecular_atmospheric_model_producer import (
|
|
14
|
+
CreateMolecularAtmosphericModel,
|
|
15
|
+
)
|
|
16
|
+
from calibpipe.tools.reference_atmospheric_model_selector import (
|
|
17
|
+
SelectMolecularAtmosphericModel,
|
|
18
|
+
)
|
|
19
|
+
from ctapipe.core import run_tool
|
|
20
|
+
from traitlets.config import Config
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.verifies_usecase("UC-120-1.2")
|
|
24
|
+
def test_calculate_macobac(tmp_path):
|
|
25
|
+
config = Config()
|
|
26
|
+
config.CalculateMACOBAC = Config()
|
|
27
|
+
config.CalculateMACOBAC.output_file = str(tmp_path / "macobac.ecsv")
|
|
28
|
+
|
|
29
|
+
with patch(
|
|
30
|
+
"calibpipe.tools.macobac_calculator.CO2DataHandler",
|
|
31
|
+
new_callable=MagicMock,
|
|
32
|
+
) as mock_class:
|
|
33
|
+
mock_class.return_value.data_path = str(
|
|
34
|
+
Path(__file__).parent.parent.parent
|
|
35
|
+
/ "data/atmosphere/molecular_atmosphere/"
|
|
36
|
+
)
|
|
37
|
+
tool = CalculateMACOBAC(config=config)
|
|
38
|
+
run_tool(tool)
|
|
39
|
+
|
|
40
|
+
output_file = Path(config.CalculateMACOBAC.output_file)
|
|
41
|
+
assert output_file.exists(), "Output file was not created."
|
|
42
|
+
|
|
43
|
+
result_table = QTable.read(output_file, format="ascii.ecsv")
|
|
44
|
+
expected_co2_concentration = 419.3
|
|
45
|
+
assert (
|
|
46
|
+
result_table["co2_concentration"][0].value
|
|
47
|
+
== pytest.approx(expected_co2_concentration, abs=0.1)
|
|
48
|
+
), f"CO2 concentration does not match expected value ({result_table['co2_concentration'][0]} != {expected_co2_concentration})."
|
|
49
|
+
assert (
|
|
50
|
+
result_table["estimation_date"][0]
|
|
51
|
+
== Time(str(datetime.now(timezone.utc).date()), out_subfmt="date").iso
|
|
52
|
+
), "Estimation date does not match expected value."
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.db()
|
|
56
|
+
@pytest.mark.verifies_usecase("UC-120-1.7")
|
|
57
|
+
@pytest.mark.verifies_usecase("UC-120-1.8")
|
|
58
|
+
def test_create_molecular_atmospheric_model(tmp_path):
|
|
59
|
+
config_path = (
|
|
60
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
61
|
+
/ "docs/source/examples/atmosphere/configuration/create_molecular_atmospheric_model.yaml"
|
|
62
|
+
)
|
|
63
|
+
with open(config_path) as file:
|
|
64
|
+
config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
65
|
+
|
|
66
|
+
config.CreateMolecularAtmosphericModel.output_path = str(tmp_path)
|
|
67
|
+
# Mock the necessary components
|
|
68
|
+
with patch(
|
|
69
|
+
# "calibpipe.tools.molecular_atmospheric_model_producer.MeteoDataHandler",
|
|
70
|
+
"calibpipe.tools.atmospheric_base_tool.MeteoDataHandler",
|
|
71
|
+
new_callable=MagicMock,
|
|
72
|
+
) as mock_meteo_handler_class:
|
|
73
|
+
tool = CreateMolecularAtmosphericModel(config=config)
|
|
74
|
+
# Test missing input data
|
|
75
|
+
with pytest.raises(MissingInputDataError):
|
|
76
|
+
run_tool(
|
|
77
|
+
tool,
|
|
78
|
+
argv=[
|
|
79
|
+
"-c",
|
|
80
|
+
str(
|
|
81
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
82
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
83
|
+
),
|
|
84
|
+
"--macobac12-table-path",
|
|
85
|
+
str(
|
|
86
|
+
Path(__file__).parent.parent.parent
|
|
87
|
+
/ "data/atmosphere/molecular_atmosphere/macobac.ecsv"
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
raises=True,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Now set up the mock data handler
|
|
94
|
+
mock_meteo_handler = MagicMock()
|
|
95
|
+
mock_meteo_handler_class.from_name.return_value = mock_meteo_handler
|
|
96
|
+
mock_meteo_handler.data_path = str(
|
|
97
|
+
Path(__file__).parent.parent.parent
|
|
98
|
+
/ "data/atmosphere/molecular_atmosphere/"
|
|
99
|
+
)
|
|
100
|
+
mock_meteo_handler.request_data.return_value = 0
|
|
101
|
+
|
|
102
|
+
run_tool(
|
|
103
|
+
tool,
|
|
104
|
+
argv=[
|
|
105
|
+
"-c",
|
|
106
|
+
str(
|
|
107
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
108
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
109
|
+
),
|
|
110
|
+
"--macobac12-table-path",
|
|
111
|
+
str(
|
|
112
|
+
Path(__file__).parent.parent.parent
|
|
113
|
+
/ "data/atmosphere/molecular_atmosphere/macobac.ecsv"
|
|
114
|
+
),
|
|
115
|
+
],
|
|
116
|
+
raises=True,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Check if the output files were created
|
|
120
|
+
output_profile = (
|
|
121
|
+
Path(config.CreateMolecularAtmosphericModel.output_path)
|
|
122
|
+
/ "contemporary_atmospheric_profile.ascii.ecsv"
|
|
123
|
+
)
|
|
124
|
+
output_extinction = (
|
|
125
|
+
Path(config.CreateMolecularAtmosphericModel.output_path)
|
|
126
|
+
/ "contemporary_rayleigh_extinction_profile.ascii.ecsv"
|
|
127
|
+
)
|
|
128
|
+
assert (
|
|
129
|
+
output_profile.exists()
|
|
130
|
+
), "Contemporary atmospheric profile file was not created."
|
|
131
|
+
assert (
|
|
132
|
+
output_extinction.exists()
|
|
133
|
+
), "Contemporary Rayleigh extinction profile file was not created."
|
|
134
|
+
|
|
135
|
+
# Read and validate the output files
|
|
136
|
+
profile_table = QTable.read(output_profile, format="ascii.ecsv")
|
|
137
|
+
extinction_table = QTable.read(output_extinction, format="ascii.ecsv")
|
|
138
|
+
|
|
139
|
+
expected_profile_columns = [
|
|
140
|
+
"altitude",
|
|
141
|
+
"atmospheric_density",
|
|
142
|
+
"atmospheric_thickness",
|
|
143
|
+
"refractive_index_m_1",
|
|
144
|
+
"temperature",
|
|
145
|
+
"pressure",
|
|
146
|
+
"partial_water_pressure",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
for column in expected_profile_columns:
|
|
150
|
+
assert (
|
|
151
|
+
column in profile_table.colnames
|
|
152
|
+
), f"{column} column missing in profile table."
|
|
153
|
+
|
|
154
|
+
assert (
|
|
155
|
+
"altitude_max" in extinction_table.colnames
|
|
156
|
+
), "Altitude_max column missing in extinction table."
|
|
157
|
+
assert (
|
|
158
|
+
"altitude_min" in extinction_table.colnames
|
|
159
|
+
), "Altitude_min column missing in extinction table."
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@pytest.mark.db()
|
|
163
|
+
@pytest.mark.verifies_usecase("UC-120-1.6")
|
|
164
|
+
def test_create_molecular_density_profile(tmp_path):
|
|
165
|
+
config_path = (
|
|
166
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
167
|
+
/ "docs/source/examples/atmosphere/configuration/create_molecular_density_profile.yaml"
|
|
168
|
+
)
|
|
169
|
+
with open(config_path) as file:
|
|
170
|
+
config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
171
|
+
|
|
172
|
+
config.CreateMolecularDensityProfile.output_path = str(tmp_path)
|
|
173
|
+
# Mock the necessary components
|
|
174
|
+
with patch(
|
|
175
|
+
"calibpipe.tools.atmospheric_base_tool.MeteoDataHandler",
|
|
176
|
+
new_callable=MagicMock,
|
|
177
|
+
) as mock_meteo_handler_class:
|
|
178
|
+
tool = CreateMolecularDensityProfile(config=config)
|
|
179
|
+
# Test missing input data
|
|
180
|
+
with pytest.raises(MissingInputDataError):
|
|
181
|
+
run_tool(
|
|
182
|
+
tool,
|
|
183
|
+
argv=[
|
|
184
|
+
"-c",
|
|
185
|
+
str(
|
|
186
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
187
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
188
|
+
),
|
|
189
|
+
],
|
|
190
|
+
raises=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Now set up the mock data handler
|
|
194
|
+
mock_meteo_handler = MagicMock()
|
|
195
|
+
mock_meteo_handler_class.from_name.return_value = mock_meteo_handler
|
|
196
|
+
mock_meteo_handler.data_path = str(
|
|
197
|
+
Path(__file__).parent.parent.parent
|
|
198
|
+
/ "data/atmosphere/molecular_atmosphere/"
|
|
199
|
+
)
|
|
200
|
+
mock_meteo_handler.request_data.return_value = 0
|
|
201
|
+
|
|
202
|
+
run_tool(
|
|
203
|
+
tool,
|
|
204
|
+
argv=[
|
|
205
|
+
"-c",
|
|
206
|
+
str(
|
|
207
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
208
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
209
|
+
),
|
|
210
|
+
],
|
|
211
|
+
raises=True,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Check if the output file was created
|
|
215
|
+
output_file = (
|
|
216
|
+
Path(config.CreateMolecularDensityProfile.output_path)
|
|
217
|
+
/ "contemporary_molecular_density_profile.ascii.ecsv"
|
|
218
|
+
)
|
|
219
|
+
assert (
|
|
220
|
+
output_file.exists()
|
|
221
|
+
), "Contemporary molecular density profile file was not created."
|
|
222
|
+
|
|
223
|
+
# Read and validate the output file
|
|
224
|
+
mdp_table = QTable.read(output_file, format="ascii.ecsv")
|
|
225
|
+
|
|
226
|
+
expected_columns = [
|
|
227
|
+
"altitude",
|
|
228
|
+
"number density",
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
for column in expected_columns:
|
|
232
|
+
assert (
|
|
233
|
+
column in mdp_table.colnames
|
|
234
|
+
), f"{column} column missing in molecular density profile table."
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@pytest.mark.db()
|
|
238
|
+
@pytest.mark.verifies_usecase("UC-120-1.3")
|
|
239
|
+
def test_select_molecular_atmospheric_model(tmp_path):
|
|
240
|
+
config_path = (
|
|
241
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
242
|
+
/ "docs/source/examples/atmosphere/configuration/select_reference_atmospheric_model.yaml"
|
|
243
|
+
)
|
|
244
|
+
with open(config_path) as file:
|
|
245
|
+
config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
246
|
+
|
|
247
|
+
config.SelectMolecularAtmosphericModel.output_path = str(tmp_path)
|
|
248
|
+
# Mock the necessary components
|
|
249
|
+
with patch(
|
|
250
|
+
"calibpipe.tools.atmospheric_base_tool.MeteoDataHandler",
|
|
251
|
+
new_callable=MagicMock,
|
|
252
|
+
) as mock_meteo_handler_class:
|
|
253
|
+
tool = SelectMolecularAtmosphericModel(config=config)
|
|
254
|
+
|
|
255
|
+
# Mock database and data handler behavior
|
|
256
|
+
mock_meteo_handler = MagicMock()
|
|
257
|
+
mock_meteo_handler_class.from_name.return_value = mock_meteo_handler
|
|
258
|
+
mock_meteo_handler.data_path = str(
|
|
259
|
+
Path(__file__).parent.parent.parent
|
|
260
|
+
/ "data/atmosphere/molecular_atmosphere/"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Test missing input data
|
|
264
|
+
mock_meteo_handler.request_data.return_value = 1
|
|
265
|
+
|
|
266
|
+
with pytest.raises(IntermittentError):
|
|
267
|
+
run_tool(
|
|
268
|
+
tool,
|
|
269
|
+
argv=[
|
|
270
|
+
"-c",
|
|
271
|
+
str(
|
|
272
|
+
Path(__file__).parent.parent.parent.parent.parent.parent
|
|
273
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
274
|
+
),
|
|
275
|
+
],
|
|
276
|
+
raises=True,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Check if the output files were created
|
|
280
|
+
output_profile = (
|
|
281
|
+
Path(config.SelectMolecularAtmosphericModel.output_path)
|
|
282
|
+
/ "selected_atmospheric_profile.ascii.ecsv"
|
|
283
|
+
)
|
|
284
|
+
output_extinction = (
|
|
285
|
+
Path(config.SelectMolecularAtmosphericModel.output_path)
|
|
286
|
+
/ "selected_rayleigh_extinction_profile.ascii.ecsv"
|
|
287
|
+
)
|
|
288
|
+
assert (
|
|
289
|
+
output_profile.exists()
|
|
290
|
+
), "Selected atmospheric profile file was not created."
|
|
291
|
+
assert (
|
|
292
|
+
output_extinction.exists()
|
|
293
|
+
), "Selected Rayleigh extinction profile file was not created."
|
|
294
|
+
|
|
295
|
+
# Read and validate the output files
|
|
296
|
+
profile_table = QTable.read(output_profile, format="ascii.ecsv")
|
|
297
|
+
extinction_table = QTable.read(output_extinction, format="ascii.ecsv")
|
|
298
|
+
|
|
299
|
+
expected_profile_columns = [
|
|
300
|
+
"altitude",
|
|
301
|
+
"atmospheric_density",
|
|
302
|
+
"atmospheric_thickness",
|
|
303
|
+
"refractive_index_m_1",
|
|
304
|
+
"temperature",
|
|
305
|
+
"pressure",
|
|
306
|
+
"partial_water_pressure",
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
for column in expected_profile_columns:
|
|
310
|
+
assert (
|
|
311
|
+
column in profile_table.colnames
|
|
312
|
+
), f"{column} column missing in profile table."
|
|
313
|
+
|
|
314
|
+
expected_extinction_columns = ["altitude_min", "altitude_max"]
|
|
315
|
+
expected_extinction_columns.extend([f"{i}.0 nm" for i in range(200, 1000)])
|
|
316
|
+
|
|
317
|
+
for column in expected_extinction_columns:
|
|
318
|
+
assert (
|
|
319
|
+
column in extinction_table.colnames
|
|
320
|
+
), f"{column} column missing in extinction table."
|
|
321
|
+
if "altitude" in column:
|
|
322
|
+
assert (
|
|
323
|
+
extinction_table[column].unit == "km"
|
|
324
|
+
), f"Column {column} unit does not match expected unit."
|
|
325
|
+
assert (
|
|
326
|
+
extinction_table[column].ndim == 1
|
|
327
|
+
), f"Column {column} does not have the expected number of dimensions."
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Import the necessary modules and classes for testing
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import astropy.units as u
|
|
5
|
+
import pytest
|
|
6
|
+
import yaml
|
|
7
|
+
from calibpipe.database.connections import CalibPipeDatabase
|
|
8
|
+
from calibpipe.database.interfaces import TableHandler
|
|
9
|
+
from calibpipe.utils.observatory import (
|
|
10
|
+
Observatory,
|
|
11
|
+
)
|
|
12
|
+
from traitlets.config import Config
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Fixture to provide a database connection
|
|
16
|
+
@pytest.fixture()
|
|
17
|
+
def test_config():
|
|
18
|
+
# Setup and connect to the test database
|
|
19
|
+
config_path = Path(__file__).parent.joinpath(
|
|
20
|
+
"../../../../../docs/source/examples/utils/configuration/"
|
|
21
|
+
)
|
|
22
|
+
with open(config_path.joinpath("upload_observatory_data_db.yaml")) as yaml_file:
|
|
23
|
+
config_data = yaml.safe_load(yaml_file)
|
|
24
|
+
config_data = config_data["UploadObservatoryData"]
|
|
25
|
+
|
|
26
|
+
with open(config_path.joinpath("db_config.yaml")) as yaml_file:
|
|
27
|
+
config_data |= yaml.safe_load(yaml_file)
|
|
28
|
+
return config_data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture()
|
|
32
|
+
def test_container(test_config):
|
|
33
|
+
return Observatory(config=Config(test_config["observatories"][0])).containers[0]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Test cases for TableHandler class and other functions in the module
|
|
37
|
+
class TestTableHandler:
|
|
38
|
+
# Test get_database_table_insertion method
|
|
39
|
+
@pytest.mark.db()
|
|
40
|
+
def test_get_database_table_insertion(self, test_container):
|
|
41
|
+
# Prepare a mock container and call the method
|
|
42
|
+
table, kwargs = TableHandler.get_database_table_insertion(test_container)
|
|
43
|
+
|
|
44
|
+
# Assert that the table and kwargs are not None
|
|
45
|
+
assert table is not None
|
|
46
|
+
assert kwargs is not None
|
|
47
|
+
|
|
48
|
+
# Test read_table_from_database method
|
|
49
|
+
@pytest.mark.db()
|
|
50
|
+
def test_read_table_from_database(self, test_container, test_config):
|
|
51
|
+
TableHandler.prepare_db_tables(
|
|
52
|
+
[
|
|
53
|
+
test_container,
|
|
54
|
+
],
|
|
55
|
+
test_config["database_configuration"],
|
|
56
|
+
)
|
|
57
|
+
condition = "c.elevation == 3000"
|
|
58
|
+
with CalibPipeDatabase(**test_config["database_configuration"]) as connection:
|
|
59
|
+
qtable = TableHandler.read_table_from_database(
|
|
60
|
+
type(test_container), connection, condition
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Assert that qtable is not None and has the expected columns
|
|
64
|
+
assert qtable is not None
|
|
65
|
+
assert "elevation" in qtable.colnames
|
|
66
|
+
assert qtable["elevation"].unit == u.m
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Test sqlalchemy types."""
|
|
2
|
+
|
|
3
|
+
from calibpipe.database.interfaces.types import (
|
|
4
|
+
BigInteger,
|
|
5
|
+
Boolean,
|
|
6
|
+
ColumnType,
|
|
7
|
+
Date,
|
|
8
|
+
DateTime,
|
|
9
|
+
Double,
|
|
10
|
+
Float,
|
|
11
|
+
Integer,
|
|
12
|
+
NDArray,
|
|
13
|
+
Numeric,
|
|
14
|
+
SmallInteger,
|
|
15
|
+
String,
|
|
16
|
+
Time,
|
|
17
|
+
)
|
|
18
|
+
from sqlalchemy.sql.type_api import TypeEngine
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_types():
|
|
22
|
+
"""Test that types are instance of TypeEngine."""
|
|
23
|
+
for type_ in [
|
|
24
|
+
Boolean,
|
|
25
|
+
SmallInteger,
|
|
26
|
+
Integer,
|
|
27
|
+
BigInteger,
|
|
28
|
+
Float,
|
|
29
|
+
Double,
|
|
30
|
+
Numeric,
|
|
31
|
+
String,
|
|
32
|
+
Date,
|
|
33
|
+
DateTime,
|
|
34
|
+
Time,
|
|
35
|
+
ColumnType,
|
|
36
|
+
NDArray,
|
|
37
|
+
]:
|
|
38
|
+
assert issubclass(type_, TypeEngine)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import yaml
|
|
6
|
+
from calibpipe.tools.atmospheric_model_db_loader import UploadAtmosphericModel
|
|
7
|
+
from calibpipe.tools.init_db import CalibPipeDatabaseInitialization
|
|
8
|
+
from calibpipe.tools.observatory_data_db_loader import UploadObservatoryData
|
|
9
|
+
from ctapipe.core import run_tool
|
|
10
|
+
from traitlets.config import Config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.db()
|
|
14
|
+
@pytest.mark.order(1)
|
|
15
|
+
def test_init_database():
|
|
16
|
+
"""
|
|
17
|
+
Fixture to initialize the database and upload required data.
|
|
18
|
+
This runs before any other database-related tests.
|
|
19
|
+
"""
|
|
20
|
+
# Paths to configuration files
|
|
21
|
+
db_config_path = (
|
|
22
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
23
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Initialize the database
|
|
27
|
+
with open(db_config_path) as file:
|
|
28
|
+
db_config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
29
|
+
tool = CalibPipeDatabaseInitialization(config=db_config)
|
|
30
|
+
run_tool(tool)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.mark.db()
|
|
34
|
+
@pytest.mark.order(2)
|
|
35
|
+
def test_upload_observatory():
|
|
36
|
+
db_config_path = (
|
|
37
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
38
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
39
|
+
)
|
|
40
|
+
with open(db_config_path) as file:
|
|
41
|
+
db_config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
42
|
+
|
|
43
|
+
observatory_data_config_path = (
|
|
44
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
45
|
+
/ "docs/source/examples/utils/configuration/upload_observatory_data_db.yaml"
|
|
46
|
+
)
|
|
47
|
+
# Upload observatory data
|
|
48
|
+
config = deepcopy(db_config)
|
|
49
|
+
with open(observatory_data_config_path) as file:
|
|
50
|
+
observatory_data_config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
51
|
+
config.update(observatory_data_config)
|
|
52
|
+
tool = UploadObservatoryData(config=config)
|
|
53
|
+
run_tool(tool)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.mark.db()
|
|
57
|
+
@pytest.mark.order(3)
|
|
58
|
+
def test_upload_atmospheric_models():
|
|
59
|
+
db_config_path = (
|
|
60
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
61
|
+
/ "docs/source/examples/utils/configuration/db_config.yaml"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
with open(db_config_path) as file:
|
|
65
|
+
db_config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
66
|
+
|
|
67
|
+
atmospheric_model_configs = list(
|
|
68
|
+
Path(__file__).parent.parent.parent.parent.parent.glob(
|
|
69
|
+
"docs/source/examples/utils/configuration/upload_atmospheric*.yaml"
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
# Upload atmospheric model data
|
|
73
|
+
for config_path in atmospheric_model_configs:
|
|
74
|
+
config = deepcopy(db_config)
|
|
75
|
+
with open(config_path) as file:
|
|
76
|
+
atmospheric_model_config = Config(yaml.load(file, Loader=yaml.SafeLoader))
|
|
77
|
+
config.update(atmospheric_model_config)
|
|
78
|
+
tool = UploadAtmosphericModel(config=config)
|
|
79
|
+
run_tool(tool)
|