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,965 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import hashlib
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from tempfile import TemporaryDirectory
|
|
7
|
+
from typing import Sequence, Tuple
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
import pandas as pd
|
|
12
|
+
from astropy.time import Time
|
|
13
|
+
from lsst.resources import ResourcePath
|
|
14
|
+
|
|
15
|
+
from rubin_sim.maf.stackers import BaseStacker
|
|
16
|
+
|
|
17
|
+
from .util import compute_conda_env, dayobs_to_date, hdf5_to_opsimdb, opsimdb_to_hdf5
|
|
18
|
+
from .vseqmetadata import VSARCHIVE_PGSCHEMA, VisitSequenceArchiveMetadata
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"compute_nightly_stats",
|
|
22
|
+
"get_visits",
|
|
23
|
+
"record_visitseq_metadata",
|
|
24
|
+
"set_visitseq_url",
|
|
25
|
+
"get_visitseq_url",
|
|
26
|
+
"update_visitseq_metadata",
|
|
27
|
+
"is_tagged",
|
|
28
|
+
"tag",
|
|
29
|
+
"untag",
|
|
30
|
+
"comment",
|
|
31
|
+
"get_comments",
|
|
32
|
+
"archive_file",
|
|
33
|
+
"get_file",
|
|
34
|
+
"add_nightly_stats",
|
|
35
|
+
"query_nightly_stats",
|
|
36
|
+
"record_conda_env",
|
|
37
|
+
"import_proto",
|
|
38
|
+
"export_proto",
|
|
39
|
+
"ARCHIVE_URL",
|
|
40
|
+
"PRENIGHT_INDEX_URL",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
ARCHIVE_URL = "s3://rubin:rubin-scheduler-prenight/opsim/vseq/"
|
|
44
|
+
PRENIGHT_INDEX_URL = "s3://rubin:rubin-scheduler-prenight/opsim/prenight_index/"
|
|
45
|
+
SQLITE_EXTINSIONS = {".db", ".sqlite", ".sqlite3", ".db3"}
|
|
46
|
+
|
|
47
|
+
LOGGER = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Computation
|
|
51
|
+
#
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def compute_nightly_stats(
|
|
55
|
+
visits: pd.DataFrame, columns: Tuple[str, ...] = ("altitude", "azimuth")
|
|
56
|
+
) -> pd.DataFrame:
|
|
57
|
+
if "day_obs" not in visits.columns:
|
|
58
|
+
# Pandas seems to work better with type hinding that astropy Time
|
|
59
|
+
start_mjd_field = ""
|
|
60
|
+
for candidate_field in ["obs_start_mjd", "observationStartMJD"]:
|
|
61
|
+
if candidate_field in visits.columns:
|
|
62
|
+
start_mjd_field = candidate_field
|
|
63
|
+
break
|
|
64
|
+
if len(start_mjd_field) == 0:
|
|
65
|
+
raise ValueError("No day_obs or start mjd field found")
|
|
66
|
+
|
|
67
|
+
times = pd.to_datetime(visits.loc[:, start_mjd_field] + 2400000, unit="D", origin="julian")
|
|
68
|
+
visits = visits.assign(day_obs=pd.Series(times).dt.date)
|
|
69
|
+
|
|
70
|
+
stats_df = (
|
|
71
|
+
visits.groupby("day_obs")[list(columns)]
|
|
72
|
+
.describe(percentiles=[0.05, 0.25, 0.5, 0.75, 0.95])
|
|
73
|
+
.stack(level=0)
|
|
74
|
+
.reset_index()
|
|
75
|
+
.rename(
|
|
76
|
+
columns={
|
|
77
|
+
"level_1": "value_name",
|
|
78
|
+
"5%": "p05",
|
|
79
|
+
"25%": "q1",
|
|
80
|
+
"50%": "median",
|
|
81
|
+
"75%": "q3",
|
|
82
|
+
"95%": "p95",
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
stats_df["accumulated"] = False
|
|
87
|
+
return stats_df
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def construct_base_resource_path(
|
|
91
|
+
archive_base: ResourcePath, telescope: str, creation_time: Time, visitseq_uuid: UUID
|
|
92
|
+
) -> ResourcePath:
|
|
93
|
+
"""Build the base ``ResourcePath`` for a visit‑sequence
|
|
94
|
+
archive entry.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
archive_base : `ResourcePath`
|
|
99
|
+
The base of the archive relative to which the visit sequence
|
|
100
|
+
will be added.
|
|
101
|
+
telescope : `str`
|
|
102
|
+
Identifier for the telescope (e.g. ``"simonyi"`` or ``"auxtel"`).
|
|
103
|
+
creation_time : `Time`
|
|
104
|
+
The timestamp when the visit sequence was created.
|
|
105
|
+
visitseq_uuid : `UUID`
|
|
106
|
+
The unique identifier of the visit sequence.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
ResourcePath
|
|
111
|
+
A ``ResourcePath`` instance representing the base directory for
|
|
112
|
+
this visit sequence. The directory is structured as:
|
|
113
|
+
|
|
114
|
+
``<archive_base>/<telescope>/<date>/<visitseq_uuid>/``
|
|
115
|
+
|
|
116
|
+
where ``<date>`` is the UTC‑12 date derived from ``creation_time``.
|
|
117
|
+
"""
|
|
118
|
+
creation_time_iso_dayobs = dayobs_to_date(Time(creation_time.mjd, format="mjd")).isoformat()
|
|
119
|
+
visitseq_base_rp = (
|
|
120
|
+
archive_base.join(telescope)
|
|
121
|
+
.join(creation_time_iso_dayobs)
|
|
122
|
+
.join(str(visitseq_uuid), forceDirectory=True)
|
|
123
|
+
)
|
|
124
|
+
return visitseq_base_rp
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
#
|
|
128
|
+
# Interaction with the file store
|
|
129
|
+
#
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _write_data_to_archive(content: bytes, visitseq_base_rp: ResourcePath, file_name: str) -> ResourcePath:
|
|
133
|
+
# Make sure the base for the visit sequence exists
|
|
134
|
+
if not visitseq_base_rp.exists():
|
|
135
|
+
visitseq_base_rp.mkdir()
|
|
136
|
+
LOGGER.debug(f"Created {visitseq_base_rp}.")
|
|
137
|
+
|
|
138
|
+
destination_rp = visitseq_base_rp.join(file_name)
|
|
139
|
+
LOGGER.debug(f"Writing {destination_rp}.")
|
|
140
|
+
destination_rp.write(content)
|
|
141
|
+
LOGGER.debug(f"Wrote {destination_rp}.")
|
|
142
|
+
return destination_rp
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _write_file_to_archive(
|
|
146
|
+
origin: str | Path,
|
|
147
|
+
visitseq_base_rp: ResourcePath,
|
|
148
|
+
) -> Tuple[ResourcePath, bytes]:
|
|
149
|
+
file_path = origin if isinstance(origin, Path) else Path(origin)
|
|
150
|
+
with open(file_path, "rb") as origin_io:
|
|
151
|
+
content = origin_io.read()
|
|
152
|
+
|
|
153
|
+
content_sha256 = bytes.fromhex(hashlib.sha256(content).hexdigest())
|
|
154
|
+
destination_rp = _write_data_to_archive(content, visitseq_base_rp, file_path.name)
|
|
155
|
+
return destination_rp, content_sha256
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def add_file(
|
|
159
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
160
|
+
uuid: UUID,
|
|
161
|
+
origin: str | Path,
|
|
162
|
+
file_type: str,
|
|
163
|
+
archive_base: str | ResourcePath,
|
|
164
|
+
update: bool = False,
|
|
165
|
+
) -> ResourcePath:
|
|
166
|
+
"""Archive a file associated with a visit sequence and
|
|
167
|
+
register its location in the metadata database.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
172
|
+
Instance of `VisitSequenceArchiveMetadata` used to
|
|
173
|
+
query the sequence metadata and register the file.
|
|
174
|
+
uuid : `UUID`
|
|
175
|
+
The unique identifier of the visit sequence that the file
|
|
176
|
+
should be linked to.
|
|
177
|
+
origin : `str` or `pathlib.Path`
|
|
178
|
+
Path to the local file to be archived.
|
|
179
|
+
file_type : `str`
|
|
180
|
+
Identifier for the type of file being archived.
|
|
181
|
+
archive_base : `lsst.resources.ResourcePath`
|
|
182
|
+
Base location of the archive.
|
|
183
|
+
update : `bool`, optional
|
|
184
|
+
If ``True`` and a record for the same ``visitseq_uuid`` and
|
|
185
|
+
``file_type`` already exists, the existing row will be
|
|
186
|
+
updated with the new SHA‑256 hash and URL. If ``False`` a
|
|
187
|
+
`ValueError` will be raised on duplicate.
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
file_rp : `lsst.resources.ResourcePath`
|
|
192
|
+
The `ResourcePath` pointing to the archived file
|
|
193
|
+
on the file store.
|
|
194
|
+
"""
|
|
195
|
+
if isinstance(archive_base, str):
|
|
196
|
+
archive_base = ResourcePath(archive_base)
|
|
197
|
+
assert isinstance(archive_base, ResourcePath)
|
|
198
|
+
|
|
199
|
+
visitseq_metadata = vsarch.get_visitseq_metadata(uuid)
|
|
200
|
+
uuid = visitseq_metadata["visitseq_uuid"]
|
|
201
|
+
telescope = visitseq_metadata["telescope"]
|
|
202
|
+
creation_time = Time(visitseq_metadata["creation_time"])
|
|
203
|
+
visitseq_base_rp = construct_base_resource_path(archive_base, telescope, creation_time, uuid)
|
|
204
|
+
|
|
205
|
+
# If we are adding a file that looks like opsim sqlite3 output but
|
|
206
|
+
# specify the file type as visits, convert to hdf5.
|
|
207
|
+
if file_type == "visits" and Path(origin).suffix.lower() in SQLITE_EXTINSIONS:
|
|
208
|
+
with TemporaryDirectory() as temp_dir:
|
|
209
|
+
new_origin = Path(temp_dir).joinpath("visits.h5")
|
|
210
|
+
opsimdb_to_hdf5(origin, new_origin)
|
|
211
|
+
location = add_file(vsarch, uuid, new_origin, "visits", archive_base, update)
|
|
212
|
+
return location
|
|
213
|
+
|
|
214
|
+
location, file_sha256 = _write_file_to_archive(origin, visitseq_base_rp)
|
|
215
|
+
if file_type == "visits":
|
|
216
|
+
vsarch.set_visitseq_url(uuid, location.geturl())
|
|
217
|
+
else:
|
|
218
|
+
vsarch.register_file(uuid, file_type, file_sha256, location, update=update)
|
|
219
|
+
|
|
220
|
+
return location
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_visits(
|
|
224
|
+
visits_url: str | ResourcePath,
|
|
225
|
+
query: str = "",
|
|
226
|
+
stackers: Sequence[BaseStacker] = (),
|
|
227
|
+
) -> pd.DataFrame:
|
|
228
|
+
"""Retrieve visits for a visit sequence.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
visits_url : `str`
|
|
233
|
+
The origin of the visits.
|
|
234
|
+
query : `str`
|
|
235
|
+
A query in `pandas.DataFrame.query` syntax to select visits
|
|
236
|
+
from the returned sequence. Applied before stackers are run.
|
|
237
|
+
stackers : `Sequence` [`BaseStacker`]
|
|
238
|
+
A sequence of maf stackers to apply.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
visits: `pd.DataFrame`
|
|
243
|
+
The visits in the sequence.
|
|
244
|
+
"""
|
|
245
|
+
origin_rp = ResourcePath(visits_url)
|
|
246
|
+
|
|
247
|
+
with TemporaryDirectory() as temp_dir:
|
|
248
|
+
h5_destination = ResourcePath(temp_dir).join("visits.h5")
|
|
249
|
+
h5_destination.transfer_from(origin_rp, "copy")
|
|
250
|
+
visits = pd.read_hdf(h5_destination.ospath, key="observations")
|
|
251
|
+
|
|
252
|
+
assert isinstance(visits, pd.DataFrame)
|
|
253
|
+
|
|
254
|
+
if len(query) > 0:
|
|
255
|
+
visits.query(query, inplace=True)
|
|
256
|
+
|
|
257
|
+
if len(stackers) > 0:
|
|
258
|
+
visit_records = visits.to_records()
|
|
259
|
+
for stacker in stackers:
|
|
260
|
+
visit_records = stacker.run(visit_records)
|
|
261
|
+
visits = pd.DataFrame(visit_records)
|
|
262
|
+
|
|
263
|
+
return visits
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
#
|
|
267
|
+
# API
|
|
268
|
+
#
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@click.group()
|
|
272
|
+
@click.option(
|
|
273
|
+
"--database",
|
|
274
|
+
default=None,
|
|
275
|
+
help="PostgreSQL database name of the metadata database",
|
|
276
|
+
)
|
|
277
|
+
@click.option(
|
|
278
|
+
"--host",
|
|
279
|
+
default=None,
|
|
280
|
+
help="PostgreSQL host address of the metadata database",
|
|
281
|
+
)
|
|
282
|
+
@click.option(
|
|
283
|
+
"--user",
|
|
284
|
+
default=None,
|
|
285
|
+
help="PostgreSQL user name to use to connect to the metadata database",
|
|
286
|
+
)
|
|
287
|
+
@click.option(
|
|
288
|
+
"--schema",
|
|
289
|
+
default=None,
|
|
290
|
+
help="Schema of the metadata database containing the visit‑sequence tables",
|
|
291
|
+
)
|
|
292
|
+
@click.pass_context
|
|
293
|
+
def vseqarchive(
|
|
294
|
+
click_context: click.Context,
|
|
295
|
+
database: str,
|
|
296
|
+
host: str,
|
|
297
|
+
user: str | None,
|
|
298
|
+
schema: str,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""visitseq command line interface."""
|
|
301
|
+
metadata_db_kwargs = {}
|
|
302
|
+
if database is not None:
|
|
303
|
+
metadata_db_kwargs["database"] = database
|
|
304
|
+
|
|
305
|
+
if user is not None:
|
|
306
|
+
metadata_db_kwargs["user"] = user
|
|
307
|
+
|
|
308
|
+
if host is not None:
|
|
309
|
+
metadata_db_kwargs["host"] = host
|
|
310
|
+
|
|
311
|
+
if schema is None:
|
|
312
|
+
if "VSARCHIVE_PGSCHEMA" in os.environ:
|
|
313
|
+
schema = os.environ["VSARCHIVE_PGSCHEMA"]
|
|
314
|
+
else:
|
|
315
|
+
schema = VSARCHIVE_PGSCHEMA
|
|
316
|
+
assert isinstance(schema, str)
|
|
317
|
+
|
|
318
|
+
click_context.obj = VisitSequenceArchiveMetadata(metadata_db_kwargs, schema)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@vseqarchive.command()
|
|
322
|
+
@click.argument("table", type=str)
|
|
323
|
+
@click.argument("visits_file", type=click.Path(exists=True))
|
|
324
|
+
@click.argument("label")
|
|
325
|
+
@click.option(
|
|
326
|
+
"--telescope",
|
|
327
|
+
default="simonyi",
|
|
328
|
+
show_default=True,
|
|
329
|
+
help="Telescope identifier (e.g. simonyi or auxtel)",
|
|
330
|
+
)
|
|
331
|
+
@click.option("--url", default=None, help="Optional URL for the visits file")
|
|
332
|
+
@click.option("--first_day_obs", default=None, help="First night of observations (YYYY-MM-DD or int)")
|
|
333
|
+
@click.option("--last_day_obs", default=None, help="Last night of observations (YYYY-MM-DD or int)")
|
|
334
|
+
@click.option(
|
|
335
|
+
"--creation_time",
|
|
336
|
+
default=None,
|
|
337
|
+
help="Creation time of the visit sequence in ISO format (e.g. 2025-01-01T00:00:00)",
|
|
338
|
+
)
|
|
339
|
+
@click.pass_obj
|
|
340
|
+
def record_visitseq_metadata(
|
|
341
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
342
|
+
table: str,
|
|
343
|
+
visits_file: str,
|
|
344
|
+
label: str,
|
|
345
|
+
telescope: str,
|
|
346
|
+
url: str | None,
|
|
347
|
+
first_day_obs: str | int | None,
|
|
348
|
+
last_day_obs: str | int | None,
|
|
349
|
+
creation_time: str | None,
|
|
350
|
+
) -> None:
|
|
351
|
+
"""Record visit‑sequence metadata from an HDF5 visits file.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
356
|
+
The database interface instance.
|
|
357
|
+
table : `str`
|
|
358
|
+
Target table for the record (e.g. ``visitseq``, ``simulations``,
|
|
359
|
+
``completed`` or ``mixedvisitseq``).
|
|
360
|
+
visits_file : `str`
|
|
361
|
+
Path to the HDF5 file (or SQLite3 file) containing the
|
|
362
|
+
``visits`` dataset.
|
|
363
|
+
label : `str`
|
|
364
|
+
Human‑readable label for the visit sequence.
|
|
365
|
+
telescope : `str`, optional
|
|
366
|
+
Telescope identifier, default ``simonyi``.
|
|
367
|
+
url : `str`, optional
|
|
368
|
+
URL where the visits file can be downloaded.
|
|
369
|
+
first_day_obs : `str` or `int`, optional
|
|
370
|
+
First day of observation in SMTN‑032 format, ISO string, or
|
|
371
|
+
``YYYY‑MM‑DD``.
|
|
372
|
+
last_day_obs : `str` or `int`, optional
|
|
373
|
+
Last day of observation (same formats as ``first_day_obs``).
|
|
374
|
+
creation_time : `str`, optional
|
|
375
|
+
ISO‑formatted timestamp of when the sequence was created;
|
|
376
|
+
if omitted, the current time is used.
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
None
|
|
381
|
+
The UUID of the inserted visit sequence is printed to stdout.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
if Path(visits_file).suffix.lower() in SQLITE_EXTINSIONS:
|
|
385
|
+
with TemporaryDirectory() as temp_dir:
|
|
386
|
+
hdf5_path = Path(temp_dir).joinpath("visits.h5")
|
|
387
|
+
opsimdb_to_hdf5(visits_file, hdf5_path)
|
|
388
|
+
visits_df = pd.read_hdf(str(hdf5_path), key="observations")
|
|
389
|
+
else:
|
|
390
|
+
visits_df = pd.read_hdf(visits_file, key="observations")
|
|
391
|
+
assert isinstance(visits_df, pd.DataFrame)
|
|
392
|
+
|
|
393
|
+
# Convert the optional creation time string to an astropy Time object
|
|
394
|
+
creation_ap_time = Time(creation_time) if creation_time is not None else Time.now()
|
|
395
|
+
|
|
396
|
+
# Record the visit sequence metadata
|
|
397
|
+
visitseq_uuid = vsarch.record_visitseq_metadata(
|
|
398
|
+
visits_df,
|
|
399
|
+
label,
|
|
400
|
+
telescope=telescope,
|
|
401
|
+
table=table,
|
|
402
|
+
url=url,
|
|
403
|
+
first_day_obs=first_day_obs,
|
|
404
|
+
last_day_obs=last_day_obs,
|
|
405
|
+
creation_time=creation_ap_time,
|
|
406
|
+
)
|
|
407
|
+
print(visitseq_uuid)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
@vseqarchive.command()
|
|
411
|
+
@click.argument("uuid", type=click.UUID)
|
|
412
|
+
@click.option(
|
|
413
|
+
"--table",
|
|
414
|
+
default="visitseq",
|
|
415
|
+
show_default=True,
|
|
416
|
+
help="Table to query (e.g. visitseq, simulations, completed, mixedvisitseq)",
|
|
417
|
+
)
|
|
418
|
+
@click.pass_obj
|
|
419
|
+
def get_visitseq_metadata(vsarch: VisitSequenceArchiveMetadata, uuid: UUID, table: str) -> None:
|
|
420
|
+
"""Retrieve and display metadata for a visit sequence.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
425
|
+
The metadata interface instance.
|
|
426
|
+
uuid : `UUID`
|
|
427
|
+
The UUID of the visit sequence to query.
|
|
428
|
+
table : `str`, optional
|
|
429
|
+
Name of the child table to search; one of ``visitseq``,
|
|
430
|
+
``simulations``, ``completed`` or ``mixedvisitseq``.
|
|
431
|
+
Defaults to ``visitseq``.
|
|
432
|
+
"""
|
|
433
|
+
sequence_metadata = vsarch.get_visitseq_metadata(uuid, table=table)
|
|
434
|
+
|
|
435
|
+
# Print the DataFrame as a tab‑separated table
|
|
436
|
+
print(sequence_metadata.to_frame().T.to_csv(sep="\t", index=False).rstrip("\n"))
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@vseqarchive.command()
|
|
440
|
+
@click.argument("uuid", type=click.UUID)
|
|
441
|
+
@click.argument("url", type=click.STRING)
|
|
442
|
+
@click.pass_obj
|
|
443
|
+
def set_visitseq_url(vsarch: VisitSequenceArchiveMetadata, uuid: UUID, url: str) -> None:
|
|
444
|
+
"""Update the stored URL for a visit‑sequence file.
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
449
|
+
The metadata interface instance.
|
|
450
|
+
uuid : `UUID`
|
|
451
|
+
The UUID of the visit sequence whose URL is to be updated.
|
|
452
|
+
url : `str`
|
|
453
|
+
The new URL pointing to the visits file.
|
|
454
|
+
"""
|
|
455
|
+
vsarch.set_visitseq_url(uuid, url)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
@vseqarchive.command()
|
|
459
|
+
@click.argument("uuid", type=click.UUID)
|
|
460
|
+
@click.pass_obj
|
|
461
|
+
def get_visitseq_url(vsarch: VisitSequenceArchiveMetadata, uuid: UUID) -> None:
|
|
462
|
+
"""Retrieve and print the URL for the visits in a visit sequence.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
467
|
+
The metadata interface instance.
|
|
468
|
+
uuid : `UUID`
|
|
469
|
+
The UUID of the visit sequence whose URL is to be retrieved.
|
|
470
|
+
"""
|
|
471
|
+
url = vsarch.get_visitseq_url(uuid)
|
|
472
|
+
click.echo(url)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
@vseqarchive.command()
|
|
476
|
+
@click.argument("uuid", type=click.UUID)
|
|
477
|
+
@click.argument("field", type=str)
|
|
478
|
+
@click.argument("value", type=str)
|
|
479
|
+
@click.pass_obj
|
|
480
|
+
def update_visitseq_metadata(
|
|
481
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
482
|
+
uuid: UUID,
|
|
483
|
+
field: str,
|
|
484
|
+
value: str,
|
|
485
|
+
) -> None:
|
|
486
|
+
"""Update a single metadata field for a visit sequence.
|
|
487
|
+
|
|
488
|
+
Parameters
|
|
489
|
+
----------
|
|
490
|
+
vsarch : `VisiteSequenceArchiveMetadata`
|
|
491
|
+
An instance of the interface to the metadata archive.
|
|
492
|
+
uuid : `UUID`
|
|
493
|
+
The UUID of the visit sequence to update.
|
|
494
|
+
field : `str`
|
|
495
|
+
The name of the column in the metadata table to modify.
|
|
496
|
+
value : `str`
|
|
497
|
+
The new value for the field. Postgresql will cast the type
|
|
498
|
+
to what is expected in the database schema.
|
|
499
|
+
"""
|
|
500
|
+
# psycopg2 usually converts strings to the right
|
|
501
|
+
# type base on column type in the database, but if
|
|
502
|
+
# hex values are provided for bytes colums it
|
|
503
|
+
# gets it wrong.
|
|
504
|
+
# So, if we're asking to update a bytes column, convert
|
|
505
|
+
# it from hex ourselves.
|
|
506
|
+
update_value: str | bytes = value
|
|
507
|
+
if field.endswith("sha256"):
|
|
508
|
+
update_value = bytes.fromhex(value)
|
|
509
|
+
|
|
510
|
+
vsarch.update_visitseq_metadata(uuid, field, update_value)
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@vseqarchive.command()
|
|
514
|
+
@click.argument("uuid", type=click.UUID)
|
|
515
|
+
@click.argument("tag", type=str)
|
|
516
|
+
@click.pass_obj
|
|
517
|
+
def is_tagged(vsarch: VisitSequenceArchiveMetadata, uuid: UUID, tag: str) -> None:
|
|
518
|
+
"""Return whether a visit sequence is tagged with a given tag.
|
|
519
|
+
|
|
520
|
+
The command prints ``true`` if the tag exists for the visit sequence
|
|
521
|
+
and ``false`` otherwise.
|
|
522
|
+
|
|
523
|
+
Parameters
|
|
524
|
+
----------
|
|
525
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
526
|
+
The interface to the metadata database.
|
|
527
|
+
uuid : `UUID`
|
|
528
|
+
The UUID of the visit sequence to check.
|
|
529
|
+
tag : `str`
|
|
530
|
+
The tag name to query.
|
|
531
|
+
"""
|
|
532
|
+
seq_is_tagged = vsarch.is_tagged(uuid, tag)
|
|
533
|
+
if seq_is_tagged:
|
|
534
|
+
click.echo("true")
|
|
535
|
+
else:
|
|
536
|
+
click.echo("false")
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
@vseqarchive.command()
|
|
540
|
+
@click.argument("uuid", type=click.UUID)
|
|
541
|
+
@click.argument("tags", nargs=-1, required=True)
|
|
542
|
+
@click.pass_obj
|
|
543
|
+
def tag(vsarch: VisitSequenceArchiveMetadata, uuid: UUID, tags: Tuple[str, ...]) -> None:
|
|
544
|
+
"""Add one or more tags to a visit sequence.
|
|
545
|
+
|
|
546
|
+
Parameters
|
|
547
|
+
----------
|
|
548
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
549
|
+
The interface to the metadata database.
|
|
550
|
+
uuid : `UUID`
|
|
551
|
+
The UUID of the visit sequence to tag.
|
|
552
|
+
tags : `str`
|
|
553
|
+
One or more tag names to add.
|
|
554
|
+
"""
|
|
555
|
+
if len(tags) < 1:
|
|
556
|
+
raise ValueError("At least one tag must be requested.")
|
|
557
|
+
|
|
558
|
+
vsarch.tag(uuid, *tags)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
@vseqarchive.command()
|
|
562
|
+
@click.argument("uuid", type=click.UUID)
|
|
563
|
+
@click.argument("tag", type=str)
|
|
564
|
+
@click.pass_obj
|
|
565
|
+
def untag(vsarch: VisitSequenceArchiveMetadata, uuid: UUID, tag: str) -> None:
|
|
566
|
+
"""Remove a tag from a visit sequence.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
571
|
+
The interface to the metadata database.
|
|
572
|
+
uuid : `UUID`
|
|
573
|
+
The UUID of the visit sequence to modify.
|
|
574
|
+
tag : `str`
|
|
575
|
+
The name of the tag to remove.
|
|
576
|
+
"""
|
|
577
|
+
vsarch.untag(uuid, tag)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
@vseqarchive.command()
|
|
581
|
+
@click.argument("uuid", type=click.UUID)
|
|
582
|
+
@click.argument("comment")
|
|
583
|
+
@click.option(
|
|
584
|
+
"--author",
|
|
585
|
+
default=None,
|
|
586
|
+
help="The author of the comment",
|
|
587
|
+
)
|
|
588
|
+
@click.pass_obj
|
|
589
|
+
def comment(
|
|
590
|
+
vsarch: VisitSequenceArchiveMetadata, uuid: UUID, comment: str, author: str | None = None
|
|
591
|
+
) -> None:
|
|
592
|
+
"""Attach a comment to a visit sequence in the metadata database.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
597
|
+
The interface to the metadata database.
|
|
598
|
+
uuid : `UUID`
|
|
599
|
+
The UUID of the visit sequence to comment on.
|
|
600
|
+
comment : `str`
|
|
601
|
+
The comment text to attach.
|
|
602
|
+
author : `str`, optional
|
|
603
|
+
The author of the comment. If omitted the comment will be recorded
|
|
604
|
+
without an author field.
|
|
605
|
+
"""
|
|
606
|
+
vsarch.comment(uuid, comment, author)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
@vseqarchive.command()
|
|
610
|
+
@click.argument("uuid", type=click.UUID)
|
|
611
|
+
@click.pass_obj
|
|
612
|
+
def get_comments(vsarch: VisitSequenceArchiveMetadata, uuid: UUID) -> None:
|
|
613
|
+
"""Retrieve all comments attached to a specific visit sequence.
|
|
614
|
+
|
|
615
|
+
Parameters
|
|
616
|
+
----------
|
|
617
|
+
uuid : `UUID`
|
|
618
|
+
The UUID of the visit sequence whose comments should be fetched.
|
|
619
|
+
"""
|
|
620
|
+
comments_df = vsarch.get_comments(uuid)
|
|
621
|
+
if not comments_df.empty:
|
|
622
|
+
# Pandas will include the index by default; omit it.
|
|
623
|
+
click.echo(comments_df.to_csv(sep="\t", index=False).rstrip("\n"))
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
@vseqarchive.command()
|
|
627
|
+
@click.argument("uuid", type=click.UUID)
|
|
628
|
+
@click.argument("origin", type=click.Path(exists=True))
|
|
629
|
+
@click.argument("file_type", type=click.STRING)
|
|
630
|
+
@click.option(
|
|
631
|
+
"--archive-base",
|
|
632
|
+
default=ARCHIVE_URL,
|
|
633
|
+
show_default=True,
|
|
634
|
+
help="Base directory for the archive (e.g. file://data/archive).",
|
|
635
|
+
)
|
|
636
|
+
@click.option(
|
|
637
|
+
"--update",
|
|
638
|
+
is_flag=True,
|
|
639
|
+
help="If set, overwrite an existing record for the same visitseq_uuid and file_type.",
|
|
640
|
+
)
|
|
641
|
+
@click.pass_obj
|
|
642
|
+
def archive_file(
|
|
643
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
644
|
+
uuid: UUID,
|
|
645
|
+
origin: str,
|
|
646
|
+
file_type: str,
|
|
647
|
+
archive_base: str,
|
|
648
|
+
update: bool,
|
|
649
|
+
) -> None:
|
|
650
|
+
"""Archive a file and register its location in the metadata database.
|
|
651
|
+
|
|
652
|
+
Parameters
|
|
653
|
+
----------
|
|
654
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
655
|
+
The metadata interface instance.
|
|
656
|
+
uuid : `UUID`
|
|
657
|
+
The UUID of the visit sequence that the file should be linked to.
|
|
658
|
+
origin : `str`
|
|
659
|
+
Path to the local file to be archived.
|
|
660
|
+
file_type : `str`
|
|
661
|
+
Identifier for the type of file being archived.
|
|
662
|
+
archive_base : `str`, optional
|
|
663
|
+
Base location of the archive.
|
|
664
|
+
update : `bool`, optional
|
|
665
|
+
If true, an existing record for the same
|
|
666
|
+
``visitseq_uuid``/``file_type`` combination is updated rather
|
|
667
|
+
than raising an error.
|
|
668
|
+
"""
|
|
669
|
+
# Convert the base path string into a ResourcePath
|
|
670
|
+
archive_base_rp = ResourcePath(archive_base)
|
|
671
|
+
|
|
672
|
+
archived_location = add_file(vsarch, uuid, origin, file_type, archive_base_rp, update=update)
|
|
673
|
+
|
|
674
|
+
click.echo(archived_location.geturl())
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
@vseqarchive.command()
|
|
678
|
+
@click.argument("uuid", type=click.UUID)
|
|
679
|
+
@click.argument("file_type", type=click.STRING)
|
|
680
|
+
@click.argument("destination", type=click.Path(exists=False))
|
|
681
|
+
@click.pass_obj
|
|
682
|
+
def get_file(
|
|
683
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
684
|
+
uuid: UUID,
|
|
685
|
+
file_type: str,
|
|
686
|
+
destination: str,
|
|
687
|
+
) -> None:
|
|
688
|
+
"""Retrieve a registered file and copy it to a local destination.
|
|
689
|
+
|
|
690
|
+
Parameters
|
|
691
|
+
----------
|
|
692
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
693
|
+
The metadata interface used to look up the file URL.
|
|
694
|
+
uuid : `UUID`
|
|
695
|
+
The unique identifier of the visit sequence containing the
|
|
696
|
+
requested file.
|
|
697
|
+
file_type : `str`
|
|
698
|
+
The handle for the type of file to retrieve (e.g. ``visits``).
|
|
699
|
+
destination : `str`
|
|
700
|
+
Path where the retrieved file should be written. If the
|
|
701
|
+
destination does not exist it will be created. For files
|
|
702
|
+
registered with the ``visits`` type and a SQLite3 extension
|
|
703
|
+
suffix, the file will be downloaded as an HDF5 ``visits.h5`` file,
|
|
704
|
+
converted back to the original opsim SQLite format, and written to
|
|
705
|
+
``destination``.
|
|
706
|
+
"""
|
|
707
|
+
file_url = vsarch.get_file_url(uuid, file_type)
|
|
708
|
+
origin_rp = ResourcePath(file_url)
|
|
709
|
+
if file_type == "visits" and Path(destination).suffix.lower() in SQLITE_EXTINSIONS:
|
|
710
|
+
with TemporaryDirectory() as temp_dir:
|
|
711
|
+
h5_destination = ResourcePath(temp_dir).join("visits.h5")
|
|
712
|
+
h5_destination.transfer_from(origin_rp, "copy")
|
|
713
|
+
click.echo(f"Copied {origin_rp.geturl()} to {h5_destination.geturl()}")
|
|
714
|
+
hdf5_to_opsimdb(h5_destination.ospath, destination)
|
|
715
|
+
click.echo(f"Converted to opsim and wrote to {destination}")
|
|
716
|
+
else:
|
|
717
|
+
destination_rp = ResourcePath(destination)
|
|
718
|
+
destination_rp.transfer_from(origin_rp, "copy")
|
|
719
|
+
click.echo(f"Copied {origin_rp.geturl()} to {destination_rp.geturl()}")
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
@vseqarchive.command()
|
|
723
|
+
@click.argument("uuid", type=click.UUID)
|
|
724
|
+
@click.argument("visits_file", type=click.Path(exists=True))
|
|
725
|
+
@click.argument(
|
|
726
|
+
"columns",
|
|
727
|
+
type=str,
|
|
728
|
+
required=False,
|
|
729
|
+
nargs=-1,
|
|
730
|
+
)
|
|
731
|
+
@click.pass_obj
|
|
732
|
+
def add_nightly_stats(
|
|
733
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
734
|
+
uuid: UUID,
|
|
735
|
+
visits_file: str,
|
|
736
|
+
columns: tuple[str, ...],
|
|
737
|
+
) -> None:
|
|
738
|
+
"""Compute nightly statistics for a visit sequence and add
|
|
739
|
+
them to the visit sequence metadata database.
|
|
740
|
+
|
|
741
|
+
Parameters
|
|
742
|
+
----------
|
|
743
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
744
|
+
The metadata interface.
|
|
745
|
+
uuid : `UUID`
|
|
746
|
+
The UUID of the visit sequence to attach the statistics to.
|
|
747
|
+
visits_file : `str`
|
|
748
|
+
Path to an HDF5 file containing a ``visits`` dataset.
|
|
749
|
+
columns : `tuple`
|
|
750
|
+
List of columns for which stats should be added.
|
|
751
|
+
"""
|
|
752
|
+
# Load the visits table from the HDF5 file
|
|
753
|
+
visits_df = pd.read_hdf(visits_file, key="observations")
|
|
754
|
+
assert isinstance(visits_df, pd.DataFrame), "Expected a DataFrame from key 'observations'"
|
|
755
|
+
|
|
756
|
+
# Compute the nightly statistics, passing the requested columns if any
|
|
757
|
+
if columns:
|
|
758
|
+
stats_df = compute_nightly_stats(visits_df, columns=tuple(columns))
|
|
759
|
+
else:
|
|
760
|
+
stats_df = compute_nightly_stats(visits_df)
|
|
761
|
+
|
|
762
|
+
# Insert into the database
|
|
763
|
+
vsarch.insert_nightly_stats(uuid, stats_df)
|
|
764
|
+
|
|
765
|
+
# Print the statistics as TSV
|
|
766
|
+
tsv = stats_df.to_csv(sep="\t", index=False).rstrip("\n")
|
|
767
|
+
click.echo(tsv)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
@vseqarchive.command()
|
|
771
|
+
@click.argument("uuid", type=click.UUID)
|
|
772
|
+
@click.pass_obj
|
|
773
|
+
def query_nightly_stats(vsarch: VisitSequenceArchiveMetadata, uuid: UUID) -> None:
|
|
774
|
+
"""Retrieve nightly statistics for a visit sequence and
|
|
775
|
+
print them as a tab‑separated table.
|
|
776
|
+
|
|
777
|
+
Parameters
|
|
778
|
+
----------
|
|
779
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
780
|
+
The metadata interface.
|
|
781
|
+
uuid : `UUID`
|
|
782
|
+
The unique identifier of the visit sequence.
|
|
783
|
+
"""
|
|
784
|
+
stats_df = vsarch.query_nightly_stats(uuid)
|
|
785
|
+
output = stats_df.to_csv(sep="\t", index=False).rstrip("\n") if not stats_df.empty else ""
|
|
786
|
+
click.echo(output)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
@vseqarchive.command()
|
|
790
|
+
@click.pass_obj
|
|
791
|
+
def record_conda_env(vsarch: VisitSequenceArchiveMetadata) -> None:
|
|
792
|
+
"""Record the current Conda environment in the metadata database,
|
|
793
|
+
and print the hash used as a key for the env in the database.
|
|
794
|
+
|
|
795
|
+
Parameters
|
|
796
|
+
----------
|
|
797
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
798
|
+
The metadata interface.
|
|
799
|
+
"""
|
|
800
|
+
env_hash, env_json = compute_conda_env()
|
|
801
|
+
vsarch.record_conda_env(env_hash, env_json)
|
|
802
|
+
click.echo(env_hash.hex())
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
@vseqarchive.command("import-proto")
|
|
806
|
+
@click.argument("archive_base", type=str)
|
|
807
|
+
@click.argument("sim_date", type=str)
|
|
808
|
+
@click.argument("sim_index", type=str)
|
|
809
|
+
@click.option(
|
|
810
|
+
"--proto-sim-archive-url",
|
|
811
|
+
default="s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
812
|
+
show_default=True,
|
|
813
|
+
help="Base URI of the prototype simulation archive.",
|
|
814
|
+
)
|
|
815
|
+
@click.pass_obj
|
|
816
|
+
def import_proto(
|
|
817
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
818
|
+
archive_base: str,
|
|
819
|
+
sim_date: str,
|
|
820
|
+
sim_index: str,
|
|
821
|
+
proto_sim_archive_url: str = "s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
822
|
+
) -> None:
|
|
823
|
+
"""Import a simulation from the prototype simulation archive,
|
|
824
|
+
and print the UUID of the resultant entry in the destintation
|
|
825
|
+
archive.
|
|
826
|
+
|
|
827
|
+
Parameters
|
|
828
|
+
----------
|
|
829
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
830
|
+
The metadata interface instance.
|
|
831
|
+
archive_base :`str`
|
|
832
|
+
Base location of the destination archive.
|
|
833
|
+
sim_date : `str`
|
|
834
|
+
The ISO‑formatted date (e.g. ``'2025-03-12'``) by
|
|
835
|
+
which the prototype simulation is indexed.
|
|
836
|
+
sim_index : `str`
|
|
837
|
+
The index of the simulation which completes the
|
|
838
|
+
identification of the simulation in the prototype.
|
|
839
|
+
proto_sim_archive_url : `str`, optional
|
|
840
|
+
Base URI of the prototype simulation archive. Defaults to
|
|
841
|
+
``s3://rubin:rubin-scheduler-prenight/opsim/``.
|
|
842
|
+
"""
|
|
843
|
+
sim_uuid = vsarch.import_sim_from_prototype_sim_archive(
|
|
844
|
+
archive_base,
|
|
845
|
+
sim_date,
|
|
846
|
+
sim_index,
|
|
847
|
+
proto_sim_archive_url,
|
|
848
|
+
)
|
|
849
|
+
click.echo(sim_uuid)
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
@vseqarchive.command("export-proto")
|
|
853
|
+
@click.argument("uuid", type=click.UUID)
|
|
854
|
+
@click.option(
|
|
855
|
+
"--proto-sim-archive-url",
|
|
856
|
+
default="s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
857
|
+
show_default=True,
|
|
858
|
+
help="Base URL of the prototype simulation archive to which to export.",
|
|
859
|
+
)
|
|
860
|
+
@click.pass_obj
|
|
861
|
+
def export_proto(
|
|
862
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
863
|
+
uuid: UUID,
|
|
864
|
+
proto_sim_archive_url: str = "s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
865
|
+
) -> None:
|
|
866
|
+
"""Export a simulation from the visit sequence archive
|
|
867
|
+
to the prototype simulation archive, and print the
|
|
868
|
+
URL in the prototype sim archive.
|
|
869
|
+
|
|
870
|
+
Parameters
|
|
871
|
+
----------
|
|
872
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
873
|
+
The metadata interface instance.
|
|
874
|
+
uuid : `UUID`
|
|
875
|
+
UUID of the simulation to export.
|
|
876
|
+
proto_sim_archive_url : `str`
|
|
877
|
+
Base URL of the prototype simulation archive to which the simulation
|
|
878
|
+
should be exported.
|
|
879
|
+
"""
|
|
880
|
+
# Import here to avoid circular imports
|
|
881
|
+
from .prototype import export_sim_to_prototype_sim_archive
|
|
882
|
+
|
|
883
|
+
destination_rp = export_sim_to_prototype_sim_archive(
|
|
884
|
+
archive_metadata=vsarch,
|
|
885
|
+
sim_uuid=uuid,
|
|
886
|
+
proto_sim_archive_url=proto_sim_archive_url,
|
|
887
|
+
)
|
|
888
|
+
click.echo(destination_rp.geturl())
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
@vseqarchive.command()
|
|
892
|
+
@click.argument("dayobs", type=click.DateTime(formats=["%Y-%m-%d", "%Y%m%d"]))
|
|
893
|
+
@click.argument("telescope", type=click.Choice(["simonyi", "auxtel"]))
|
|
894
|
+
@click.option(
|
|
895
|
+
"--destination",
|
|
896
|
+
default=PRENIGHT_INDEX_URL,
|
|
897
|
+
type=str,
|
|
898
|
+
help="Base of the resource to which to write the prenight index.",
|
|
899
|
+
)
|
|
900
|
+
@click.pass_obj
|
|
901
|
+
def make_prenight_index(
|
|
902
|
+
vsarch: VisitSequenceArchiveMetadata,
|
|
903
|
+
dayobs: str | datetime.datetime | datetime.date | int | Time,
|
|
904
|
+
telescope: str,
|
|
905
|
+
destination: str,
|
|
906
|
+
) -> None:
|
|
907
|
+
"""Write a json index of prenight for a telescope and dayobs to a resource.
|
|
908
|
+
|
|
909
|
+
Parameters
|
|
910
|
+
----------
|
|
911
|
+
vsarch : `VisitSequenceArchiveMetadata`
|
|
912
|
+
The metadata interface instance.
|
|
913
|
+
dayobs : `datetime` or `date` or `int` or `str`
|
|
914
|
+
A datetime in the desired dayobs
|
|
915
|
+
telescope : `str`
|
|
916
|
+
The telescope, either ``simonyi`` or ``auxtel``
|
|
917
|
+
destination : `str`
|
|
918
|
+
Base of the resource to which to write the prenight index.
|
|
919
|
+
"""
|
|
920
|
+
dayobs = dayobs_to_date(dayobs)
|
|
921
|
+
assert isinstance(dayobs, datetime.date)
|
|
922
|
+
|
|
923
|
+
prenights = vsarch.sims_on_night_with_stats(
|
|
924
|
+
dayobs, tags=("prenight",), telescope=telescope, max_simulation_age=4000
|
|
925
|
+
).set_index("visitseq_uuid")
|
|
926
|
+
|
|
927
|
+
def to_hex(data: memoryview | None) -> str | None:
|
|
928
|
+
if data is None:
|
|
929
|
+
return None
|
|
930
|
+
|
|
931
|
+
assert isinstance(data, memoryview)
|
|
932
|
+
return data.hex()
|
|
933
|
+
|
|
934
|
+
prenights["conda_env_sha256"] = prenights["conda_env_sha256"].apply(to_hex)
|
|
935
|
+
|
|
936
|
+
def to_str_dayobs(d: datetime.date | None) -> str | None:
|
|
937
|
+
if d is None:
|
|
938
|
+
return None
|
|
939
|
+
return d.isoformat()
|
|
940
|
+
|
|
941
|
+
for column in ("sim_creation_day_obs", "first_day_obs", "last_day_obs", "parent_last_day_obs"):
|
|
942
|
+
prenights[column] = prenights[column].apply(to_str_dayobs)
|
|
943
|
+
|
|
944
|
+
# Convert UUID type into something to_json can deal with.
|
|
945
|
+
prenights.index = prenights.index.map(str)
|
|
946
|
+
for row in prenights.index:
|
|
947
|
+
parent_visitseq_uuid = prenights.loc[row, "parent_visitseq_uuid"]
|
|
948
|
+
if isinstance(parent_visitseq_uuid, UUID):
|
|
949
|
+
prenights.loc[row, "parent_visitseq_uuid"] = str(parent_visitseq_uuid)
|
|
950
|
+
|
|
951
|
+
table_json = prenights.to_json(orient="index", date_format="iso", indent=4)
|
|
952
|
+
|
|
953
|
+
destination_dir_rp = (
|
|
954
|
+
ResourcePath(destination)
|
|
955
|
+
.join(telescope)
|
|
956
|
+
.join(str(dayobs.year))
|
|
957
|
+
.join(str(dayobs.month), forceDirectory=True)
|
|
958
|
+
)
|
|
959
|
+
destination_rp = destination_dir_rp.join(f"{telescope}_prenights_for_{dayobs.isoformat()}.json")
|
|
960
|
+
destination_rp.write(table_json.encode())
|
|
961
|
+
click.echo(destination_rp.geturl())
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
if __name__ == "__main__":
|
|
965
|
+
vseqarchive()
|