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.
- batch/compile_prenight_metadata_cache.sh +34 -0
- batch/run_auxtel_prenight_sims.sh +163 -0
- batch/run_prenight_sims.sh +239 -0
- docs/.gitignore +3 -0
- docs/Makefile +32 -0
- docs/api.rst +25 -0
- docs/conf.py +8 -0
- docs/data-api.rst +12 -0
- docs/data-download.rst +70 -0
- docs/developer.rst +14 -0
- docs/documenteer.toml +11 -0
- docs/index.rst +22 -0
- docs/installation.rst +76 -0
- docs/introduction.rst +37 -0
- docs/maf-api-batches.rst +12 -0
- docs/maf-api-db.rst +12 -0
- docs/maf-api-maf-contrib.rst +12 -0
- docs/maf-api-maps.rst +12 -0
- docs/maf-api-metricbundles.rst +12 -0
- docs/maf-api-metrics.rst +12 -0
- docs/maf-api-plots.rst +12 -0
- docs/maf-api-run-comparison.rst +12 -0
- docs/maf-api-slicers.rst +12 -0
- docs/maf-api-stackers.rst +12 -0
- docs/maf-api-utils.rst +12 -0
- docs/maf-api.rst +22 -0
- docs/maf.rst +60 -0
- docs/metric_list.py +49 -0
- docs/moving-objects-api.rst +12 -0
- docs/moving-objects.rst +22 -0
- docs/phot-utils-api.rst +12 -0
- docs/phot-utils.rst +11 -0
- docs/prenight.rst +117 -0
- docs/protoarchive.rst +276 -0
- docs/satellite-constellations-api.rst +12 -0
- docs/satellite-constellations.rst +13 -0
- docs/selfcal-api.rst +12 -0
- docs/selfcal.rst +15 -0
- docs/sim-archive-api.rst +12 -0
- docs/skybrightness-api.rst +12 -0
- docs/skybrightness.rst +23 -0
- docs/user-guide.rst +28 -0
- docs/vseqarchive-architecture.rst +438 -0
- docs/vseqarchive-setup.rst +238 -0
- docs/vseqarchive.rst +225 -0
- rubin_sim/__init__.py +7 -0
- rubin_sim/data/__init__.py +1 -0
- rubin_sim/data/rs_download_data.py +155 -0
- rubin_sim/maf/__init__.py +34 -0
- rubin_sim/maf/batches/__init__.py +18 -0
- rubin_sim/maf/batches/altaz_batch.py +161 -0
- rubin_sim/maf/batches/col_map_dict.py +274 -0
- rubin_sim/maf/batches/common.py +388 -0
- rubin_sim/maf/batches/ddf_batch.py +728 -0
- rubin_sim/maf/batches/filterchange_batch.py +182 -0
- rubin_sim/maf/batches/glance_batch.py +591 -0
- rubin_sim/maf/batches/hourglass_batch.py +79 -0
- rubin_sim/maf/batches/info_batch.py +114 -0
- rubin_sim/maf/batches/metadata_batch.py +558 -0
- rubin_sim/maf/batches/moving_objects_batch.py +1587 -0
- rubin_sim/maf/batches/openshutter_batch.py +100 -0
- rubin_sim/maf/batches/radar_limited.py +1100 -0
- rubin_sim/maf/batches/science_radar_batch.py +1910 -0
- rubin_sim/maf/batches/skycoverage.py +155 -0
- rubin_sim/maf/batches/slew_batch.py +146 -0
- rubin_sim/maf/batches/srd_batch.py +637 -0
- rubin_sim/maf/batches/time_batch.py +757 -0
- rubin_sim/maf/batches/time_sci_batch.py +105 -0
- rubin_sim/maf/batches/visitdepth_batch.py +485 -0
- rubin_sim/maf/db/__init__.py +2 -0
- rubin_sim/maf/db/add_run.py +34 -0
- rubin_sim/maf/db/results_db.py +1074 -0
- rubin_sim/maf/db/tracking_db.py +332 -0
- rubin_sim/maf/ddf_dir.py +53 -0
- rubin_sim/maf/generate_ss.py +218 -0
- rubin_sim/maf/glance_dir.py +58 -0
- rubin_sim/maf/maf_contrib/__init__.py +25 -0
- rubin_sim/maf/maf_contrib/calculate_lsst_field_visibility_astropy.py +170 -0
- rubin_sim/maf/maf_contrib/depth_limited_num_gal_metric.py +109 -0
- rubin_sim/maf/maf_contrib/example_new_metrics.py +40 -0
- rubin_sim/maf/maf_contrib/filter_pair_t_gaps_metric.py +208 -0
- rubin_sim/maf/maf_contrib/grb_transient_metric.py +249 -0
- rubin_sim/maf/maf_contrib/gw170817_det_metric.py +72 -0
- rubin_sim/maf/maf_contrib/intervals_between_obs_metric.py +68 -0
- rubin_sim/maf/maf_contrib/kne_metrics.py +473 -0
- rubin_sim/maf/maf_contrib/lss_metrics.py +45 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/__init__.py +3 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/constants_for_pipeline.py +73 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_metric_extended.py +205 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_with_pixel_calibration.py +171 -0
- rubin_sim/maf/maf_contrib/lv_dwarfs/__init__.py +1 -0
- rubin_sim/maf/maf_contrib/lv_dwarfs/lv_dwarfs_metrics.py +388 -0
- rubin_sim/maf/maf_contrib/microlensing_metric.py +593 -0
- rubin_sim/maf/maf_contrib/num_obs_in_survey_time_overlap_metric.py +43 -0
- rubin_sim/maf/maf_contrib/periodic_metric.py +51 -0
- rubin_sim/maf/maf_contrib/periodic_star_metric.py +186 -0
- rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py +236 -0
- rubin_sim/maf/maf_contrib/presto_color_kne_pop_metric.py +441 -0
- rubin_sim/maf/maf_contrib/selfcal_uniformity_metric.py +171 -0
- rubin_sim/maf/maf_contrib/star_count_mass_metric.py +64 -0
- rubin_sim/maf/maf_contrib/star_count_metric.py +56 -0
- rubin_sim/maf/maf_contrib/star_counts/__init__.py +8 -0
- rubin_sim/maf/maf_contrib/star_counts/abs_mag.py +324 -0
- rubin_sim/maf/maf_contrib/star_counts/coords.py +99 -0
- rubin_sim/maf/maf_contrib/star_counts/readme.txt +1 -0
- rubin_sim/maf/maf_contrib/star_counts/spec_type.py +91 -0
- rubin_sim/maf/maf_contrib/star_counts/starcount.py +30 -0
- rubin_sim/maf/maf_contrib/star_counts/starcount_bymass.py +93 -0
- rubin_sim/maf/maf_contrib/star_counts/stellardensity.py +59 -0
- rubin_sim/maf/maf_contrib/static_probes_fom_summary_metric.py +386 -0
- rubin_sim/maf/maf_contrib/tdes_pop_metric.py +362 -0
- rubin_sim/maf/maf_contrib/transient_ascii_sed_metric.py +652 -0
- rubin_sim/maf/maf_contrib/triplet_metric.py +156 -0
- rubin_sim/maf/maf_contrib/var_depth_metric.py +157 -0
- rubin_sim/maf/maf_contrib/xrb_metrics.py +523 -0
- rubin_sim/maf/maf_contrib/young_stellar_objects_metric.py +200 -0
- rubin_sim/maf/maf_night_report.py +200 -0
- rubin_sim/maf/make_fbs_tracking_db.py +90 -0
- rubin_sim/maf/maps/__init__.py +8 -0
- rubin_sim/maf/maps/base_map.py +72 -0
- rubin_sim/maf/maps/create_gaia_density_map.py +158 -0
- rubin_sim/maf/maps/dust_map.py +66 -0
- rubin_sim/maf/maps/dust_map_3d.py +190 -0
- rubin_sim/maf/maps/ebv_3d_hp.py +170 -0
- rubin_sim/maf/maps/ebv_hp.py +64 -0
- rubin_sim/maf/maps/gal_coords_map.py +22 -0
- rubin_sim/maf/maps/galactic_plane_priority_maps.py +210 -0
- rubin_sim/maf/maps/stellar_density_map.py +73 -0
- rubin_sim/maf/maps/trilegal_map.py +82 -0
- rubin_sim/maf/metadata_dir.py +90 -0
- rubin_sim/maf/metric_bundles/__init__.py +3 -0
- rubin_sim/maf/metric_bundles/metric_bundle.py +816 -0
- rubin_sim/maf/metric_bundles/metric_bundle_group.py +834 -0
- rubin_sim/maf/metric_bundles/mo_metric_bundle.py +646 -0
- rubin_sim/maf/metrics/__init__.py +52 -0
- rubin_sim/maf/metrics/agn_time_lag_metric.py +118 -0
- rubin_sim/maf/metrics/agnstructure.py +141 -0
- rubin_sim/maf/metrics/area_summary_metrics.py +120 -0
- rubin_sim/maf/metrics/base_metric.py +215 -0
- rubin_sim/maf/metrics/brown_dwarf_metric.py +162 -0
- rubin_sim/maf/metrics/cadence_metrics.py +400 -0
- rubin_sim/maf/metrics/calibration_metrics.py +618 -0
- rubin_sim/maf/metrics/chip_vendor_metric.py +43 -0
- rubin_sim/maf/metrics/color_slope_metrics.py +189 -0
- rubin_sim/maf/metrics/cosmology_summary_metrics.py +1212 -0
- rubin_sim/maf/metrics/coverage_metric.py +48 -0
- rubin_sim/maf/metrics/crowding_metric.py +255 -0
- rubin_sim/maf/metrics/cumulative_metric.py +46 -0
- rubin_sim/maf/metrics/dcr_metric.py +93 -0
- rubin_sim/maf/metrics/exgal_m5.py +57 -0
- rubin_sim/maf/metrics/fft_metric.py +27 -0
- rubin_sim/maf/metrics/galactic_plane_metrics.py +375 -0
- rubin_sim/maf/metrics/galplane_time_sampling_metrics.py +236 -0
- rubin_sim/maf/metrics/hourglass_metric.py +130 -0
- rubin_sim/maf/metrics/incremental_template_metric.py +255 -0
- rubin_sim/maf/metrics/kuiper_metrics.py +26 -0
- rubin_sim/maf/metrics/mo_metrics.py +1496 -0
- rubin_sim/maf/metrics/mo_summary_metrics.py +464 -0
- rubin_sim/maf/metrics/night_pointing_metric.py +70 -0
- rubin_sim/maf/metrics/optimal_m5_metric.py +94 -0
- rubin_sim/maf/metrics/pair_metric.py +69 -0
- rubin_sim/maf/metrics/periodic_detect_metric.py +133 -0
- rubin_sim/maf/metrics/phase_gap_metric.py +172 -0
- rubin_sim/maf/metrics/qso_number_counts_metric.py +138 -0
- rubin_sim/maf/metrics/scaling_metrics.py +148 -0
- rubin_sim/maf/metrics/schedview_metrics.py +39 -0
- rubin_sim/maf/metrics/season_metrics.py +344 -0
- rubin_sim/maf/metrics/simple_metrics.py +546 -0
- rubin_sim/maf/metrics/sky_sat_metric.py +18 -0
- rubin_sim/maf/metrics/sn_cadence_metric.py +104 -0
- rubin_sim/maf/metrics/sn_n_sn_metric.py +1364 -0
- rubin_sim/maf/metrics/sn_sl_metric.py +216 -0
- rubin_sim/maf/metrics/sn_snr_metric.py +715 -0
- rubin_sim/maf/metrics/snr_weight.py +24 -0
- rubin_sim/maf/metrics/star_density.py +77 -0
- rubin_sim/maf/metrics/string_count_metric.py +95 -0
- rubin_sim/maf/metrics/summary_metrics.py +201 -0
- rubin_sim/maf/metrics/surfb_metric.py +175 -0
- rubin_sim/maf/metrics/technical_metrics.py +271 -0
- rubin_sim/maf/metrics/tgaps.py +412 -0
- rubin_sim/maf/metrics/tomography_models.py +2034 -0
- rubin_sim/maf/metrics/transient_metrics.py +268 -0
- rubin_sim/maf/metrics/uniformity_metrics.py +418 -0
- rubin_sim/maf/metrics/use_metrics.py +55 -0
- rubin_sim/maf/metrics/vector_metrics.py +190 -0
- rubin_sim/maf/metrics/visit_groups_metric.py +341 -0
- rubin_sim/maf/metrics/weak_lensing_systematics_metric.py +210 -0
- rubin_sim/maf/plots/__init__.py +14 -0
- rubin_sim/maf/plots/hg_plotters.py +1117 -0
- rubin_sim/maf/plots/hourglass_plotters.py +120 -0
- rubin_sim/maf/plots/mo_plotters.py +358 -0
- rubin_sim/maf/plots/nd_plotters.py +220 -0
- rubin_sim/maf/plots/neo_distance_plotter.py +140 -0
- rubin_sim/maf/plots/night_pointing_plotter.py +96 -0
- rubin_sim/maf/plots/oned_plotters.py +114 -0
- rubin_sim/maf/plots/perceptual_rainbow.py +30 -0
- rubin_sim/maf/plots/plot_handler.py +766 -0
- rubin_sim/maf/plots/skyproj_plotters.py +540 -0
- rubin_sim/maf/plots/spatial_plotters.py +1029 -0
- rubin_sim/maf/plots/special_plotters.py +256 -0
- rubin_sim/maf/plots/two_d_plotters.py +177 -0
- rubin_sim/maf/plots/xyplotter.py +37 -0
- rubin_sim/maf/run_comparison/__init__.py +5 -0
- rubin_sim/maf/run_comparison/archive.py +584 -0
- rubin_sim/maf/run_comparison/gather_summaries.py +125 -0
- rubin_sim/maf/run_comparison/microlensing_compare.py +514 -0
- rubin_sim/maf/run_comparison/radar_plot.py +202 -0
- rubin_sim/maf/run_comparison/summary_plots.py +710 -0
- rubin_sim/maf/run_moving_calc.py +239 -0
- rubin_sim/maf/run_moving_fractions.py +128 -0
- rubin_sim/maf/run_moving_join.py +103 -0
- rubin_sim/maf/run_selfcal_metric.py +92 -0
- rubin_sim/maf/scimaf_dir.py +87 -0
- rubin_sim/maf/show_maf.py +227 -0
- rubin_sim/maf/slicers/__init__.py +13 -0
- rubin_sim/maf/slicers/base_slicer.py +513 -0
- rubin_sim/maf/slicers/base_spatial_slicer.py +202 -0
- rubin_sim/maf/slicers/healpix_sdss_slicer.py +118 -0
- rubin_sim/maf/slicers/healpix_slicer.py +189 -0
- rubin_sim/maf/slicers/healpix_subset_slicer.py +293 -0
- rubin_sim/maf/slicers/hourglass_slicer.py +37 -0
- rubin_sim/maf/slicers/mo_slicer.py +171 -0
- rubin_sim/maf/slicers/movie_slicer.py +288 -0
- rubin_sim/maf/slicers/nd_slicer.py +147 -0
- rubin_sim/maf/slicers/one_d_slicer.py +210 -0
- rubin_sim/maf/slicers/time_interval_slicers.py +283 -0
- rubin_sim/maf/slicers/uni_slicer.py +57 -0
- rubin_sim/maf/slicers/user_points_slicer.py +141 -0
- rubin_sim/maf/stackers/__init__.py +13 -0
- rubin_sim/maf/stackers/base_stacker.py +221 -0
- rubin_sim/maf/stackers/coord_stackers.py +224 -0
- rubin_sim/maf/stackers/date_stackers.py +192 -0
- rubin_sim/maf/stackers/general_stackers.py +683 -0
- rubin_sim/maf/stackers/get_col_info.py +104 -0
- rubin_sim/maf/stackers/label_stackers.py +114 -0
- rubin_sim/maf/stackers/m5_optimal_stacker.py +126 -0
- rubin_sim/maf/stackers/mo_phase.py +265 -0
- rubin_sim/maf/stackers/mo_stackers.py +486 -0
- rubin_sim/maf/stackers/n_follow_stacker.py +187 -0
- rubin_sim/maf/stackers/neo_dist_stacker.py +126 -0
- rubin_sim/maf/stackers/sdss_stackers.py +30 -0
- rubin_sim/maf/stackers/sn_stacker.py +152 -0
- rubin_sim/maf/stackers/teff_stacker.py +154 -0
- rubin_sim/maf/utils/__init__.py +6 -0
- rubin_sim/maf/utils/astrometry_utils.py +93 -0
- rubin_sim/maf/utils/generate_fov_map.py +58 -0
- rubin_sim/maf/utils/get_date_version.py +28 -0
- rubin_sim/maf/utils/maf_utils.py +212 -0
- rubin_sim/maf/utils/opsim_utils.py +259 -0
- rubin_sim/maf/utils/output_utils.py +34 -0
- rubin_sim/maf/utils/sn_n_sn_utils.py +894 -0
- rubin_sim/maf/utils/sn_utils.py +369 -0
- rubin_sim/maf/utils/stellar_mags.py +197 -0
- rubin_sim/maf/web/__init__.py +2 -0
- rubin_sim/maf/web/favicon.ico +0 -0
- rubin_sim/maf/web/maf_run_results.py +645 -0
- rubin_sim/maf/web/maf_tracking.py +139 -0
- rubin_sim/maf/web/sorttable.js +496 -0
- rubin_sim/maf/web/templates/allmetricresults.html +72 -0
- rubin_sim/maf/web/templates/configs.html +28 -0
- rubin_sim/maf/web/templates/macros.html +243 -0
- rubin_sim/maf/web/templates/master.html +294 -0
- rubin_sim/maf/web/templates/metricselect.html +91 -0
- rubin_sim/maf/web/templates/multicolor.html +147 -0
- rubin_sim/maf/web/templates/results.html +82 -0
- rubin_sim/maf/web/templates/runselect.html +58 -0
- rubin_sim/maf/web/templates/stats.html +50 -0
- rubin_sim/moving_objects/__init__.py +8 -0
- rubin_sim/moving_objects/base_obs.py +468 -0
- rubin_sim/moving_objects/cheby_fits.py +689 -0
- rubin_sim/moving_objects/cheby_values.py +231 -0
- rubin_sim/moving_objects/chebyshev_utils.py +365 -0
- rubin_sim/moving_objects/direct_obs.py +376 -0
- rubin_sim/moving_objects/make_lsst_obs.py +282 -0
- rubin_sim/moving_objects/ooephemerides.py +643 -0
- rubin_sim/moving_objects/orbits.py +507 -0
- rubin_sim/moving_objects/pre_generate.py +37 -0
- rubin_sim/moving_objects/utils.py +61 -0
- rubin_sim/phot_utils/__init__.py +7 -0
- rubin_sim/phot_utils/bandpass.py +425 -0
- rubin_sim/phot_utils/photometric_parameters.py +449 -0
- rubin_sim/phot_utils/physical_parameters.py +50 -0
- rubin_sim/phot_utils/predicted_zeropoints.py +245 -0
- rubin_sim/phot_utils/sed.py +1773 -0
- rubin_sim/phot_utils/sed_utils.py +52 -0
- rubin_sim/phot_utils/signaltonoise.py +695 -0
- rubin_sim/phot_utils/spectral_resampling.py +161 -0
- rubin_sim/satellite_constellations/__init__.py +3 -0
- rubin_sim/satellite_constellations/basis_function.py +40 -0
- rubin_sim/satellite_constellations/model_observatory.py +157 -0
- rubin_sim/satellite_constellations/sat_utils.py +488 -0
- rubin_sim/selfcal/__init__.py +4 -0
- rubin_sim/selfcal/generate_catalog.py +188 -0
- rubin_sim/selfcal/offsets.py +119 -0
- rubin_sim/selfcal/solver.py +143 -0
- rubin_sim/selfcal/star_tools.py +42 -0
- rubin_sim/sim_archive/__init__.py +14 -0
- rubin_sim/sim_archive/conda_packages.py +65 -0
- rubin_sim/sim_archive/future_vsarch.py +480 -0
- rubin_sim/sim_archive/make_snapshot.py +255 -0
- rubin_sim/sim_archive/prenight.py +216 -0
- rubin_sim/sim_archive/prenightindex.py +229 -0
- rubin_sim/sim_archive/prototype.py +1029 -0
- rubin_sim/sim_archive/sim_archive.py +680 -0
- rubin_sim/sim_archive/tempdb.py +56 -0
- rubin_sim/sim_archive/util.py +146 -0
- rubin_sim/sim_archive/vseqarchive.py +965 -0
- rubin_sim/sim_archive/vseqmetadata.py +1721 -0
- rubin_sim/skybrightness/__init__.py +5 -0
- rubin_sim/skybrightness/allsky_db.py +93 -0
- rubin_sim/skybrightness/data/ESO_Spectra/eso_tools.py +505 -0
- rubin_sim/skybrightness/data/solarSpec/package.py +22 -0
- rubin_sim/skybrightness/generate_hdf5.py +243 -0
- rubin_sim/skybrightness/interp_components.py +798 -0
- rubin_sim/skybrightness/sky_model.py +771 -0
- rubin_sim/skybrightness/twilight_func.py +83 -0
- rubin_sim/skybrightness/utils.py +91 -0
- rubin_sim-2.6.1.dev2.dist-info/METADATA +95 -0
- rubin_sim-2.6.1.dev2.dist-info/RECORD +387 -0
- rubin_sim-2.6.1.dev2.dist-info/WHEEL +5 -0
- rubin_sim-2.6.1.dev2.dist-info/entry_points.txt +24 -0
- rubin_sim-2.6.1.dev2.dist-info/licenses/LICENSE +674 -0
- rubin_sim-2.6.1.dev2.dist-info/top_level.txt +5 -0
- sql/make_vseqmeta.sql +251 -0
- tests/data/test_data.py +26 -0
- tests/data/test_ddf_grid.py +17 -0
- tests/maf/test_3x2fom.py +68 -0
- tests/maf/test_archive.py +162 -0
- tests/maf/test_basemetrics.py +99 -0
- tests/maf/test_batchcommon.py +36 -0
- tests/maf/test_batches.py +159 -0
- tests/maf/test_cadencemetrics.py +388 -0
- tests/maf/test_calibrationmetrics.py +242 -0
- tests/maf/test_color_slopes.py +71 -0
- tests/maf/test_cosmology_summaries.py +20 -0
- tests/maf/test_gathersummaries.py +48 -0
- tests/maf/test_healpixslicer.py +439 -0
- tests/maf/test_healpixsubsetslicer.py +273 -0
- tests/maf/test_hgplotters.py +145 -0
- tests/maf/test_hourglassmetric.py +33 -0
- tests/maf/test_io.py +187 -0
- tests/maf/test_json.py +169 -0
- tests/maf/test_maps.py +199 -0
- tests/maf/test_metricbundle.py +69 -0
- tests/maf/test_mometrics.py +422 -0
- tests/maf/test_movieslicer.py +216 -0
- tests/maf/test_ndslicer.py +240 -0
- tests/maf/test_neodistanceplotter.py +41 -0
- tests/maf/test_onedslicer.py +245 -0
- tests/maf/test_opsimutils.py +80 -0
- tests/maf/test_plotters.py +134 -0
- tests/maf/test_resultsdb.py +145 -0
- tests/maf/test_schedviewmetrics.py +14 -0
- tests/maf/test_simplemetrics.py +200 -0
- tests/maf/test_snmetrics.py +335 -0
- tests/maf/test_snmetrics_nsn.py +123 -0
- tests/maf/test_stackers.py +423 -0
- tests/maf/test_stellarmags.py +50 -0
- tests/maf/test_stringcount.py +26 -0
- tests/maf/test_summary_plots.py +197 -0
- tests/maf/test_summarymetrics.py +82 -0
- tests/maf/test_technicalmetrics.py +267 -0
- tests/maf/test_templatemetrics.py +86 -0
- tests/maf/test_timeintervalslicers.py +97 -0
- tests/maf/test_trackingdb.py +88 -0
- tests/maf/test_unislicer.py +116 -0
- tests/maf/test_vectormetrics.py +225 -0
- tests/maf/test_visitgroupsmetric.py +181 -0
- tests/moving_objects/test_camera.py +39 -0
- tests/moving_objects/test_chebyfits.py +188 -0
- tests/moving_objects/test_chebyshevutils.py +76 -0
- tests/moving_objects/test_chebyvalues.py +245 -0
- tests/moving_objects/test_ephemerides.py +219 -0
- tests/moving_objects/test_orbits.py +170 -0
- tests/moving_objects/test_sed_data.py +61 -0
- tests/phot_utils/test_approximatebandpasses.py +91 -0
- tests/phot_utils/test_photometricparameters.py +268 -0
- tests/phot_utils/test_photometry.py +61 -0
- tests/phot_utils/test_predicted_zeropoints.py +53 -0
- tests/phot_utils/test_read_bandpasses.py +29 -0
- tests/phot_utils/test_sed.py +368 -0
- tests/phot_utils/test_snr.py +370 -0
- tests/satellite_constellations/test_satellites.py +36 -0
- tests/sim_archive/test_make_snapshot.py +24 -0
- tests/sim_archive/test_prototype_sim_archive.py +204 -0
- tests/sim_archive/test_vseqarchive.py +947 -0
- 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)
|