rubin-sim 2.6.1.dev2__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 (387) hide show
  1. batch/compile_prenight_metadata_cache.sh +34 -0
  2. batch/run_auxtel_prenight_sims.sh +163 -0
  3. batch/run_prenight_sims.sh +239 -0
  4. docs/.gitignore +3 -0
  5. docs/Makefile +32 -0
  6. docs/api.rst +25 -0
  7. docs/conf.py +8 -0
  8. docs/data-api.rst +12 -0
  9. docs/data-download.rst +70 -0
  10. docs/developer.rst +14 -0
  11. docs/documenteer.toml +11 -0
  12. docs/index.rst +22 -0
  13. docs/installation.rst +76 -0
  14. docs/introduction.rst +37 -0
  15. docs/maf-api-batches.rst +12 -0
  16. docs/maf-api-db.rst +12 -0
  17. docs/maf-api-maf-contrib.rst +12 -0
  18. docs/maf-api-maps.rst +12 -0
  19. docs/maf-api-metricbundles.rst +12 -0
  20. docs/maf-api-metrics.rst +12 -0
  21. docs/maf-api-plots.rst +12 -0
  22. docs/maf-api-run-comparison.rst +12 -0
  23. docs/maf-api-slicers.rst +12 -0
  24. docs/maf-api-stackers.rst +12 -0
  25. docs/maf-api-utils.rst +12 -0
  26. docs/maf-api.rst +22 -0
  27. docs/maf.rst +60 -0
  28. docs/metric_list.py +49 -0
  29. docs/moving-objects-api.rst +12 -0
  30. docs/moving-objects.rst +22 -0
  31. docs/phot-utils-api.rst +12 -0
  32. docs/phot-utils.rst +11 -0
  33. docs/prenight.rst +117 -0
  34. docs/protoarchive.rst +276 -0
  35. docs/satellite-constellations-api.rst +12 -0
  36. docs/satellite-constellations.rst +13 -0
  37. docs/selfcal-api.rst +12 -0
  38. docs/selfcal.rst +15 -0
  39. docs/sim-archive-api.rst +12 -0
  40. docs/skybrightness-api.rst +12 -0
  41. docs/skybrightness.rst +23 -0
  42. docs/user-guide.rst +28 -0
  43. docs/vseqarchive-architecture.rst +438 -0
  44. docs/vseqarchive-setup.rst +238 -0
  45. docs/vseqarchive.rst +225 -0
  46. rubin_sim/__init__.py +7 -0
  47. rubin_sim/data/__init__.py +1 -0
  48. rubin_sim/data/rs_download_data.py +155 -0
  49. rubin_sim/maf/__init__.py +34 -0
  50. rubin_sim/maf/batches/__init__.py +18 -0
  51. rubin_sim/maf/batches/altaz_batch.py +161 -0
  52. rubin_sim/maf/batches/col_map_dict.py +274 -0
  53. rubin_sim/maf/batches/common.py +388 -0
  54. rubin_sim/maf/batches/ddf_batch.py +728 -0
  55. rubin_sim/maf/batches/filterchange_batch.py +182 -0
  56. rubin_sim/maf/batches/glance_batch.py +591 -0
  57. rubin_sim/maf/batches/hourglass_batch.py +79 -0
  58. rubin_sim/maf/batches/info_batch.py +114 -0
  59. rubin_sim/maf/batches/metadata_batch.py +558 -0
  60. rubin_sim/maf/batches/moving_objects_batch.py +1587 -0
  61. rubin_sim/maf/batches/openshutter_batch.py +100 -0
  62. rubin_sim/maf/batches/radar_limited.py +1100 -0
  63. rubin_sim/maf/batches/science_radar_batch.py +1910 -0
  64. rubin_sim/maf/batches/skycoverage.py +155 -0
  65. rubin_sim/maf/batches/slew_batch.py +146 -0
  66. rubin_sim/maf/batches/srd_batch.py +637 -0
  67. rubin_sim/maf/batches/time_batch.py +757 -0
  68. rubin_sim/maf/batches/time_sci_batch.py +105 -0
  69. rubin_sim/maf/batches/visitdepth_batch.py +485 -0
  70. rubin_sim/maf/db/__init__.py +2 -0
  71. rubin_sim/maf/db/add_run.py +34 -0
  72. rubin_sim/maf/db/results_db.py +1074 -0
  73. rubin_sim/maf/db/tracking_db.py +332 -0
  74. rubin_sim/maf/ddf_dir.py +53 -0
  75. rubin_sim/maf/generate_ss.py +218 -0
  76. rubin_sim/maf/glance_dir.py +58 -0
  77. rubin_sim/maf/maf_contrib/__init__.py +25 -0
  78. rubin_sim/maf/maf_contrib/calculate_lsst_field_visibility_astropy.py +170 -0
  79. rubin_sim/maf/maf_contrib/depth_limited_num_gal_metric.py +109 -0
  80. rubin_sim/maf/maf_contrib/example_new_metrics.py +40 -0
  81. rubin_sim/maf/maf_contrib/filter_pair_t_gaps_metric.py +208 -0
  82. rubin_sim/maf/maf_contrib/grb_transient_metric.py +249 -0
  83. rubin_sim/maf/maf_contrib/gw170817_det_metric.py +72 -0
  84. rubin_sim/maf/maf_contrib/intervals_between_obs_metric.py +68 -0
  85. rubin_sim/maf/maf_contrib/kne_metrics.py +473 -0
  86. rubin_sim/maf/maf_contrib/lss_metrics.py +45 -0
  87. rubin_sim/maf/maf_contrib/lss_obs_strategy/__init__.py +3 -0
  88. rubin_sim/maf/maf_contrib/lss_obs_strategy/constants_for_pipeline.py +73 -0
  89. rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_metric_extended.py +205 -0
  90. rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_with_pixel_calibration.py +171 -0
  91. rubin_sim/maf/maf_contrib/lv_dwarfs/__init__.py +1 -0
  92. rubin_sim/maf/maf_contrib/lv_dwarfs/lv_dwarfs_metrics.py +388 -0
  93. rubin_sim/maf/maf_contrib/microlensing_metric.py +593 -0
  94. rubin_sim/maf/maf_contrib/num_obs_in_survey_time_overlap_metric.py +43 -0
  95. rubin_sim/maf/maf_contrib/periodic_metric.py +51 -0
  96. rubin_sim/maf/maf_contrib/periodic_star_metric.py +186 -0
  97. rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py +236 -0
  98. rubin_sim/maf/maf_contrib/presto_color_kne_pop_metric.py +441 -0
  99. rubin_sim/maf/maf_contrib/selfcal_uniformity_metric.py +171 -0
  100. rubin_sim/maf/maf_contrib/star_count_mass_metric.py +64 -0
  101. rubin_sim/maf/maf_contrib/star_count_metric.py +56 -0
  102. rubin_sim/maf/maf_contrib/star_counts/__init__.py +8 -0
  103. rubin_sim/maf/maf_contrib/star_counts/abs_mag.py +324 -0
  104. rubin_sim/maf/maf_contrib/star_counts/coords.py +99 -0
  105. rubin_sim/maf/maf_contrib/star_counts/readme.txt +1 -0
  106. rubin_sim/maf/maf_contrib/star_counts/spec_type.py +91 -0
  107. rubin_sim/maf/maf_contrib/star_counts/starcount.py +30 -0
  108. rubin_sim/maf/maf_contrib/star_counts/starcount_bymass.py +93 -0
  109. rubin_sim/maf/maf_contrib/star_counts/stellardensity.py +59 -0
  110. rubin_sim/maf/maf_contrib/static_probes_fom_summary_metric.py +386 -0
  111. rubin_sim/maf/maf_contrib/tdes_pop_metric.py +362 -0
  112. rubin_sim/maf/maf_contrib/transient_ascii_sed_metric.py +652 -0
  113. rubin_sim/maf/maf_contrib/triplet_metric.py +156 -0
  114. rubin_sim/maf/maf_contrib/var_depth_metric.py +157 -0
  115. rubin_sim/maf/maf_contrib/xrb_metrics.py +523 -0
  116. rubin_sim/maf/maf_contrib/young_stellar_objects_metric.py +200 -0
  117. rubin_sim/maf/maf_night_report.py +200 -0
  118. rubin_sim/maf/make_fbs_tracking_db.py +90 -0
  119. rubin_sim/maf/maps/__init__.py +8 -0
  120. rubin_sim/maf/maps/base_map.py +72 -0
  121. rubin_sim/maf/maps/create_gaia_density_map.py +158 -0
  122. rubin_sim/maf/maps/dust_map.py +66 -0
  123. rubin_sim/maf/maps/dust_map_3d.py +190 -0
  124. rubin_sim/maf/maps/ebv_3d_hp.py +170 -0
  125. rubin_sim/maf/maps/ebv_hp.py +64 -0
  126. rubin_sim/maf/maps/gal_coords_map.py +22 -0
  127. rubin_sim/maf/maps/galactic_plane_priority_maps.py +210 -0
  128. rubin_sim/maf/maps/stellar_density_map.py +73 -0
  129. rubin_sim/maf/maps/trilegal_map.py +82 -0
  130. rubin_sim/maf/metadata_dir.py +90 -0
  131. rubin_sim/maf/metric_bundles/__init__.py +3 -0
  132. rubin_sim/maf/metric_bundles/metric_bundle.py +816 -0
  133. rubin_sim/maf/metric_bundles/metric_bundle_group.py +834 -0
  134. rubin_sim/maf/metric_bundles/mo_metric_bundle.py +646 -0
  135. rubin_sim/maf/metrics/__init__.py +52 -0
  136. rubin_sim/maf/metrics/agn_time_lag_metric.py +118 -0
  137. rubin_sim/maf/metrics/agnstructure.py +141 -0
  138. rubin_sim/maf/metrics/area_summary_metrics.py +120 -0
  139. rubin_sim/maf/metrics/base_metric.py +215 -0
  140. rubin_sim/maf/metrics/brown_dwarf_metric.py +162 -0
  141. rubin_sim/maf/metrics/cadence_metrics.py +400 -0
  142. rubin_sim/maf/metrics/calibration_metrics.py +618 -0
  143. rubin_sim/maf/metrics/chip_vendor_metric.py +43 -0
  144. rubin_sim/maf/metrics/color_slope_metrics.py +189 -0
  145. rubin_sim/maf/metrics/cosmology_summary_metrics.py +1212 -0
  146. rubin_sim/maf/metrics/coverage_metric.py +48 -0
  147. rubin_sim/maf/metrics/crowding_metric.py +255 -0
  148. rubin_sim/maf/metrics/cumulative_metric.py +46 -0
  149. rubin_sim/maf/metrics/dcr_metric.py +93 -0
  150. rubin_sim/maf/metrics/exgal_m5.py +57 -0
  151. rubin_sim/maf/metrics/fft_metric.py +27 -0
  152. rubin_sim/maf/metrics/galactic_plane_metrics.py +375 -0
  153. rubin_sim/maf/metrics/galplane_time_sampling_metrics.py +236 -0
  154. rubin_sim/maf/metrics/hourglass_metric.py +130 -0
  155. rubin_sim/maf/metrics/incremental_template_metric.py +255 -0
  156. rubin_sim/maf/metrics/kuiper_metrics.py +26 -0
  157. rubin_sim/maf/metrics/mo_metrics.py +1496 -0
  158. rubin_sim/maf/metrics/mo_summary_metrics.py +464 -0
  159. rubin_sim/maf/metrics/night_pointing_metric.py +70 -0
  160. rubin_sim/maf/metrics/optimal_m5_metric.py +94 -0
  161. rubin_sim/maf/metrics/pair_metric.py +69 -0
  162. rubin_sim/maf/metrics/periodic_detect_metric.py +133 -0
  163. rubin_sim/maf/metrics/phase_gap_metric.py +172 -0
  164. rubin_sim/maf/metrics/qso_number_counts_metric.py +138 -0
  165. rubin_sim/maf/metrics/scaling_metrics.py +148 -0
  166. rubin_sim/maf/metrics/schedview_metrics.py +39 -0
  167. rubin_sim/maf/metrics/season_metrics.py +344 -0
  168. rubin_sim/maf/metrics/simple_metrics.py +546 -0
  169. rubin_sim/maf/metrics/sky_sat_metric.py +18 -0
  170. rubin_sim/maf/metrics/sn_cadence_metric.py +104 -0
  171. rubin_sim/maf/metrics/sn_n_sn_metric.py +1364 -0
  172. rubin_sim/maf/metrics/sn_sl_metric.py +216 -0
  173. rubin_sim/maf/metrics/sn_snr_metric.py +715 -0
  174. rubin_sim/maf/metrics/snr_weight.py +24 -0
  175. rubin_sim/maf/metrics/star_density.py +77 -0
  176. rubin_sim/maf/metrics/string_count_metric.py +95 -0
  177. rubin_sim/maf/metrics/summary_metrics.py +201 -0
  178. rubin_sim/maf/metrics/surfb_metric.py +175 -0
  179. rubin_sim/maf/metrics/technical_metrics.py +271 -0
  180. rubin_sim/maf/metrics/tgaps.py +412 -0
  181. rubin_sim/maf/metrics/tomography_models.py +2034 -0
  182. rubin_sim/maf/metrics/transient_metrics.py +268 -0
  183. rubin_sim/maf/metrics/uniformity_metrics.py +418 -0
  184. rubin_sim/maf/metrics/use_metrics.py +55 -0
  185. rubin_sim/maf/metrics/vector_metrics.py +190 -0
  186. rubin_sim/maf/metrics/visit_groups_metric.py +341 -0
  187. rubin_sim/maf/metrics/weak_lensing_systematics_metric.py +210 -0
  188. rubin_sim/maf/plots/__init__.py +14 -0
  189. rubin_sim/maf/plots/hg_plotters.py +1117 -0
  190. rubin_sim/maf/plots/hourglass_plotters.py +120 -0
  191. rubin_sim/maf/plots/mo_plotters.py +358 -0
  192. rubin_sim/maf/plots/nd_plotters.py +220 -0
  193. rubin_sim/maf/plots/neo_distance_plotter.py +140 -0
  194. rubin_sim/maf/plots/night_pointing_plotter.py +96 -0
  195. rubin_sim/maf/plots/oned_plotters.py +114 -0
  196. rubin_sim/maf/plots/perceptual_rainbow.py +30 -0
  197. rubin_sim/maf/plots/plot_handler.py +766 -0
  198. rubin_sim/maf/plots/skyproj_plotters.py +540 -0
  199. rubin_sim/maf/plots/spatial_plotters.py +1029 -0
  200. rubin_sim/maf/plots/special_plotters.py +256 -0
  201. rubin_sim/maf/plots/two_d_plotters.py +177 -0
  202. rubin_sim/maf/plots/xyplotter.py +37 -0
  203. rubin_sim/maf/run_comparison/__init__.py +5 -0
  204. rubin_sim/maf/run_comparison/archive.py +584 -0
  205. rubin_sim/maf/run_comparison/gather_summaries.py +125 -0
  206. rubin_sim/maf/run_comparison/microlensing_compare.py +514 -0
  207. rubin_sim/maf/run_comparison/radar_plot.py +202 -0
  208. rubin_sim/maf/run_comparison/summary_plots.py +710 -0
  209. rubin_sim/maf/run_moving_calc.py +239 -0
  210. rubin_sim/maf/run_moving_fractions.py +128 -0
  211. rubin_sim/maf/run_moving_join.py +103 -0
  212. rubin_sim/maf/run_selfcal_metric.py +92 -0
  213. rubin_sim/maf/scimaf_dir.py +87 -0
  214. rubin_sim/maf/show_maf.py +227 -0
  215. rubin_sim/maf/slicers/__init__.py +13 -0
  216. rubin_sim/maf/slicers/base_slicer.py +513 -0
  217. rubin_sim/maf/slicers/base_spatial_slicer.py +202 -0
  218. rubin_sim/maf/slicers/healpix_sdss_slicer.py +118 -0
  219. rubin_sim/maf/slicers/healpix_slicer.py +189 -0
  220. rubin_sim/maf/slicers/healpix_subset_slicer.py +293 -0
  221. rubin_sim/maf/slicers/hourglass_slicer.py +37 -0
  222. rubin_sim/maf/slicers/mo_slicer.py +171 -0
  223. rubin_sim/maf/slicers/movie_slicer.py +288 -0
  224. rubin_sim/maf/slicers/nd_slicer.py +147 -0
  225. rubin_sim/maf/slicers/one_d_slicer.py +210 -0
  226. rubin_sim/maf/slicers/time_interval_slicers.py +283 -0
  227. rubin_sim/maf/slicers/uni_slicer.py +57 -0
  228. rubin_sim/maf/slicers/user_points_slicer.py +141 -0
  229. rubin_sim/maf/stackers/__init__.py +13 -0
  230. rubin_sim/maf/stackers/base_stacker.py +221 -0
  231. rubin_sim/maf/stackers/coord_stackers.py +224 -0
  232. rubin_sim/maf/stackers/date_stackers.py +192 -0
  233. rubin_sim/maf/stackers/general_stackers.py +683 -0
  234. rubin_sim/maf/stackers/get_col_info.py +104 -0
  235. rubin_sim/maf/stackers/label_stackers.py +114 -0
  236. rubin_sim/maf/stackers/m5_optimal_stacker.py +126 -0
  237. rubin_sim/maf/stackers/mo_phase.py +265 -0
  238. rubin_sim/maf/stackers/mo_stackers.py +486 -0
  239. rubin_sim/maf/stackers/n_follow_stacker.py +187 -0
  240. rubin_sim/maf/stackers/neo_dist_stacker.py +126 -0
  241. rubin_sim/maf/stackers/sdss_stackers.py +30 -0
  242. rubin_sim/maf/stackers/sn_stacker.py +152 -0
  243. rubin_sim/maf/stackers/teff_stacker.py +154 -0
  244. rubin_sim/maf/utils/__init__.py +6 -0
  245. rubin_sim/maf/utils/astrometry_utils.py +93 -0
  246. rubin_sim/maf/utils/generate_fov_map.py +58 -0
  247. rubin_sim/maf/utils/get_date_version.py +28 -0
  248. rubin_sim/maf/utils/maf_utils.py +212 -0
  249. rubin_sim/maf/utils/opsim_utils.py +259 -0
  250. rubin_sim/maf/utils/output_utils.py +34 -0
  251. rubin_sim/maf/utils/sn_n_sn_utils.py +894 -0
  252. rubin_sim/maf/utils/sn_utils.py +369 -0
  253. rubin_sim/maf/utils/stellar_mags.py +197 -0
  254. rubin_sim/maf/web/__init__.py +2 -0
  255. rubin_sim/maf/web/favicon.ico +0 -0
  256. rubin_sim/maf/web/maf_run_results.py +645 -0
  257. rubin_sim/maf/web/maf_tracking.py +139 -0
  258. rubin_sim/maf/web/sorttable.js +496 -0
  259. rubin_sim/maf/web/templates/allmetricresults.html +72 -0
  260. rubin_sim/maf/web/templates/configs.html +28 -0
  261. rubin_sim/maf/web/templates/macros.html +243 -0
  262. rubin_sim/maf/web/templates/master.html +294 -0
  263. rubin_sim/maf/web/templates/metricselect.html +91 -0
  264. rubin_sim/maf/web/templates/multicolor.html +147 -0
  265. rubin_sim/maf/web/templates/results.html +82 -0
  266. rubin_sim/maf/web/templates/runselect.html +58 -0
  267. rubin_sim/maf/web/templates/stats.html +50 -0
  268. rubin_sim/moving_objects/__init__.py +8 -0
  269. rubin_sim/moving_objects/base_obs.py +468 -0
  270. rubin_sim/moving_objects/cheby_fits.py +689 -0
  271. rubin_sim/moving_objects/cheby_values.py +231 -0
  272. rubin_sim/moving_objects/chebyshev_utils.py +365 -0
  273. rubin_sim/moving_objects/direct_obs.py +376 -0
  274. rubin_sim/moving_objects/make_lsst_obs.py +282 -0
  275. rubin_sim/moving_objects/ooephemerides.py +643 -0
  276. rubin_sim/moving_objects/orbits.py +507 -0
  277. rubin_sim/moving_objects/pre_generate.py +37 -0
  278. rubin_sim/moving_objects/utils.py +61 -0
  279. rubin_sim/phot_utils/__init__.py +7 -0
  280. rubin_sim/phot_utils/bandpass.py +425 -0
  281. rubin_sim/phot_utils/photometric_parameters.py +449 -0
  282. rubin_sim/phot_utils/physical_parameters.py +50 -0
  283. rubin_sim/phot_utils/predicted_zeropoints.py +245 -0
  284. rubin_sim/phot_utils/sed.py +1773 -0
  285. rubin_sim/phot_utils/sed_utils.py +52 -0
  286. rubin_sim/phot_utils/signaltonoise.py +695 -0
  287. rubin_sim/phot_utils/spectral_resampling.py +161 -0
  288. rubin_sim/satellite_constellations/__init__.py +3 -0
  289. rubin_sim/satellite_constellations/basis_function.py +40 -0
  290. rubin_sim/satellite_constellations/model_observatory.py +157 -0
  291. rubin_sim/satellite_constellations/sat_utils.py +488 -0
  292. rubin_sim/selfcal/__init__.py +4 -0
  293. rubin_sim/selfcal/generate_catalog.py +188 -0
  294. rubin_sim/selfcal/offsets.py +119 -0
  295. rubin_sim/selfcal/solver.py +143 -0
  296. rubin_sim/selfcal/star_tools.py +42 -0
  297. rubin_sim/sim_archive/__init__.py +14 -0
  298. rubin_sim/sim_archive/conda_packages.py +65 -0
  299. rubin_sim/sim_archive/future_vsarch.py +480 -0
  300. rubin_sim/sim_archive/make_snapshot.py +255 -0
  301. rubin_sim/sim_archive/prenight.py +216 -0
  302. rubin_sim/sim_archive/prenightindex.py +229 -0
  303. rubin_sim/sim_archive/prototype.py +1029 -0
  304. rubin_sim/sim_archive/sim_archive.py +680 -0
  305. rubin_sim/sim_archive/tempdb.py +56 -0
  306. rubin_sim/sim_archive/util.py +146 -0
  307. rubin_sim/sim_archive/vseqarchive.py +965 -0
  308. rubin_sim/sim_archive/vseqmetadata.py +1721 -0
  309. rubin_sim/skybrightness/__init__.py +5 -0
  310. rubin_sim/skybrightness/allsky_db.py +93 -0
  311. rubin_sim/skybrightness/data/ESO_Spectra/eso_tools.py +505 -0
  312. rubin_sim/skybrightness/data/solarSpec/package.py +22 -0
  313. rubin_sim/skybrightness/generate_hdf5.py +243 -0
  314. rubin_sim/skybrightness/interp_components.py +798 -0
  315. rubin_sim/skybrightness/sky_model.py +771 -0
  316. rubin_sim/skybrightness/twilight_func.py +83 -0
  317. rubin_sim/skybrightness/utils.py +91 -0
  318. rubin_sim-2.6.1.dev2.dist-info/METADATA +95 -0
  319. rubin_sim-2.6.1.dev2.dist-info/RECORD +387 -0
  320. rubin_sim-2.6.1.dev2.dist-info/WHEEL +5 -0
  321. rubin_sim-2.6.1.dev2.dist-info/entry_points.txt +24 -0
  322. rubin_sim-2.6.1.dev2.dist-info/licenses/LICENSE +674 -0
  323. rubin_sim-2.6.1.dev2.dist-info/top_level.txt +5 -0
  324. sql/make_vseqmeta.sql +251 -0
  325. tests/data/test_data.py +26 -0
  326. tests/data/test_ddf_grid.py +17 -0
  327. tests/maf/test_3x2fom.py +68 -0
  328. tests/maf/test_archive.py +162 -0
  329. tests/maf/test_basemetrics.py +99 -0
  330. tests/maf/test_batchcommon.py +36 -0
  331. tests/maf/test_batches.py +159 -0
  332. tests/maf/test_cadencemetrics.py +388 -0
  333. tests/maf/test_calibrationmetrics.py +242 -0
  334. tests/maf/test_color_slopes.py +71 -0
  335. tests/maf/test_cosmology_summaries.py +20 -0
  336. tests/maf/test_gathersummaries.py +48 -0
  337. tests/maf/test_healpixslicer.py +439 -0
  338. tests/maf/test_healpixsubsetslicer.py +273 -0
  339. tests/maf/test_hgplotters.py +145 -0
  340. tests/maf/test_hourglassmetric.py +33 -0
  341. tests/maf/test_io.py +187 -0
  342. tests/maf/test_json.py +169 -0
  343. tests/maf/test_maps.py +199 -0
  344. tests/maf/test_metricbundle.py +69 -0
  345. tests/maf/test_mometrics.py +422 -0
  346. tests/maf/test_movieslicer.py +216 -0
  347. tests/maf/test_ndslicer.py +240 -0
  348. tests/maf/test_neodistanceplotter.py +41 -0
  349. tests/maf/test_onedslicer.py +245 -0
  350. tests/maf/test_opsimutils.py +80 -0
  351. tests/maf/test_plotters.py +134 -0
  352. tests/maf/test_resultsdb.py +145 -0
  353. tests/maf/test_schedviewmetrics.py +14 -0
  354. tests/maf/test_simplemetrics.py +200 -0
  355. tests/maf/test_snmetrics.py +335 -0
  356. tests/maf/test_snmetrics_nsn.py +123 -0
  357. tests/maf/test_stackers.py +423 -0
  358. tests/maf/test_stellarmags.py +50 -0
  359. tests/maf/test_stringcount.py +26 -0
  360. tests/maf/test_summary_plots.py +197 -0
  361. tests/maf/test_summarymetrics.py +82 -0
  362. tests/maf/test_technicalmetrics.py +267 -0
  363. tests/maf/test_templatemetrics.py +86 -0
  364. tests/maf/test_timeintervalslicers.py +97 -0
  365. tests/maf/test_trackingdb.py +88 -0
  366. tests/maf/test_unislicer.py +116 -0
  367. tests/maf/test_vectormetrics.py +225 -0
  368. tests/maf/test_visitgroupsmetric.py +181 -0
  369. tests/moving_objects/test_camera.py +39 -0
  370. tests/moving_objects/test_chebyfits.py +188 -0
  371. tests/moving_objects/test_chebyshevutils.py +76 -0
  372. tests/moving_objects/test_chebyvalues.py +245 -0
  373. tests/moving_objects/test_ephemerides.py +219 -0
  374. tests/moving_objects/test_orbits.py +170 -0
  375. tests/moving_objects/test_sed_data.py +61 -0
  376. tests/phot_utils/test_approximatebandpasses.py +91 -0
  377. tests/phot_utils/test_photometricparameters.py +268 -0
  378. tests/phot_utils/test_photometry.py +61 -0
  379. tests/phot_utils/test_predicted_zeropoints.py +53 -0
  380. tests/phot_utils/test_read_bandpasses.py +29 -0
  381. tests/phot_utils/test_sed.py +368 -0
  382. tests/phot_utils/test_snr.py +370 -0
  383. tests/satellite_constellations/test_satellites.py +36 -0
  384. tests/sim_archive/test_make_snapshot.py +24 -0
  385. tests/sim_archive/test_prototype_sim_archive.py +204 -0
  386. tests/sim_archive/test_vseqarchive.py +947 -0
  387. tests/skybrightness/test_skymodel.py +359 -0
@@ -0,0 +1,947 @@
1
+ import datetime
2
+ import hashlib
3
+ import io
4
+ import os
5
+ import unittest
6
+ from getpass import getuser
7
+ from io import StringIO
8
+ from pathlib import Path
9
+ from tempfile import NamedTemporaryFile, TemporaryDirectory
10
+ from typing import ClassVar, cast
11
+ from uuid import UUID, uuid1
12
+
13
+ import click
14
+ import click.testing
15
+ import numpy as np
16
+ import pandas as pd
17
+ import testing.postgresql
18
+ import yaml
19
+ from astropy.time import Time
20
+ from lsst.resources import ResourcePath
21
+ from psycopg2 import sql
22
+ from rubin_scheduler.scheduler.utils import SchemaConverter
23
+
24
+ from rubin_sim.sim_archive import vseqarchive, vseqmetadata
25
+ from rubin_sim.sim_archive.prototype import export_sim_to_prototype_sim_archive
26
+ from rubin_sim.sim_archive.tempdb import LocalOnlyPostgresql
27
+
28
+ TEST_METADATA_DB_SCHEMA = "test"
29
+
30
+ # Test visits using consdb column names
31
+ TEST_VISITS = pd.read_csv(
32
+ StringIO(
33
+ """
34
+ obs_start_mjd s_ra s_dec band sky_rotation exp_time altitude azimuth
35
+ 64632.271368 51.536172 -12.851173 r 122.606347 29.2 67.439834 317.602598
36
+ 64632.271816 52.370715 -9.768649 r 124.357953 29.2 65.262546 323.291472
37
+ 64632.272265 55.094497 -9.088026 r 130.639271 29.2 65.900938 329.433578
38
+ 64633.272712 54.314320 -12.171543 r 130.159006 29.2 67.624221 321.348869
39
+ 64633.273163 57.080518 -11.470472 r 136.801494 29.2. 68.327156 328.146833
40
+ """
41
+ ),
42
+ sep=r"\s+",
43
+ )
44
+
45
+ # Test visits using opsim column names
46
+ # Taken from baseline, but used instead of baseline
47
+ # so it works when get_baseline() fails.
48
+ TEST_OBS = pd.DataFrame(
49
+ {
50
+ "ID": {0: 0, 1: 1, 2: 2},
51
+ "RA": {0: 4.565221270744618, 1: 4.587617254424366, 2: 4.569874353050399},
52
+ "dec": {0: -0.47877497696518434, 1: -0.3942901024801995, 2: -0.34708159948758066},
53
+ "mjd": {0: 60980.00162606769, 1: 60980.00187693634, 2: 60980.002122816804},
54
+ "flush_by_mjd": {0: 60980.02490014931, 1: 60980.02490014931, 2: 60980.02490014931},
55
+ "exptime": {0: 15.0, 1: 15.0, 2: 15.0},
56
+ "band": {0: "r", 1: "r", 2: "r"},
57
+ "filter": {0: "r", 1: "r", 2: "r"},
58
+ "rotSkyPos": {0: 5.876022662133265, 1: 5.919075559769473, 2: 5.94587053925843},
59
+ "rotSkyPos_desired": {0: 0.0, 1: 0.0, 2: 0.0},
60
+ "nexp": {0: 1, 1: 1, 2: 1},
61
+ "airmass": {0: 1.9425330274137829, 1: 2.0097732394514645, 2: 2.1437619807371933},
62
+ "FWHM_500": {0: 0.9789617075932842, 1: 0.9789617075932842, 2: 0.9789617075932842},
63
+ "FWHMeff": {0: 1.7561043378017294, 1: 1.7923280611783998, 2: 1.8630958886242825},
64
+ "FWHM_geometric": {0: 1.4955177656730214, 1: 1.5252936662886447, 2: 1.5834648204491601},
65
+ "skybrightness": {0: 18.571033643475822, 1: 18.499742358339535, 2: 18.4280549931739},
66
+ "night": {0: 0, 1: 0, 2: 0},
67
+ "slewtime": {0: 106.51934924053808, 1: 5.675050952948157, 2: 5.244072029689301},
68
+ "visittime": {0: 16.0, 1: 16.0, 2: 16.0},
69
+ "slewdist": {0: 1.0476600064430734, 1: 0.08688615327418507, 2: 0.05002065991954836},
70
+ "fivesigmadepth": {0: 21.929073206570653, 1: 21.863266843112314, 2: 21.768922123808583},
71
+ "alt": {0: 0.5428304424974331, 1: 0.5289410180743699, 2: 0.49345760977076153},
72
+ "az": {0: 4.437241068620033, 1: 4.536614524865317, 2: 4.57781450588731},
73
+ "pa": {0: 1.9277474700579063, 1: 1.9698101688642318, 2: 1.997014567506373},
74
+ "pseudo_pa": {0: 1.9306710506086011, 1: 1.9726118749454766, 2: 1.999754703197624},
75
+ "clouds": {0: 0.0, 1: 0.0, 2: 0.0},
76
+ "moonAlt": {0: 1.1813900553282524, 1: 1.1818643717117499, 2: 1.1823244654562768},
77
+ "sunAlt": {0: -0.21560611790270862, 1: -0.2168497474147377, 2: -0.21806791322669622},
78
+ "scheduler_note": {0: "twilight_near_sun, 0", 1: "twilight_near_sun, 0", 2: "twilight_near_sun, 0"},
79
+ "target_name": {0: "bulgy", 1: "bulgy", 2: "bulgy"},
80
+ "target_id": {0: 0, 1: 1, 2: 2},
81
+ "lmst": {0: 22.024928004140648, 1: 22.03096533614179, 2: 22.03688262412642},
82
+ "rotTelPos": {0: 0.7670373688600254, 1: 0.7659252955606934, 2: 0.7662731443238844},
83
+ "rotTelPos_backup": {0: 0.0, 1: 0.0, 2: 0.0},
84
+ "moonAz": {0: 0.4062715059631942, 1: 0.40266683110724966, 2: 0.3991218282595135},
85
+ "sunAz": {0: 4.275439688438064, 1: 4.274507556583111, 2: 4.273593129782555},
86
+ "sunRA": {0: 3.771398634838603, 1: 3.7714029138801326, 2: 3.7714071078422475},
87
+ "sunDec": {0: -0.2499715857671971, 1: -0.24997299288585925, 2: -0.24997437202179135},
88
+ "moonRA": {0: 5.912319257908991, 1: 5.912350571271447, 2: 5.912381259109779},
89
+ "moonDec": {0: -0.1677616241703147, 1: -0.16773479483758946, 2: -0.1677084921417354},
90
+ "moonDist": {0: 1.296308274027283, 1: 1.2808773106616722, 2: 1.3009217682807237},
91
+ "solarElong": {0: 0.7714313357065409, 1: 0.7844179510467449, 2: 0.7670707340378583},
92
+ "moonPhase": {0: 65.73037246219155, 1: 65.73146495686613, 2: 65.73253572916387},
93
+ "cummTelAz": {0: -1.8461922459167281, 1: -1.7467049213662156, 2: -1.7050034487399},
94
+ "observation_reason": {0: "pairs_i_5.0", 1: "pairs_i_5.0", 2: "pairs_i_5.0"},
95
+ "science_program": {0: "None", 1: "None", 2: "None"},
96
+ "cloud_extinction": {0: 0.0, 1: 0.0, 2: 0.0},
97
+ "note": {0: "", 1: "", 2: ""},
98
+ }
99
+ )
100
+
101
+
102
+ class TestVisitSequenceArchive(unittest.TestCase):
103
+ temp_dir: ClassVar[TemporaryDirectory]
104
+ test_archive_url: ClassVar[str]
105
+ test_archive: ClassVar[ResourcePath]
106
+ test_database: ClassVar[LocalOnlyPostgresql]
107
+ vsarch: ClassVar[vseqmetadata.VisitSequenceArchiveMetadata]
108
+
109
+ @classmethod
110
+ def setUpClass(cls) -> None:
111
+ try:
112
+ testing.postgresql.find_program("postgres", ["bin"])
113
+ except RuntimeError:
114
+ raise unittest.SkipTest("PostgreSQL not found, skipping visit sequence archive tests")
115
+
116
+ cls.temp_dir = TemporaryDirectory()
117
+ cls.test_archive_url = "file://" + cls.temp_dir.name + "/archive/"
118
+ cls.test_archive = ResourcePath(cls.test_archive_url)
119
+
120
+ cls.test_database = LocalOnlyPostgresql(base_dir=cls.temp_dir.name)
121
+ cls.vsarch = vseqmetadata.VisitSequenceArchiveMetadata(
122
+ metadata_db_kwargs=cls.test_database.psycopg2_dsn(),
123
+ metadata_db_schema=TEST_METADATA_DB_SCHEMA,
124
+ )
125
+ cls.vsarch.create_schema_in_database()
126
+
127
+ @classmethod
128
+ def tearDownClass(cls) -> None:
129
+ try:
130
+ if cls.test_database is not None:
131
+ cls.test_database.stop()
132
+ finally:
133
+ cls.temp_dir.cleanup()
134
+
135
+ def setUp(self) -> None:
136
+ self.start_environ: dict = {}
137
+ self.start_environ.update(os.environ)
138
+ os.environ["LSST_DISABLE_BUCKET_VALIDATION"] = "1"
139
+
140
+ def tearDown(self) -> None:
141
+ for key in os.environ:
142
+ if key not in self.start_environ:
143
+ del os.environ[key]
144
+ else:
145
+ if os.environ[key] != self.start_environ[key]:
146
+ os.environ[key] = self.start_environ[key]
147
+
148
+ def num_rows_in_table(self, archive: vseqarchive.VisitSequenceArchiveMetadata, table: str) -> int:
149
+ result = archive.query(
150
+ sql.SQL("SELECT COUNT(*) FROM {}.{};").format(
151
+ sql.Identifier(TEST_METADATA_DB_SCHEMA), sql.Identifier(table)
152
+ ),
153
+ {},
154
+ )
155
+ assert isinstance(result[0][0], int)
156
+ num_rows: int = result[0][0]
157
+ return num_rows
158
+
159
+ def num_rows_for_visitseq(
160
+ self, archive: vseqarchive.VisitSequenceArchiveMetadata, table: str, visitseq_uuid: UUID
161
+ ) -> int:
162
+ data = {"visitseq_uuid": visitseq_uuid}
163
+ result = archive.query(
164
+ sql.SQL("SELECT COUNT(*) FROM {}.{} WHERE visitseq_uuid={};").format(
165
+ sql.Identifier(TEST_METADATA_DB_SCHEMA),
166
+ sql.Identifier(table),
167
+ sql.Placeholder("visitseq_uuid"),
168
+ ),
169
+ data,
170
+ )
171
+ assert isinstance(result[0][0], int)
172
+ num_rows: int = result[0][0]
173
+ return num_rows
174
+
175
+ def test_record_visitseq_metadata(self) -> None:
176
+ # Remember how many simulations there were before we add one
177
+ original_num_sequences = self.num_rows_in_table(self.vsarch, "visitseq")
178
+
179
+ visits = TEST_VISITS
180
+ label = f"Test on {Time.now().iso}"
181
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
182
+ assert isinstance(vseq_uuid, UUID)
183
+
184
+ num_matching_seqs = self.num_rows_for_visitseq(self.vsarch, "visitseq", vseq_uuid)
185
+ assert num_matching_seqs == 1
186
+
187
+ # Verify that we added only one
188
+ new_num_sequences = self.num_rows_in_table(self.vsarch, "visitseq")
189
+ assert new_num_sequences == (original_num_sequences + 1)
190
+
191
+ def test_update_visitseq_metadata(self) -> None:
192
+ visits = TEST_VISITS
193
+ label = f"Test on {Time.now().iso}"
194
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
195
+ assert isinstance(vseq_uuid, UUID)
196
+
197
+ initial_visitseq_metadata = self.vsarch.get_visitseq_metadata(vseq_uuid)
198
+ assert initial_visitseq_metadata.first_day_obs is None
199
+ self.vsarch.update_visitseq_metadata(vseq_uuid, "first_day_obs", "2020-01-01")
200
+ visitseq_metadata_1 = self.vsarch.get_visitseq_metadata(vseq_uuid)
201
+ assert visitseq_metadata_1.first_day_obs == datetime.date(2020, 1, 1)
202
+
203
+ def test_get_set_visitseq_url(self) -> None:
204
+ with NamedTemporaryFile() as temp_file:
205
+ test_url = ResourcePath(temp_file.name).geturl()
206
+
207
+ visits = TEST_VISITS
208
+ label = f"Test on {Time.now().iso}"
209
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq", url=test_url)
210
+ return_url = self.vsarch.get_visitseq_url(vseq_uuid)
211
+ assert return_url == test_url
212
+
213
+ with NamedTemporaryFile() as temp_file:
214
+ new_test_url = ResourcePath(temp_file.name).geturl()
215
+ self.vsarch.set_visitseq_url(vseq_uuid, new_test_url)
216
+ return_url = self.vsarch.get_visitseq_url(vseq_uuid)
217
+ assert return_url != test_url
218
+ assert return_url == new_test_url
219
+
220
+ def test_record_simulation_metadata_simple(self) -> None:
221
+ # Remember how many simulations there were before we add one
222
+ original_num_sequences = self.num_rows_in_table(self.vsarch, "simulations")
223
+
224
+ visits = TEST_VISITS
225
+ label = f"Test on {Time.now().iso}"
226
+ vseq_uuid = self.vsarch.record_simulation_metadata(visits, label)
227
+ assert isinstance(vseq_uuid, UUID)
228
+
229
+ num_matching_seqs = self.num_rows_for_visitseq(self.vsarch, "simulations", vseq_uuid)
230
+ assert num_matching_seqs == 1
231
+
232
+ # Verify that we added only one
233
+ new_num_sequences = self.num_rows_in_table(self.vsarch, "simulations")
234
+ assert new_num_sequences == (original_num_sequences + 1)
235
+
236
+ def test_record_simulation_metadata_long(self) -> None:
237
+ # Remember how many simulations there were before we add one
238
+ original_num_sequences = self.num_rows_in_table(self.vsarch, "simulations")
239
+
240
+ visits = TEST_VISITS
241
+ label = f"Test on {Time.now().iso}"
242
+ first_day_obs = "2025-01-01"
243
+ sim_runner_kwargs = {"foo": 5, "bar": [1, 2, 3]}
244
+
245
+ vseq_uuid = self.vsarch.record_simulation_metadata(
246
+ visits, label, first_day_obs=first_day_obs, sim_runner_kwargs=sim_runner_kwargs
247
+ )
248
+ assert isinstance(vseq_uuid, UUID)
249
+
250
+ num_matching_seqs = self.num_rows_for_visitseq(self.vsarch, "simulations", vseq_uuid)
251
+ assert num_matching_seqs == 1
252
+
253
+ # Verify that we added only one
254
+ new_num_sequences = self.num_rows_in_table(self.vsarch, "simulations")
255
+ assert new_num_sequences == (original_num_sequences + 1)
256
+
257
+ def test_get_visitseq_metadata(self) -> None:
258
+ visits = TEST_VISITS
259
+ label = f"Test on {Time.now().iso}"
260
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
261
+ visit_seq = self.vsarch.get_visitseq_metadata(vseq_uuid)
262
+ assert visit_seq["visitseq_label"] == label
263
+
264
+ def test_record_completed_metadata(self) -> None:
265
+ # Remember how many simulations there were before we add one
266
+ original_num_sequences = self.num_rows_in_table(self.vsarch, "completed")
267
+
268
+ visits = TEST_VISITS
269
+ label = f"Test on {Time.now().iso}"
270
+ vseq_uuid = self.vsarch.record_completed_metadata(
271
+ visits, label, query="SELECT * FROM foo WHERE bar='baz';"
272
+ )
273
+ assert isinstance(vseq_uuid, UUID)
274
+
275
+ num_matching_seqs = self.num_rows_for_visitseq(self.vsarch, "completed", vseq_uuid)
276
+ assert num_matching_seqs == 1
277
+
278
+ # Verify that we added only one
279
+ new_num_sequences = self.num_rows_in_table(self.vsarch, "completed")
280
+ assert new_num_sequences == (original_num_sequences + 1)
281
+
282
+ def test_record_mixed_metadata(self) -> None:
283
+ visits = TEST_VISITS
284
+ label = f"Test on {Time.now().iso}"
285
+ last_early_day_obs = 20251201
286
+ first_late_day_obs = 20251202
287
+ early_parent_uuid = uuid1()
288
+ late_parent_uuid = uuid1()
289
+ vseq_uuid = self.vsarch.record_mixed_metadata(
290
+ visits, label, last_early_day_obs, first_late_day_obs, early_parent_uuid, late_parent_uuid
291
+ )
292
+ visit_seq = self.vsarch.get_visitseq_metadata(vseq_uuid, "mixedvisitseq")
293
+ assert visit_seq["last_early_day_obs"] == datetime.date(2025, 12, 1)
294
+ assert visit_seq["first_late_day_obs"] == datetime.date(2025, 12, 2)
295
+ assert visit_seq["early_parent_uuid"] == early_parent_uuid
296
+ assert visit_seq["late_parent_uuid"] == late_parent_uuid
297
+
298
+ def test_tagging(self) -> None:
299
+ visits = TEST_VISITS
300
+ label = f"Test on {Time.now().iso}"
301
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
302
+
303
+ result = self.vsarch.is_tagged(vseq_uuid, "test1")
304
+ assert not result
305
+
306
+ self.vsarch.tag(vseq_uuid, "test1")
307
+ result = self.vsarch.is_tagged(vseq_uuid, "test1")
308
+ assert result
309
+
310
+ self.vsarch.tag(vseq_uuid, "test2", "test3")
311
+ result = self.vsarch.is_tagged(vseq_uuid, "test2")
312
+ result = self.vsarch.is_tagged(vseq_uuid, "test3")
313
+ assert result
314
+
315
+ self.vsarch.untag(vseq_uuid, "test2")
316
+ assert not self.vsarch.is_tagged(vseq_uuid, "test2")
317
+ assert self.vsarch.is_tagged(vseq_uuid, "test3")
318
+
319
+ self.vsarch.untag(vseq_uuid, "test3")
320
+ self.vsarch.untag(vseq_uuid, "test1")
321
+
322
+ def test_comments(self) -> None:
323
+ visits = TEST_VISITS
324
+ label = f"Test on {Time.now().iso}"
325
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
326
+
327
+ # There should be no comments on our newly created visitseq
328
+ # to start with. What happens when we ask for comments?
329
+ initial_comments = self.vsarch.get_comments(vseq_uuid)
330
+ assert len(initial_comments) == 0
331
+
332
+ # Test without an author
333
+ comment1 = "This is a first comment."
334
+ self.vsarch.comment(vseq_uuid, comment1)
335
+ comments_after_one = self.vsarch.get_comments(vseq_uuid)
336
+ assert len(comments_after_one) == 1
337
+ assert comment1 in comments_after_one.comment.values
338
+
339
+ # Test with an author
340
+ comment2 = "This is a test comment"
341
+ author = "Unit Tester"
342
+ self.vsarch.comment(vseq_uuid, comment2, author=author)
343
+ comments_after_two = self.vsarch.get_comments(vseq_uuid)
344
+ assert len(comments_after_two) == 2
345
+ assert comment1 in comments_after_two.comment.values
346
+ assert comment2 in comments_after_two.comment.values
347
+ assert author in comments_after_two.author.values
348
+
349
+ def test_register_and_get_file_url(self) -> None:
350
+ # Simple test: register a file, and see if we can get the URL back.
351
+ with NamedTemporaryFile() as temp_file:
352
+ file_content = os.urandom(100)
353
+ file_sha256 = bytes.fromhex(hashlib.sha256(file_content).hexdigest())
354
+ file_rp = ResourcePath(temp_file.name)
355
+ file_url = file_rp.geturl()
356
+ file_type = "test"
357
+ test_uuid = uuid1()
358
+ self.vsarch.register_file(test_uuid, file_type, file_sha256, file_url)
359
+ returned_url = self.vsarch.get_file_url(test_uuid, file_type)
360
+ assert returned_url == file_url
361
+
362
+ returned_sha256 = self.vsarch.get_file_sha256(test_uuid, file_type)
363
+ assert returned_sha256 == file_sha256
364
+ # Make sure we cannot register a file (same UUID and type) twice
365
+ # accidentally.
366
+ with NamedTemporaryFile() as temp_file:
367
+ new_file_content = os.urandom(100)
368
+ new_file_sha256 = bytes.fromhex(hashlib.sha256(new_file_content).hexdigest())
369
+ new_file_rp = ResourcePath(temp_file.name)
370
+ new_file_url = new_file_rp.geturl()
371
+ file_type = "test"
372
+ with self.assertRaises(ValueError):
373
+ self.vsarch.register_file(test_uuid, file_type, new_file_sha256, new_file_url)
374
+
375
+ returned_url = self.vsarch.get_file_url(test_uuid, file_type)
376
+ assert returned_url != new_file_url
377
+ assert returned_url == file_url
378
+
379
+ # Now, insist we want to replace the old value.
380
+ self.vsarch.register_file(test_uuid, file_type, new_file_sha256, new_file_url, update=True)
381
+ returned_url = self.vsarch.get_file_url(test_uuid, file_type)
382
+ assert returned_url == new_file_url
383
+ assert returned_url != file_url
384
+
385
+ # Verify that passing the ResourcePath instead of a URL works
386
+ with NamedTemporaryFile() as temp_file:
387
+ file_content = os.urandom(100)
388
+ file_sha256 = bytes.fromhex(hashlib.sha256(file_content).hexdigest())
389
+ file_rp = ResourcePath(temp_file.name)
390
+ file_type = "test"
391
+ test_uuid = uuid1()
392
+ self.vsarch.register_file(test_uuid, file_type, file_sha256, file_rp)
393
+ returned_url = self.vsarch.get_file_url(test_uuid, file_type)
394
+ assert returned_url == file_rp.geturl()
395
+
396
+ # Test behavior when there are no matching files
397
+ test_uuid = uuid1()
398
+ with self.assertRaises(ValueError) as assert_raises_context:
399
+ returned_url = self.vsarch.get_file_url(test_uuid, "test")
400
+
401
+ assert assert_raises_context.exception.args[0].startswith("No URLs found for test for visitseq")
402
+
403
+ def test_nightly_stats(self) -> None:
404
+ visits = TEST_VISITS.copy()
405
+ times = Time(np.floor(visits.obs_start_mjd - 0.5), format="mjd").to_datetime()
406
+ assert isinstance(times, np.ndarray)
407
+ visits = visits.assign(day_obs=pd.Series(times).dt.date)
408
+
409
+ test_uuid = uuid1()
410
+ stats_df = vseqarchive.compute_nightly_stats(visits)
411
+ self.vsarch.insert_nightly_stats(test_uuid, stats_df)
412
+ returned_stats_df = self.vsarch.query_nightly_stats(test_uuid)
413
+ assert len(returned_stats_df) > 0
414
+ assert len(stats_df) == len(returned_stats_df)
415
+
416
+ def test_record_conda_env(self) -> None:
417
+ conda_env_hash, conda_env_json = vseqarchive.compute_conda_env()
418
+ self.vsarch.record_conda_env(conda_env_hash, conda_env_json)
419
+ assert self.vsarch.conda_env_is_saved(conda_env_hash)
420
+ assert conda_env_hash is not None
421
+
422
+ def test_archive_file(self) -> None:
423
+ test_file_type = "testbytes"
424
+ visits = TEST_VISITS
425
+ label = f"Test on {Time.now().iso}"
426
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
427
+ content = os.urandom(100)
428
+
429
+ with NamedTemporaryFile() as temp_file:
430
+ file_name = temp_file.name
431
+ temp_file.write(content)
432
+ temp_file.flush()
433
+ sent_location = vseqarchive.add_file(
434
+ self.vsarch, vseq_uuid, file_name, test_file_type, self.test_archive
435
+ )
436
+
437
+ found_location = self.vsarch.get_file_url(vseq_uuid, test_file_type)
438
+ assert found_location == sent_location.geturl()
439
+
440
+ found_content = ResourcePath(uri=found_location).read()
441
+ assert found_content == content
442
+
443
+ def test_archive_visits(self) -> None:
444
+ # Saving with the file type of visits takes a different
445
+ # path through the code, so test it separately.
446
+ test_file_type = "visits"
447
+ visits = TEST_VISITS
448
+ label = f"Test on {Time.now().iso}"
449
+ vseq_uuid = self.vsarch.record_visitseq_metadata(visits, label, table="visitseq")
450
+
451
+ with NamedTemporaryFile() as temp_file:
452
+ visits.to_hdf(temp_file.name, key="observations")
453
+ file_name = temp_file.name
454
+ sent_location = vseqarchive.add_file(
455
+ self.vsarch, vseq_uuid, file_name, test_file_type, self.test_archive
456
+ )
457
+
458
+ found_location = self.vsarch.get_file_url(vseq_uuid, test_file_type)
459
+ assert found_location == sent_location.geturl()
460
+
461
+ visits_found_location = self.vsarch.get_visitseq_url(vseq_uuid)
462
+ assert visits_found_location == sent_location.geturl()
463
+
464
+ reread_visits = vseqarchive.get_visits(found_location)
465
+ pd.testing.assert_frame_equal(reread_visits, visits)
466
+
467
+ max_obs_start_mjd = visits.obs_start_mjd.max()
468
+ max_visits = vseqarchive.get_visits(
469
+ found_location, query=f"obs_start_mjd > {max_obs_start_mjd - 0.0001}"
470
+ )
471
+ assert len(max_visits) == 1
472
+
473
+ def test_sims_on_nights(self) -> None:
474
+ # Make a tag for just this execution of this test:
475
+ test_tag = str(uuid1())
476
+
477
+ first_day_obs = "2025-12-01"
478
+ last_day_obs = "2025-12-03"
479
+ tags = (test_tag,)
480
+
481
+ # Verify that we start off with no sims matching
482
+ # our test tag.
483
+ result = self.vsarch.sims_on_nights(first_day_obs, last_day_obs, tags)
484
+ assert len(result) == 0
485
+
486
+ # Add a sim with our tag, and verify we see it
487
+ sim_uuids = []
488
+ sim_uuid = self.vsarch.record_simulation_metadata(
489
+ TEST_VISITS,
490
+ f"Test {test_tag} on {Time.now().iso}",
491
+ telescope="simonyi",
492
+ first_day_obs="2025-12-01",
493
+ last_day_obs="2025-12-03",
494
+ )
495
+ sim_uuids.append(sim_uuid)
496
+ self.vsarch.tag(sim_uuid, *tags)
497
+ result = self.vsarch.sims_on_nights(first_day_obs, last_day_obs, tags)
498
+ assert len(result) == 1
499
+ assert result.visitseq_uuid[0] == sim_uuid
500
+
501
+ # Make sure we do not get it back if we ask for a tag it
502
+ # does not have
503
+ result = self.vsarch.sims_on_nights(first_day_obs, last_day_obs, tags + ("extra",))
504
+ assert len(result) == 0
505
+
506
+ # Add another sim with a wider time range, and make sure
507
+ # we get both back.
508
+ sim_uuid = self.vsarch.record_simulation_metadata(
509
+ TEST_VISITS,
510
+ f"Test {test_tag} on {Time.now().iso}",
511
+ telescope="simonyi",
512
+ first_day_obs="2025-11-15",
513
+ last_day_obs="2025-12-30",
514
+ )
515
+ sim_uuids.append(sim_uuid)
516
+ self.vsarch.tag(sim_uuid, *tags)
517
+ result = self.vsarch.sims_on_nights(first_day_obs, last_day_obs, tags)
518
+ assert set(sim_uuids) == set(result.visitseq_uuid)
519
+
520
+ # Shift the time range we request to get only the second
521
+ # we added
522
+ result = self.vsarch.sims_on_nights("2025-12-10", tags=tags)
523
+ assert len(result) == 1
524
+ assert sim_uuids[-1] == result.visitseq_uuid[0]
525
+
526
+ # Make sure we do not see this if we ask for auxtel
527
+ result = self.vsarch.sims_on_nights(first_day_obs, last_day_obs, tags, telescope="auxtel")
528
+ assert len(result) == 0
529
+
530
+ def test_proto_export_import(self) -> None:
531
+ # ***** Create a test simulation
532
+ label = f"Test on {Time.now().iso}"
533
+ first_day_obs = "2025-01-01"
534
+ last_day_obs = "2025-01-05"
535
+ sim_runner_kwargs = {"foo": 5, "bar": [1, 2, 3]}
536
+
537
+ sim_uuid = self.vsarch.record_simulation_metadata(
538
+ TEST_VISITS,
539
+ label,
540
+ first_day_obs=first_day_obs,
541
+ last_day_obs=last_day_obs,
542
+ sim_runner_kwargs=sim_runner_kwargs,
543
+ )
544
+ in_tags = ["prototest1", "proto"]
545
+ self.vsarch.tag(sim_uuid, *in_tags)
546
+
547
+ with NamedTemporaryFile() as temp_file:
548
+ TEST_VISITS.to_hdf(temp_file.name, key="observations")
549
+ file_name = temp_file.name
550
+ vseqarchive.add_file(self.vsarch, sim_uuid, file_name, "visits", self.test_archive)
551
+
552
+ # Include an attached file
553
+ content = os.urandom(100)
554
+ test_file_type = "testbytes"
555
+ with NamedTemporaryFile() as temp_file:
556
+ file_name = temp_file.name
557
+ temp_file.write(content)
558
+ temp_file.flush()
559
+ vseqarchive.add_file(self.vsarch, sim_uuid, file_name, test_file_type, self.test_archive)
560
+
561
+ # ***** Send it to a prototype archive
562
+ proto_archive_url = "file://" + self.temp_dir.name + "/proto/"
563
+ proto_sim_rp = export_sim_to_prototype_sim_archive(self.vsarch, sim_uuid, proto_archive_url)
564
+ assert proto_sim_rp.isdir()
565
+ assert proto_sim_rp.exists()
566
+ proto_sim_index = proto_sim_rp.ospath.split("/")[-2]
567
+ assert proto_sim_index.isdigit()
568
+ today = datetime.datetime.now().date().isoformat()
569
+ assert proto_sim_rp.ospath.split("/")[-3] == today
570
+
571
+ metadata_yaml_rp = proto_sim_rp.join("sim_metadata.yaml")
572
+ assert metadata_yaml_rp.exists()
573
+ read_metadata = yaml.safe_load(metadata_yaml_rp.read().decode("UTF-8"))
574
+
575
+ assert set(read_metadata["tags"]) == set(in_tags)
576
+ assert read_metadata["label"] == label
577
+ assert read_metadata["uuid"] == str(sim_uuid)
578
+ read_bytes = ResourcePath(read_metadata["files"]["testbytes"]["url"]).read()
579
+ assert read_bytes == content
580
+
581
+ # ***** Transfer it back out of the prototype archive into
582
+ # a new entry in the vseqarchive
583
+ new_sim_uuid = self.vsarch.import_sim_from_prototype_sim_archive(
584
+ self.test_archive_url, today, proto_sim_index, proto_archive_url
585
+ )
586
+ round_trip_metadata = self.vsarch.get_visitseq_metadata(new_sim_uuid, "simulations_extra")
587
+ assert round_trip_metadata["files"]["testbytes"] == read_metadata["files"]["testbytes"]["url"]
588
+ assert set(["from_prototype_sim_archive"] + in_tags) == set(round_trip_metadata["tags"])
589
+
590
+ def run_click_command(self, command: list[str]) -> str:
591
+ # Wrapper around click's testing tools that sets up
592
+ # the environment to point at the tests and runs
593
+ # a command in that environment.
594
+
595
+ dsn = self.test_database.psycopg2_dsn()
596
+ env = {
597
+ "VSARCHIVE_PGDATABASE": dsn["database"],
598
+ "VSARCHIVE_PGHOST": dsn["host"],
599
+ "VSARCHIVE_PGPORT": str(dsn["port"]),
600
+ "VSARCHIVE_PGUSER": getuser(),
601
+ "VSARCHIVE_PGSCHEMA": TEST_METADATA_DB_SCHEMA,
602
+ }
603
+
604
+ runner = click.testing.CliRunner()
605
+ result = runner.invoke(vseqarchive.vseqarchive, command, env=env)
606
+
607
+ # Verify the command ran successfully.
608
+ self.assertEqual(result.exit_code, 0, msg=result.output)
609
+ return result.output
610
+
611
+ def test_cli(self) -> None:
612
+ # Create a temporary HDF5 file with a ``visits`` key
613
+ # and test recording it.
614
+ with NamedTemporaryFile(suffix=".h5") as temp_file:
615
+ # Make a dummy data file
616
+
617
+ TEST_VISITS.to_hdf(temp_file.name, key="observations", mode="w")
618
+
619
+ table_name = "simulations"
620
+ label = f"CLI test {Time.now().iso}"
621
+ url = ResourcePath(temp_file.name).geturl()
622
+ record_command = [
623
+ "record-visitseq-metadata",
624
+ table_name,
625
+ temp_file.name,
626
+ label,
627
+ "--telescope",
628
+ "simonyi",
629
+ "--url",
630
+ url,
631
+ ]
632
+
633
+ output = self.run_click_command(record_command)
634
+
635
+ # Extract the UUID that was printed.
636
+ uuid_str = output.strip()
637
+ visitseq_uuid = UUID(uuid_str)
638
+
639
+ # Verify that the stored label matches what we supplied.
640
+ stored_metadata = self.vsarch.get_visitseq_metadata(visitseq_uuid, table=table_name)
641
+ self.assertEqual(stored_metadata["visitseq_label"], label)
642
+
643
+ # Archive the visits themselves
644
+ archive_visits_command = [
645
+ "archive-file",
646
+ uuid_str,
647
+ temp_file.name,
648
+ "visits",
649
+ "--archive-base",
650
+ self.test_archive_url,
651
+ ]
652
+ output = self.run_click_command(archive_visits_command)
653
+ archived_url = output.strip()
654
+ assert archived_url.startswith(self.test_archive_url)
655
+ assert archived_url.endswith(Path(temp_file.name).name)
656
+
657
+ # Compute and add nightly statistics
658
+ nightly_stats_command = [
659
+ "add-nightly-stats",
660
+ uuid_str,
661
+ temp_file.name,
662
+ ]
663
+ output = self.run_click_command(nightly_stats_command)
664
+ stats_from_output = pd.read_csv(io.StringIO(output), sep="\t")
665
+ assert len(stats_from_output) > 0
666
+
667
+ query_nightly_stats_command = ["query-nightly-stats", uuid_str]
668
+ query_output = self.run_click_command(query_nightly_stats_command)
669
+ returned_stats = pd.read_csv(io.StringIO(query_output), sep="\t")
670
+ assert len(returned_stats) == len(stats_from_output)
671
+
672
+ # Get the file out of the archive, and test it against
673
+ # what we sent.
674
+ with TemporaryDirectory() as temp_dir:
675
+ visits_fname = str(Path(temp_dir).joinpath("visits.h5"))
676
+ get_visits_command = ["get-file", uuid_str, "visits", visits_fname]
677
+ self.run_click_command(get_visits_command)
678
+ read_visits = pd.read_hdf(visits_fname, "observations")
679
+ assert isinstance(read_visits, pd.DataFrame)
680
+ pd.testing.assert_frame_equal(read_visits, TEST_VISITS)
681
+
682
+ # Attach a different file and see if we can get it back
683
+ test_content = "I am some test content."
684
+ with TemporaryDirectory() as temp_dir:
685
+ origin_fname = str(Path(temp_dir).joinpath("origin.txt"))
686
+ with open(origin_fname, "w") as origin_fp:
687
+ origin_fp.write(test_content)
688
+ arch_file_command = [
689
+ "archive-file",
690
+ uuid_str,
691
+ origin_fname,
692
+ "test",
693
+ "--archive-base",
694
+ self.test_archive_url,
695
+ ]
696
+ self.run_click_command(arch_file_command)
697
+ dest_fname = str(Path(temp_dir).joinpath("destination.txt"))
698
+ get_file_command = ["get-file", uuid_str, "test", dest_fname]
699
+ self.run_click_command(get_file_command)
700
+ with open(dest_fname) as dest_fp:
701
+ returned_content = dest_fp.read()
702
+ assert returned_content == test_content
703
+
704
+ # Test getting the URL back from the metadata database
705
+ get_url_command = [
706
+ "get-visitseq-url",
707
+ uuid_str,
708
+ ]
709
+ first_get_url_output = self.run_click_command(get_url_command)
710
+ first_returned_url = first_get_url_output.strip()
711
+ assert first_returned_url == archived_url
712
+
713
+ # Test changing other metadata.
714
+ # Use first_day_obs as an example
715
+ assert stored_metadata.first_day_obs is None
716
+ test_day_obs_datetime = cast(
717
+ datetime.datetime,
718
+ Time(TEST_VISITS.obs_start_mjd.min() - 0.5, format="mjd").datetime,
719
+ )
720
+ test_day_obs_str = test_day_obs_datetime.date().isoformat()
721
+ update_first_day_obs_command = [
722
+ "update-visitseq-metadata",
723
+ uuid_str,
724
+ "first_day_obs",
725
+ test_day_obs_str,
726
+ ]
727
+ self.run_click_command(update_first_day_obs_command)
728
+ updated_metadata = self.vsarch.get_visitseq_metadata(visitseq_uuid, table=table_name)
729
+ assert updated_metadata.first_day_obs.isoformat() == test_day_obs_str
730
+
731
+ # This will be needed when testing prenight index creation.
732
+ update_last_day_obs_command = [
733
+ "update-visitseq-metadata",
734
+ uuid_str,
735
+ "last_day_obs",
736
+ test_day_obs_str,
737
+ ]
738
+ self.run_click_command(update_last_day_obs_command)
739
+
740
+ #
741
+ # Test tagging and untagging
742
+ #
743
+
744
+ sample_tags = ["testtag1", "testtag2", "testtag3"]
745
+
746
+ # Check that a sample tag is not there
747
+ is_tagged1_command = ["is-tagged", uuid_str, sample_tags[0]]
748
+ is_tagged_output = self.run_click_command(is_tagged1_command)
749
+ is_tagged = is_tagged_output.strip()
750
+ assert is_tagged == "false"
751
+
752
+ # add our sample tags, and verify that they are there
753
+ tag_command = ["tag", uuid_str] + sample_tags
754
+ self.run_click_command(tag_command)
755
+ for tag in sample_tags:
756
+ is_tagged_command = ["is-tagged", uuid_str, tag]
757
+ assert self.run_click_command(is_tagged_command).strip() == "true"
758
+
759
+ # drop a sample tag, and check that it is gone.
760
+ untag_command = ["untag", uuid_str, sample_tags[0]]
761
+ self.run_click_command(untag_command)
762
+ is_tagged_command = ["is-tagged", uuid_str, sample_tags[0]]
763
+ assert self.run_click_command(is_tagged_command).strip() == "false"
764
+
765
+ #
766
+ # Test adding comments
767
+ #
768
+
769
+ # Verify that there are no comments before we've added any.
770
+ get_comments_command = ["get-comments", uuid_str]
771
+ init_comments = self.run_click_command(get_comments_command)
772
+ assert len(init_comments.strip()) == 0
773
+
774
+ # Add a comment
775
+ test_comment = "My first test comment."
776
+ add_first_comment_command = ["comment", uuid_str, test_comment]
777
+ self.run_click_command(add_first_comment_command)
778
+ after_first_test_comment = self.run_click_command(get_comments_command)
779
+ assert test_comment in after_first_test_comment
780
+
781
+ next_test_comment = "My second test comment."
782
+ add_second_comment_command = ["comment", uuid_str, next_test_comment]
783
+ self.run_click_command(add_second_comment_command)
784
+ after_second_test_comment = self.run_click_command(get_comments_command)
785
+ assert test_comment in after_second_test_comment
786
+ assert next_test_comment in after_second_test_comment
787
+
788
+ # Make a temp to test prenight index creation.
789
+ with TemporaryDirectory() as temp_dir:
790
+ # Tag the sim as a prenight so it will be picked up
791
+ tag_command = ["tag", uuid_str, "prenight"]
792
+ self.run_click_command(tag_command)
793
+
794
+ # Test making a prenight index
795
+ prenight_index_base_rp = ResourcePath(temp_dir).join("prenight", forceDirectory=True)
796
+ make_prenight_index_command = [
797
+ "make-prenight-index",
798
+ test_day_obs_str,
799
+ "simonyi",
800
+ "--destination",
801
+ prenight_index_base_rp.geturl(),
802
+ ]
803
+ make_index_output = self.run_click_command(make_prenight_index_command)
804
+ prenight_index_url = make_index_output.strip()
805
+ prenight_index_df = pd.read_json(ResourcePath(prenight_index_url).ospath, orient="index")
806
+ assert prenight_index_df.loc[uuid_str, "first_day_obs"] == test_day_obs_str
807
+
808
+ # Test exporting a sim to a prototype archive,
809
+ # and importing it again.
810
+
811
+ # Make a temp archive for the test proto archive.
812
+ with TemporaryDirectory() as temp_dir:
813
+ proto_base_rp = ResourcePath(temp_dir).join("proto", forceDirectory=True)
814
+ proto_base_url = proto_base_rp.geturl()
815
+
816
+ # Write a test sim into the test proto archive
817
+ output = self.run_click_command(
818
+ ["export-proto", uuid_str, "--proto-sim-archive-url", proto_base_url]
819
+ )
820
+ exported_url = output.strip()
821
+
822
+ expected_date = datetime.datetime.now(datetime.UTC).date().isoformat()
823
+ expected_exported_url = proto_base_rp.join(expected_date).join("1", forceDirectory=True).geturl()
824
+ assert exported_url == expected_exported_url
825
+
826
+ # Import the test sim back from the proto archive
827
+ output = self.run_click_command(
828
+ [
829
+ "import-proto",
830
+ self.test_archive_url,
831
+ expected_date,
832
+ "1",
833
+ "--proto-sim-archive-url",
834
+ proto_base_url,
835
+ ]
836
+ )
837
+ # Make sure we get back a valid UUID
838
+ UUID(output.strip())
839
+
840
+ # Test setting the URL to something else.
841
+ new_url = f"{url}/new_extra_stuff"
842
+ set_url_command = ["set-visitseq-url", uuid_str, new_url]
843
+ self.run_click_command(set_url_command)
844
+ new_get_url_output = self.run_click_command(get_url_command)
845
+ new_returned_url = new_get_url_output.strip()
846
+ assert new_returned_url != first_returned_url
847
+ assert new_returned_url == new_url
848
+
849
+ def test_record_conda_env_cli(self) -> None:
850
+ # Separate this from the other CLI testing
851
+ # because it is much slower than anything
852
+ # else, and we may want to disable this
853
+ # test independenty of the others.
854
+ output = self.run_click_command(["record-conda-env"])
855
+ hash_hex = output.strip()
856
+ hash_sha256 = bytes.fromhex(hash_hex)
857
+
858
+ query = sql.SQL(
859
+ """
860
+ SELECT package_version
861
+ FROM {}.conda_packages
862
+ WHERE conda_env_hash={} AND package_name='numpy'
863
+ """
864
+ ).format(sql.Identifier(TEST_METADATA_DB_SCHEMA), sql.Placeholder("hash_sha256"))
865
+ data = {"hash_sha256": hash_sha256}
866
+ found_versions = self.vsarch.query(query, data)
867
+ assert len(found_versions) == 1
868
+ found_version = found_versions[0][0]
869
+ assert found_version == np.__version__
870
+
871
+ # Associate it with a simulation, and query
872
+ # it from the simulation
873
+ # Begin by adding a simulation
874
+ visits = TEST_VISITS
875
+ label = f"Test on {Time.now().iso}"
876
+ vseq_uuid = self.vsarch.record_simulation_metadata(visits, label)
877
+
878
+ # Update the simulation to refer to the conda env
879
+ update_sim_conda_env_command = [
880
+ "update-visitseq-metadata",
881
+ str(vseq_uuid),
882
+ "conda_env_sha256",
883
+ hash_hex,
884
+ ]
885
+ self.run_click_command(update_sim_conda_env_command)
886
+
887
+ query = sql.SQL(
888
+ """
889
+ SELECT package_version
890
+ FROM {}.simulation_packages
891
+ WHERE package_name='numpy' AND visitseq_uuid={}
892
+ """
893
+ ).format(
894
+ sql.Identifier(TEST_METADATA_DB_SCHEMA),
895
+ sql.Placeholder("visitseq_uuid"),
896
+ )
897
+ package_query_data = {"visitseq_uuid": vseq_uuid}
898
+ found_versions = self.vsarch.query(query, package_query_data)
899
+ assert len(found_versions) == 1
900
+ found_version = found_versions[0][0]
901
+ assert found_version == np.__version__
902
+
903
+ def test_opsim_cli(self) -> None:
904
+ # Test that we can archive and retrieve visits
905
+ # in the sqlite3 format.
906
+
907
+ # Make a sample file in opsim sqlite3 database format
908
+ schema_converter = SchemaConverter()
909
+ db_file_name = str(Path(self.temp_dir.name).joinpath("test_opsim_cli_opsim.db"))
910
+ schema_converter.obs2opsim(TEST_OBS, db_file_name)
911
+
912
+ # Make a visit sequence and send the visits
913
+ # attached to it.
914
+ label = f"CLI test {Time.now().iso}"
915
+ record_command = [
916
+ "record-visitseq-metadata",
917
+ "visitseq",
918
+ db_file_name,
919
+ label,
920
+ "--telescope",
921
+ "simonyi",
922
+ ]
923
+ uuid_str = self.run_click_command(record_command).strip()
924
+
925
+ # Archive the visits from the sqlite3 file
926
+ archive_visits_command = [
927
+ "archive-file",
928
+ uuid_str,
929
+ db_file_name,
930
+ "visits",
931
+ "--archive-base",
932
+ self.test_archive_url,
933
+ ]
934
+ self.run_click_command(archive_visits_command)
935
+
936
+ # Get it back as an h5 file
937
+ h5_file_name = str(Path(self.temp_dir.name).joinpath("test_opsim_cli_returned.h5"))
938
+ get_h5_visits_command = ["get-file", uuid_str, "visits", h5_file_name]
939
+ self.run_click_command(get_h5_visits_command)
940
+ returned_visits_h5 = pd.read_hdf(h5_file_name, "observations")
941
+ assert len(returned_visits_h5) == len(TEST_OBS)
942
+
943
+ ret_db_file_name = str(Path(self.temp_dir.name).joinpath("test_opsim_cli_returned.db"))
944
+ get_opsim_visits_command = ["get-file", uuid_str, "visits", ret_db_file_name]
945
+ self.run_click_command(get_opsim_visits_command)
946
+ ret_obs = schema_converter.opsim2obs(ret_db_file_name)
947
+ assert len(ret_obs) == len(TEST_OBS)