gea-program 0.44.0__tar.gz → 0.45.0__tar.gz
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.
- {gea_program-0.44.0 → gea_program-0.45.0}/PKG-INFO +16 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/README.md +15 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/__init__.py +1 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/__main__.py +59 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/acceptance_tests.py +281 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/client_reports.py +15 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/dossier.py +134 -17
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/dossier_verify.py +49 -2
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/dossier.md +3 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/opseis.md +2 -2
- gea_program-0.45.0/gea/help/pad-summary.md +13 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/report-history.md +2 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/helplib.py +1 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/notify.py +6 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/op_seismicity.py +109 -5
- gea_program-0.45.0/gea/pad_summary.py +305 -0
- gea_program-0.45.0/gea/pdf_report.py +532 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_watch.py +10 -5
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/service.py +24 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/web/app.html +16 -4
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/PKG-INFO +16 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/SOURCES.txt +3 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/pyproject.toml +1 -1
- {gea_program-0.44.0 → gea_program-0.45.0}/LICENSE +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/alerts.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/backup.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/bench.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/blind_harness.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog_map.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/catalog_register.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/certificates.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/cli.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/config_versioning.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/conformance.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/control_room.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/correlation.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/dashboard.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/declarations.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/deviation.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/dlis.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/dnp3.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/doctor.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/drill_string.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/earth_model.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/economics.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/ed25519.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/edr.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/elevation.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/etp.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/example_register_map.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fat_sat.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/files.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/follower.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/forward_model.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/friction_priors.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/gamma.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/geodesy.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/hartip.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/alarms.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/alerts.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/backup.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/catalog.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/charts.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/conformance.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/control-room.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/data-in.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/declare.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/dlis.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/doctor.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/drift.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/economics.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/edr.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/etp.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/files.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/historian.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/instruments.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/interval.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/map.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/month-end.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/notifications.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/osdu.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/patches.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/quality.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/seismic.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/setup.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/simulation.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/site.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/sites.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/slip.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/soak.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/sra.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/start.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/structure.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/tools.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/transients.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/vibration.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/well-report.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/help/well3d.md +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/housekeeping.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/interval.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/jobs.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/live_ports.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/loadtest.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/modbus.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/model_card.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/month_end.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/opcua_port.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/operator_app.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/osdu.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/patches.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/penetrator.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/permits.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/piwebapi.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/ports.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/ppdm.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/project.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/quakeml.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reconciler.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/report_history.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sample_record.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sbom.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seedlink.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/segy.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_array.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_film.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_response.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_track.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/series_cache.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/service_life.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/shapefile.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/shell.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/shut_in.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/simulation.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/site_map.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sla_report.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/slip.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/soak.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/sra.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/store_forward.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/strata_join.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/structure.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/supervisor.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/survey_view.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/telemetry.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/tool_library.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/transient.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/vibration.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/well3d.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/well_assembler.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/well_report.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/wits0.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/witsml.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea/workspace.py +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/setup.cfg +0 -0
- {gea_program-0.44.0 → gea_program-0.45.0}/tests/test_acceptance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gea-program
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.0
|
|
4
4
|
Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
|
|
5
5
|
Author: Daniel T. Murphy
|
|
6
6
|
License: MPL-2.0
|
|
@@ -648,6 +648,21 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### The lateral, the printable reports, the daily summary and the key
|
|
652
|
+
|
|
653
|
+
An earthquake near a well with a directional survey is measured from the
|
|
654
|
+
nearest point of its lateral, not from the pad - with the measured depth
|
|
655
|
+
there and the distance to the hypocentre - so a protocol radius is read
|
|
656
|
+
against the well bore a regulator means. Every client report is also
|
|
657
|
+
written as a PDF beside its HTML (the drawings redrawn, long tables
|
|
658
|
+
continued across pages) and kept in report history. Each morning the
|
|
659
|
+
program writes a one-page pad summary - what changed, what is due, what is
|
|
660
|
+
still missing - and sends it through the notification rules (`gea
|
|
661
|
+
pad-summary`). The dossier signing key can be retired and replaced: the old
|
|
662
|
+
key signs the hand-over, earlier dossiers still check out, and later ones
|
|
663
|
+
carry the rotation so a client can follow it (`gea dossier --rotate-key`).
|
|
664
|
+
`gea help pad-summary`.
|
|
665
|
+
|
|
651
666
|
### The earthquake watch and the alerts
|
|
652
667
|
|
|
653
668
|
While a well operates, the program reads the USGS earthquake catalogue
|
|
@@ -609,6 +609,21 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### The lateral, the printable reports, the daily summary and the key
|
|
613
|
+
|
|
614
|
+
An earthquake near a well with a directional survey is measured from the
|
|
615
|
+
nearest point of its lateral, not from the pad - with the measured depth
|
|
616
|
+
there and the distance to the hypocentre - so a protocol radius is read
|
|
617
|
+
against the well bore a regulator means. Every client report is also
|
|
618
|
+
written as a PDF beside its HTML (the drawings redrawn, long tables
|
|
619
|
+
continued across pages) and kept in report history. Each morning the
|
|
620
|
+
program writes a one-page pad summary - what changed, what is due, what is
|
|
621
|
+
still missing - and sends it through the notification rules (`gea
|
|
622
|
+
pad-summary`). The dossier signing key can be retired and replaced: the old
|
|
623
|
+
key signs the hand-over, earlier dossiers still check out, and later ones
|
|
624
|
+
carry the rotation so a client can follow it (`gea dossier --rotate-key`).
|
|
625
|
+
`gea help pad-summary`.
|
|
626
|
+
|
|
612
627
|
### The earthquake watch and the alerts
|
|
613
628
|
|
|
614
629
|
While a well operates, the program reads the USGS earthquake catalogue
|
|
@@ -584,9 +584,21 @@ def main(argv=None) -> int:
|
|
|
584
584
|
p_dz.add_argument("--sources", type=str, default="", help="with --verify: a folder holding the source records to check against their hashes")
|
|
585
585
|
p_dz.add_argument("--key", action="store_true", help="print this site's signing key fingerprint")
|
|
586
586
|
p_dz.add_argument("--list", action="store_true", help="list the dossiers this site has signed")
|
|
587
|
+
p_dz.add_argument("--rotate-key", action="store_true", help="retire this site's signing key and start a new one, signed over by the old (give --reason)")
|
|
588
|
+
p_dz.add_argument("--reason", type=str, default="", help="with --rotate-key: why the key is retired")
|
|
589
|
+
p_dz.add_argument("--key-history", action="store_true", help="the site's keys, current and retired, and the rotations between them")
|
|
587
590
|
p_dz.add_argument("--actor", type=str, default="cli")
|
|
588
591
|
p_dz.add_argument("--json", action="store_true")
|
|
589
592
|
|
|
593
|
+
p_ps = sub.add_parser("pad-summary", help="the daily pad summary: what changed since the last one, what is due, what is still missing - one page, kept in report history; the service writes it each day and sends it through the notification rules")
|
|
594
|
+
p_ps.add_argument("--workspace", type=str, required=True)
|
|
595
|
+
p_ps.add_argument("--send", action="store_true", help="also hand it to the notification rules (the event pad.summary)")
|
|
596
|
+
p_ps.add_argument("--at", type=str, default=None, help="the daily time the service writes it, HH:MM UTC")
|
|
597
|
+
p_ps.add_argument("--on", action="store_true", help="the service writes it each day")
|
|
598
|
+
p_ps.add_argument("--off", action="store_true", help="the service does not")
|
|
599
|
+
p_ps.add_argument("--actor", type=str, default="cli")
|
|
600
|
+
p_ps.add_argument("--json", action="store_true")
|
|
601
|
+
|
|
590
602
|
p_rh = sub.add_parser("reports-history", help="every version of every client report this workspace has written, kept under reports/history; --prune keeps the newest N")
|
|
591
603
|
p_rh.add_argument("--workspace", type=str, required=True)
|
|
592
604
|
p_rh.add_argument("--family", type=str, default=None, help="e.g. wells/Well-1/well_report or pad_report")
|
|
@@ -2297,6 +2309,23 @@ def main(argv=None) -> int:
|
|
|
2297
2309
|
k = _DZ.key_info(ws)
|
|
2298
2310
|
print(f"Ed25519 {k['fingerprint']} (public key {k['public_key']}, made {k['created_utc']})" if k else "no signing key yet: the first dossier makes one")
|
|
2299
2311
|
return 0
|
|
2312
|
+
if a.rotate_key:
|
|
2313
|
+
try:
|
|
2314
|
+
r_ = _DZ.rotate_key(ws, a.actor, a.reason)
|
|
2315
|
+
except ValueError as ex:
|
|
2316
|
+
raise SystemExit(f"dossier: {ex}")
|
|
2317
|
+
print(f"retired {r_['old']['fingerprint']}; the new key is {r_['new']['fingerprint']}")
|
|
2318
|
+
print(" the rotation is signed by both keys and rides in every later dossier; give the new fingerprint to your clients")
|
|
2319
|
+
return 0
|
|
2320
|
+
if a.key_history:
|
|
2321
|
+
h_ = _DZ.key_history(ws)
|
|
2322
|
+
if a.json:
|
|
2323
|
+
print(_json.dumps(h_, default=str)); return 0
|
|
2324
|
+
for k in h_["keys"]:
|
|
2325
|
+
print(f"{k['fingerprint']} made {k.get('created_utc') or '?'} " + (f"retired {k['retired_utc']} ({k.get('reason', '')}) -> {k.get('successor')}" if k.get("retired_utc") else "CURRENT"))
|
|
2326
|
+
if not h_["keys"]:
|
|
2327
|
+
print("no key history yet")
|
|
2328
|
+
return 0
|
|
2300
2329
|
if a.list:
|
|
2301
2330
|
for e in reversed(_DZ.ledger(ws)):
|
|
2302
2331
|
print(f"{e['utc']} {e['dossier_id']} {e['display']:<24} {e['n_files']:>4} files {e['path']}")
|
|
@@ -2309,6 +2338,36 @@ def main(argv=None) -> int:
|
|
|
2309
2338
|
print(f" {os.path.join(ws.reports_dir, e['path'])}")
|
|
2310
2339
|
print(f" key fingerprint {e['fingerprint']}")
|
|
2311
2340
|
return 0
|
|
2341
|
+
elif a.cmd == "pad-summary":
|
|
2342
|
+
import json as _json
|
|
2343
|
+
from . import pad_summary as _PS
|
|
2344
|
+
from .cli import _safe_stdout
|
|
2345
|
+
from .notify import Notifier
|
|
2346
|
+
from .workspace import Workspace, WorkspaceError
|
|
2347
|
+
_safe_stdout()
|
|
2348
|
+
try:
|
|
2349
|
+
ws = Workspace(a.workspace)
|
|
2350
|
+
except WorkspaceError as e:
|
|
2351
|
+
raise SystemExit(f"pad-summary: {e}")
|
|
2352
|
+
if a.at or a.on or a.off:
|
|
2353
|
+
try:
|
|
2354
|
+
s_ = _PS.set_settings(ws, a.actor, enabled=(True if a.on else False if a.off else None), at=a.at)
|
|
2355
|
+
except ValueError as e:
|
|
2356
|
+
raise SystemExit(f"pad-summary: {e}")
|
|
2357
|
+
print(f"daily pad summary {'on' if s_['enabled'] else 'off'} at {s_['at']} UTC, sent through the notification rules: {'yes' if s_['send'] else 'no'}")
|
|
2358
|
+
return 0
|
|
2359
|
+
n_ = None
|
|
2360
|
+
if a.send:
|
|
2361
|
+
n_ = Notifier(ws.path); n_.reload()
|
|
2362
|
+
r = _PS.write(ws, __version__, a.actor, n_, send=bool(a.send))
|
|
2363
|
+
if a.json:
|
|
2364
|
+
print(_json.dumps(r, default=str)); return 0
|
|
2365
|
+
print(f"wrote {r['report_id']} (since {r['since']}): {r['paths']['html']}")
|
|
2366
|
+
if r['paths'].get('pdf'):
|
|
2367
|
+
print(f" printable: {r['paths']['pdf']}")
|
|
2368
|
+
if a.send:
|
|
2369
|
+
print(f" sent: {', '.join(str(d['channel']) + (' ok' if d['ok'] else ' FAILED') for d in r['delivered']) or 'no notification rule for pad.summary'}")
|
|
2370
|
+
return 0
|
|
2312
2371
|
elif a.cmd == "reports-history":
|
|
2313
2372
|
import json as _json
|
|
2314
2373
|
from . import report_history as _RH
|
|
@@ -7827,7 +7827,7 @@ def section_by_edr(tmp: str) -> None:
|
|
|
7827
7827
|
"survey": r_svy.returncode == 0 and "stations: 835 depth 584.0 - 838.2 m (the log is indexed in feet; converted to metres for the survey)" in r_svy.stdout
|
|
7828
7828
|
and "gamma -> GR" in r_svy.stdout and "NOT ESTIMATED: no density" in r_svy.stdout,
|
|
7829
7829
|
"cli": r_self.returncode == 0 and "Total depth 2,750.0 ft MD" in r_self.stdout and r_file.returncode == 0 and Path(out, "md_tvd.csv").is_file()
|
|
7830
|
-
and "written: drilling_record.md, drilling_record.html, drilling_record.json, gamma_log.las, md_tvd.csv" in r_file.stdout
|
|
7830
|
+
and "written: drilling_record.md, drilling_record.html, drilling_record.json, drilling_record.pdf, gamma_log.las, md_tvd.csv" in r_file.stdout
|
|
7831
7831
|
and r_json.returncode == 0 and json.loads(r_json.stdout)["n_sections"] == 3 and r_bad.returncode != 0 and "not an EDR export" in (r_bad.stderr + r_bad.stdout)
|
|
7832
7832
|
and r_none.returncode != 0 and "edr needs --file" in (r_none.stderr + r_none.stdout) and "<" not in (r_none.stderr + r_none.stdout).split("edr needs")[1],
|
|
7833
7833
|
"ingest": r_hist.returncode == 1 and "Traceback" not in (r_hist.stderr + r_hist.stdout) and "which is an EDR export" in (r_hist.stderr + r_hist.stdout)
|
|
@@ -10331,6 +10331,285 @@ def section_cm_watch_and_alerts(tmp: str) -> None:
|
|
|
10331
10331
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
10332
10332
|
|
|
10333
10333
|
|
|
10334
|
+
def section_cn_four(tmp: str) -> None:
|
|
10335
|
+
"""Section CN - the distance from the lateral, the printable PDF of every client report, the daily pad summary and
|
|
10336
|
+
the signing key's rotation: an earthquake measured from the nearest point of a surveyed lateral (in plan, with the
|
|
10337
|
+
measured depth there and the distance to the hypocentre) where the pad is kilometres off, a protocol level reached
|
|
10338
|
+
because of it, a well without a survey still measured from its pad, the watch circle reaching as far as the
|
|
10339
|
+
laterals; a PDF beside each report, read back page by page, kept in history, its tables continued across pages;
|
|
10340
|
+
the summary's three lists, its daily hour, its history and its delivery through a webhook rule; a key retired and
|
|
10341
|
+
a new one signed over by the old, earlier dossiers still valid, a dossier signed with the retired key afterwards
|
|
10342
|
+
not the site's, a lost key recorded unsigned; the API, the CLI, the tabs and the help pages."""
|
|
10343
|
+
import http.cookiejar
|
|
10344
|
+
import http.server
|
|
10345
|
+
import math as _m
|
|
10346
|
+
import subprocess as _sp
|
|
10347
|
+
import threading as _th
|
|
10348
|
+
import urllib.request
|
|
10349
|
+
import urllib.error
|
|
10350
|
+
import zipfile as _zf
|
|
10351
|
+
from datetime import datetime, timedelta, timezone
|
|
10352
|
+
from . import __version__
|
|
10353
|
+
from . import alerts as AL
|
|
10354
|
+
from . import client_reports as CR
|
|
10355
|
+
from . import dossier as DZ
|
|
10356
|
+
from . import edr as E
|
|
10357
|
+
from . import geodesy as GD
|
|
10358
|
+
from . import helplib as H
|
|
10359
|
+
from . import op_seismicity as OS
|
|
10360
|
+
from . import pad_summary as PS
|
|
10361
|
+
from . import pdf_report as PDF
|
|
10362
|
+
from . import report_history as RH
|
|
10363
|
+
from . import seismic_watch as SW
|
|
10364
|
+
from . import well3d as W
|
|
10365
|
+
from . import well_report as WR
|
|
10366
|
+
from .notify import Notifier
|
|
10367
|
+
from .workspace import Workspace
|
|
10368
|
+
from .service import Service, Users
|
|
10369
|
+
pkg = Path(__file__).parent
|
|
10370
|
+
d = Path(tmp, "cn"); d.mkdir()
|
|
10371
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
10372
|
+
FT = 0.3048
|
|
10373
|
+
|
|
10374
|
+
# -- CN1 the distance from the lateral ---------------------------------------------------------------------------
|
|
10375
|
+
ev = E.selftest_events(str(d))
|
|
10376
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad CN", actor="tester")
|
|
10377
|
+
wl = ws.add_well_file(ev["path"], display="LAT-1H", actor="tester"); wv = ws.add_well_file(ev["path"], display="VERT-2", actor="tester")
|
|
10378
|
+
la0, lo0 = 31.40, -104.00
|
|
10379
|
+
ws.set_well(wl["id"], surface={"lat": la0, "lon": lo0, "datum": "WGS84", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
|
|
10380
|
+
ws.set_well(wv["id"], surface={"lat": 31.30, "lon": -104.00, "datum": "WGS84"}, actor="tester")
|
|
10381
|
+
# vertical to 8,000 ft, a 90-degree build over 1,000 ft (radius 636.62 ft), then 10,000 ft due east, true north
|
|
10382
|
+
W.attach_trajectory(ws, wl["id"], "s.csv", b"MD (ft),Inc (deg),Azi (deg)\n0,0,90\n8000,0,90\n9000,90,90\n19000,90,90\n", "tester", "true", "ft", None, None, None, "test survey")
|
|
10383
|
+
R_ = 1000.0 / (_m.pi / 2)
|
|
10384
|
+
fr = GD._tm(la0, lo0, 1.0, 0.0, 0.0, "WGS84")
|
|
10385
|
+
z_lat = (8000.0 + R_) * FT - 3000.0 * FT # the lateral's depth below sea level, m
|
|
10386
|
+
pA = GD.unproject(5000.0 * FT, 500.0, fr) # 500 m north of the lateral at 5,000 ft east
|
|
10387
|
+
pB = GD.unproject((10000.0 + R_) * FT + 300.0, 400.0, fr) # 500 m beyond the toe, north-east
|
|
10388
|
+
t_ev = (now - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%S")
|
|
10389
|
+
OS.save_operation(ws, "tester", wl["id"], "completion", (now - timedelta(days=3)).strftime("%Y-%m-%d"), None, "stage log")
|
|
10390
|
+
sc = OS.workspace_scene(ws)
|
|
10391
|
+
sc["events"] = [{"event_id": "A", "time": t_ev, "magnitude": 2.6, "mag_type": "ml", "lat": pA[0], "lon": pA[1], "depth_km": 2.0},
|
|
10392
|
+
{"event_id": "B", "time": t_ev, "magnitude": 3.1, "mag_type": "ml", "lat": pB[0], "lon": pB[1], "depth_km": None},
|
|
10393
|
+
{"event_id": "C", "time": t_ev, "magnitude": 1.2, "mag_type": "ml", "lat": 31.305, "lon": -104.0, "depth_km": 4.0}]
|
|
10394
|
+
sc["catalog"] = {"start": (now - timedelta(days=3650)).strftime("%Y-%m-%d"), "end": now.strftime("%Y-%m-%d"), "min_magnitude": 1.0}
|
|
10395
|
+
sc["settings"] = {**sc["settings"], "protocol": "oklahoma-occ-2016", "radius_km": 2.0}
|
|
10396
|
+
res = OS.analyse(sc)
|
|
10397
|
+
rw = {w_["display"]: w_ for w_ in res["wells"]}
|
|
10398
|
+
eA = next(e for e in rw["LAT-1H"]["events"] if e["event_id"] == "A")
|
|
10399
|
+
eB = next(e for e in rw["LAT-1H"]["events"] if e["event_id"] == "B")
|
|
10400
|
+
eC = next(e for e in rw["VERT-2"]["events"] if e["event_id"] == "C")
|
|
10401
|
+
hypo = _m.hypot(500.0, 2000.0 - z_lat) / 1000.0
|
|
10402
|
+
act = SW.active(ws, now)
|
|
10403
|
+
reach = OS.lateral_of(ws, ws.well(wl["id"]), la0, lo0)["reach_km"]
|
|
10404
|
+
pg = OS.page(ws, "workspace")
|
|
10405
|
+
c1 = {
|
|
10406
|
+
"plan": abs(eA["distance_km"] - 0.5) < 0.005 and eA["distance_basis"] == "lateral" and abs(eA["nearest_md_ft"] - (9000 + 5000 - R_)) < 30
|
|
10407
|
+
and abs(eA["distance_surface_km"] - _m.hypot(1.524, 0.5)) < 0.01 and abs(eA["hypocentral_km"] - hypo) < 0.02,
|
|
10408
|
+
"toe": abs(eB["distance_km"] - 0.5) < 0.005 and abs(eB["nearest_md_ft"] - 19000) < 1 and eB["distance_surface_km"] > 3.4 and "hypocentral_km" not in eB,
|
|
10409
|
+
"protocol": eB["within_radius"] and (eB.get("protocol") or {}).get("highest", {}).get("m") == 3.0 and eA["protocol"]["highest"]["m"] == 2.5,
|
|
10410
|
+
"surface": eC["distance_basis"] == "surface" and rw["VERT-2"]["distance_from"]["basis"] == "surface" and abs(eC["distance_km"] - 0.556) < 0.01,
|
|
10411
|
+
"basis": rw["LAT-1H"]["distance_from"]["basis"] == "lateral" and "19,000 ft MD" in rw["LAT-1H"]["distance_from"]["detail"] and abs(reach - ((10000 + R_) * FT) / 1000) < 0.01,
|
|
10412
|
+
"circle": act["circle"] is not None and act["circle"]["radius_km"] >= reach + 20.0 - 0.01,
|
|
10413
|
+
"text": "lateral, nearest at" in OS.text(res) and "distances from the nearest point" in OS.text(res),
|
|
10414
|
+
"page": "lateral" not in [w_.get("lateral") for w_ in pg["wells"]] and all("lateral" not in w_ for w_ in pg["wells"]) and pg["query"]["radius_km"] > 20 + reach - 0.1,
|
|
10415
|
+
}
|
|
10416
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
10417
|
+
ok(not failed1,
|
|
10418
|
+
"CN1 the distance from the lateral: an earthquake 500 m off a surveyed lateral and 1.6 km from its pad is 0.50 km away, at the "
|
|
10419
|
+
"measured depth of the nearest point, with the distance to its hypocentre; one beyond the toe reaches a protocol level the pad "
|
|
10420
|
+
"distance would not; a well without a survey still measured from its pad; the watch circle and the query reach the laterals"
|
|
10421
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
10422
|
+
|
|
10423
|
+
# -- CN2 the printable PDF -----------------------------------------------------------------------------------------
|
|
10424
|
+
rw1 = WR.write_well(ws, wl["id"], __version__, actor="tester")
|
|
10425
|
+
rp1 = WR.write_pad(ws, __version__, actor="tester")
|
|
10426
|
+
pdfs = [rw1["paths"].get("pdf"), rp1["paths"].get("pdf")]
|
|
10427
|
+
rd = [PDF.read(Path(p).read_bytes()) for p in pdfs if p]
|
|
10428
|
+
long_doc = CR.Document("A long table ≥ two pages", "TEST-1", [["Report ID", "TEST-1"], ["Result", "NO CHANGE"]],
|
|
10429
|
+
[CR.Section("1", "Rows", ["° ± – — ≤ x" * 3], [CR.Table(["Row", "A long cell"], [[i, "word " * (i % 40) + "unbrokenwordthatiswiderthanthecolumnitsitsin" * (1 if i == 7 else 0)] for i in range(160)], "the caption")])], footer="footer line")
|
|
10430
|
+
b1, b2 = PDF.render(long_doc), PDF.render(long_doc)
|
|
10431
|
+
rl = PDF.read(b1)
|
|
10432
|
+
hist_pdf = [f_["name"] for e_ in RH.entries(ws.reports_dir) for f_ in e_["files"] if f_["name"].endswith(".pdf")]
|
|
10433
|
+
c2 = {
|
|
10434
|
+
"written": len(rd) == 2 and all(r_["ok"] for r_ in rd) and CR.LAST_PDF_ERROR is None
|
|
10435
|
+
and all(Path(p).with_suffix(".html").is_file() for p in pdfs),
|
|
10436
|
+
"content": rw1["report_id"] in rd[0]["text"][0] and "page 1 of" in rd[0]["text"][0] and "LAT-1H" in "\n".join(rd[0]["text"])
|
|
10437
|
+
and any("plan view" in t_ for t_ in rd[0]["text"]) and rp1["report_id"] in rd[1]["text"][0],
|
|
10438
|
+
"fonts": set(rd[0]["fonts"]) == {"Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique"},
|
|
10439
|
+
"pages": rl["ok"] and rl["pages"] >= 3 and all("A long cell" in t_ for t_ in rl["text"][:-1]) and f"page {rl['pages']} of {rl['pages']}" in rl["text"][-1]
|
|
10440
|
+
and ">=" in rl["text"][0] and "°" in rl["text"][0] and "footer line" in rl["text"][-1],
|
|
10441
|
+
"same": b1 == b2,
|
|
10442
|
+
"history": "well_report.pdf" in hist_pdf and "pad_report.pdf" in hist_pdf,
|
|
10443
|
+
"links": "['pdf','html','md','json','csv']" in (pkg / "web" / "app.html").read_text(encoding="utf-8"),
|
|
10444
|
+
}
|
|
10445
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
10446
|
+
ok(not failed2,
|
|
10447
|
+
"CN2 the printable PDF: the well and pad reports each written as a PDF beside the HTML and read back - the report number, "
|
|
10448
|
+
"'page 1 of', the plan view's own labels redrawn, the four base fonts; a 160-row table continued across pages with its "
|
|
10449
|
+
"header on each, a word wider than its column broken, the symbols carried; the same bytes for the same report; kept in history"
|
|
10450
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
10451
|
+
|
|
10452
|
+
# -- CN3 the daily pad summary -------------------------------------------------------------------------------------
|
|
10453
|
+
hooks = []
|
|
10454
|
+
|
|
10455
|
+
class Hook(http.server.BaseHTTPRequestHandler):
|
|
10456
|
+
def log_message(self, *a):
|
|
10457
|
+
pass
|
|
10458
|
+
|
|
10459
|
+
def do_POST(self):
|
|
10460
|
+
n = int(self.headers.get("Content-Length") or 0)
|
|
10461
|
+
hooks.append(json.loads(self.rfile.read(n) or b"{}"))
|
|
10462
|
+
self.send_response(200); self.end_headers()
|
|
10463
|
+
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Hook)
|
|
10464
|
+
_th.Thread(target=srv.serve_forever, daemon=True).start()
|
|
10465
|
+
Path(wsp, "config", "notifications.json").write_text(json.dumps({"channels": [{"name": "hook", "kind": "webhook", "url": f"http://127.0.0.1:{srv.server_address[1]}/hook"}],
|
|
10466
|
+
"rules": [{"event": "pad.summary", "channels": ["hook"]}], "quiet_s": 0}), encoding="utf-8")
|
|
10467
|
+
nt = Notifier(wsp); nt.reload()
|
|
10468
|
+
AL.raise_alert(ws, None, {"event": "seismic.event", "key": "seismic.event:A", "well": "LAT-1H", "detail": "M 2.6 0.5 km from the lateral of LAT-1H"}, now)
|
|
10469
|
+
OS.save_operation(ws, "tester", wv["id"], "disposal", (now - timedelta(days=1)).strftime("%Y-%m-%d"), None, "an honest permit")
|
|
10470
|
+
OS.save_operation(ws, "tester", wv["id"], "injection", (now + timedelta(days=2)).strftime("%Y-%m-%d"), None, "permit 2")
|
|
10471
|
+
g = PS.gather(ws, now + timedelta(minutes=1), now - timedelta(hours=1))
|
|
10472
|
+
t0 = now.replace(hour=10, minute=59, second=0) if now.hour < 23 else now.replace(hour=10, minute=59, second=0)
|
|
10473
|
+
due_before = PS.due_now(ws, t0)
|
|
10474
|
+
s1 = PS.write(ws, __version__, "tester", nt, now + timedelta(minutes=2))
|
|
10475
|
+
due_after = PS.due_now(ws, (now + timedelta(minutes=2)).replace(hour=23, minute=0))
|
|
10476
|
+
nxt = (now + timedelta(days=1)).replace(hour=11, minute=1, second=0)
|
|
10477
|
+
due_next = PS.due_now(ws, nxt)
|
|
10478
|
+
ws.audit("tester", "test.after", {})
|
|
10479
|
+
s2 = PS.write(ws, __version__, "tester", nt, now + timedelta(minutes=5), send=False)
|
|
10480
|
+
srv.shutdown()
|
|
10481
|
+
vers = [e_ for e_ in RH.entries(ws.reports_dir) if e_["family"] == "daily/pad_summary"]
|
|
10482
|
+
html_ = Path(wsp, "reports", "daily", "pad_summary.html").read_text(encoding="utf-8")
|
|
10483
|
+
c3 = {
|
|
10484
|
+
"lists": any(c_["action"] == "seismicity.operation" or c_["action"].startswith("seismicity.") for c_ in g["changed"]) and len(g["alerts"]) == 1
|
|
10485
|
+
and any(x["status"] == "UNDER WAY" and "completion" in x["item"] for x in g["due"]) and any(x["status"] == "STARTS SOON" for x in g["due"])
|
|
10486
|
+
and any(x["item"] == "the earthquake watch" for x in g["due"]) and any(x["item"] == "the backup" and x["status"] == "NEVER" for x in g["due"])
|
|
10487
|
+
and any(m_["level"] == "required" for m_ in g["missing"]),
|
|
10488
|
+
"clean": "honest" not in html_.lower() and "(see the audit log)" in html_,
|
|
10489
|
+
"files": all(Path(wsp, "reports", "daily", "pad_summary." + x).is_file() for x in ("pdf", "html", "md", "json")) and PDF.read(Path(wsp, "reports", "daily", "pad_summary.pdf").read_bytes())["ok"],
|
|
10490
|
+
"history": len(vers) == 2 and s2["since"] == PS._utc(now + timedelta(minutes=2)),
|
|
10491
|
+
"hour": (not due_before or now.hour >= 11) and not due_after and due_next,
|
|
10492
|
+
"sent": len(hooks) == 1 and hooks[0].get("event") == "pad.summary" and "What is due" in hooks[0].get("text", "") and s1["delivered"] == [{"channel": "hook", "ok": True}] and s2["delivered"] == [],
|
|
10493
|
+
"event": "pad.summary" in __import__("gea.notify", fromlist=["EVENTS"]).EVENTS,
|
|
10494
|
+
}
|
|
10495
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
10496
|
+
ok(not failed3,
|
|
10497
|
+
"CN3 the daily pad summary: the actions, alerts and new versions since the last, what is under way, starting soon or due, the "
|
|
10498
|
+
"watch and the backup, the checklist's missing items; a typed note a client report may not carry left out; PDF, HTML, markdown "
|
|
10499
|
+
"and JSON with each day's version kept; due once a day from its hour; delivered through a webhook rule for pad.summary"
|
|
10500
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
10501
|
+
|
|
10502
|
+
# -- CN4 the key's rotation ----------------------------------------------------------------------------------------
|
|
10503
|
+
e1 = DZ.build(ws, "tester", __version__, wl["id"])
|
|
10504
|
+
k1 = DZ.key_info(ws)
|
|
10505
|
+
old_file = Path(DZ.key_path(ws)).read_text(encoding="utf-8")
|
|
10506
|
+
try:
|
|
10507
|
+
DZ.rotate_key(ws, "tester", " ")
|
|
10508
|
+
empty_refused = False
|
|
10509
|
+
except ValueError:
|
|
10510
|
+
empty_refused = True
|
|
10511
|
+
time.sleep(1.1)
|
|
10512
|
+
rot = DZ.rotate_key(ws, "tester", "scheduled change")
|
|
10513
|
+
new_file = Path(DZ.key_path(ws)).read_text(encoding="utf-8")
|
|
10514
|
+
time.sleep(1.1)
|
|
10515
|
+
e2 = DZ.build(ws, "tester", __version__, wl["id"])
|
|
10516
|
+
v1 = DZ.verify(os.path.join(ws.reports_dir, e1["path"]), ws)
|
|
10517
|
+
v2 = DZ.verify(os.path.join(ws.reports_dir, e2["path"]), ws)
|
|
10518
|
+
Path(DZ.key_path(ws)).write_text(old_file, encoding="utf-8") # someone still holding the retired key signs with it
|
|
10519
|
+
time.sleep(1.1)
|
|
10520
|
+
e_bad = DZ.build(ws, "tester", __version__, wl["id"])
|
|
10521
|
+
Path(DZ.key_path(ws)).write_text(new_file, encoding="utf-8")
|
|
10522
|
+
v_bad = DZ.verify(os.path.join(ws.reports_dir, e_bad["path"]), ws)
|
|
10523
|
+
x = d / "vx"; x.mkdir()
|
|
10524
|
+
with _zf.ZipFile(os.path.join(ws.reports_dir, e2["path"])) as z_:
|
|
10525
|
+
z_.extract("verify_dossier.py", x)
|
|
10526
|
+
rots_in = json.loads(z_.read("keys/rotations.json"))
|
|
10527
|
+
sa = _sp.run([sys.executable, "-I", str(x / "verify_dossier.py"), os.path.join(ws.reports_dir, e2["path"])], capture_output=True, text=True)
|
|
10528
|
+
hist_txt = Path(wsp, DZ.KEY_HISTORY).read_text(encoding="utf-8")
|
|
10529
|
+
c4 = {
|
|
10530
|
+
"rotated": empty_refused and rot["old"]["fingerprint"] == k1["fingerprint"] and rot["new"]["fingerprint"] != k1["fingerprint"]
|
|
10531
|
+
and bool(rot["rotation"]["old_signature"]) and json.loads(old_file)["seed"] not in new_file + hist_txt and '"seed"' not in hist_txt,
|
|
10532
|
+
"old_valid": v1["status"] == "VALID" and any("a former key of this site" in c_["detail"] and c_["ok"] for c_ in v1["workspace_checks"]),
|
|
10533
|
+
"new_valid": v2["status"] == "VALID" and any(c_["check"] == "the key rotations verify" and c_["ok"] for c_ in v2["checks"])
|
|
10534
|
+
and v2["key_chain"][0]["old"] == k1["fingerprint"] and v2["key_chain"][0]["signed_by_old"] is True and len(rots_in["rotations"]) == 1,
|
|
10535
|
+
"after_retired": v_bad["status"] == "INVALID" and any("not the site's" in c_["detail"] for c_ in v_bad["workspace_checks"])
|
|
10536
|
+
and any(c_["check"] == "the key rotations verify" and not c_["ok"] and "does not end at the key" in c_["detail"] for c_ in v_bad["checks"]),
|
|
10537
|
+
"standalone": sa.returncode == 0 and "signed by both keys" in sa.stdout and k1["fingerprint"] in sa.stdout,
|
|
10538
|
+
"page": [r_["signed_by_old"] for r_ in DZ.page(ws)["key_history"]["rotations"]] == [True] and len(DZ.page(ws)["key_history"]["keys"]) == 2,
|
|
10539
|
+
}
|
|
10540
|
+
os.remove(DZ.key_path(ws)); time.sleep(1.1)
|
|
10541
|
+
e3 = DZ.build(ws, "tester", __version__, wl["id"])
|
|
10542
|
+
v3 = DZ.verify(os.path.join(ws.reports_dir, e3["path"]), ws)
|
|
10543
|
+
c4["lost"] = (v3["status"] == "VALID" and len(v3["key_chain"]) == 2 and v3["key_chain"][1]["signed_by_old"] is None
|
|
10544
|
+
and "not signed by the retired key" in next(c_["detail"] for c_ in v3["checks"] if c_["check"] == "the key rotations verify"))
|
|
10545
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
10546
|
+
ok(not failed4,
|
|
10547
|
+
"CN4 the key's rotation: a reason required; the old key signs the hand-over and its seed is gone; a dossier it signed before still "
|
|
10548
|
+
"VALID here as a former key's, one signed after with it not the site's (its own rotations do not end at its key); the next dossier carries the rotation, which its own "
|
|
10549
|
+
"checker verifies with both signatures; a lost key file recorded as a rotation the old key did not sign"
|
|
10550
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
10551
|
+
|
|
10552
|
+
# -- CN5 the API, the CLI, the tabs, the help ------------------------------------------------------------------------
|
|
10553
|
+
Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator"); Users(str(Path(wsp, "users.json"))).add("ad", "admin-pass-12", "admin")
|
|
10554
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
10555
|
+
base_url = svc.url.rstrip("/")
|
|
10556
|
+
|
|
10557
|
+
def opener(name, pw_):
|
|
10558
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
10559
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
10560
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
10561
|
+
return o
|
|
10562
|
+
|
|
10563
|
+
def call(o, method, path, body=None):
|
|
10564
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
10565
|
+
if method == "POST":
|
|
10566
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
10567
|
+
try:
|
|
10568
|
+
with o.open(rq, timeout=120) as rr_:
|
|
10569
|
+
ct = rr_.headers.get("Content-Type", "")
|
|
10570
|
+
b_ = rr_.read()
|
|
10571
|
+
return rr_.status, (json.loads(b_) if "json" in ct else {"_ct": ct, "_len": len(b_)})
|
|
10572
|
+
except urllib.error.HTTPError as e:
|
|
10573
|
+
return e.code, json.loads(e.read())
|
|
10574
|
+
try:
|
|
10575
|
+
O, A = opener("op", "op-pass-123"), opener("ad", "admin-pass-12")
|
|
10576
|
+
s_g, gj = call(O, "GET", "/api/pad-summary")
|
|
10577
|
+
s_w, wj = call(O, "POST", "/api/pad-summary", {})
|
|
10578
|
+
s_so, _ = call(O, "POST", "/api/pad-summary/settings", {"at": "12:30"})
|
|
10579
|
+
s_sa, sj = call(A, "POST", "/api/pad-summary/settings", {"at": "12:30", "enabled": True})
|
|
10580
|
+
s_sb, _ = call(A, "POST", "/api/pad-summary/settings", {"at": "25:00"})
|
|
10581
|
+
s_ro, _ = call(O, "POST", "/api/dossier/rotate", {"reason": "x"})
|
|
10582
|
+
s_re, _ = call(A, "POST", "/api/dossier/rotate", {"reason": ""})
|
|
10583
|
+
s_ra, raj = call(A, "POST", "/api/dossier/rotate", {"reason": "API test"})
|
|
10584
|
+
s_dz, dzj = call(O, "GET", "/api/dossier")
|
|
10585
|
+
s_pdf, pdj = call(O, "GET", "/reports/daily/pad_summary.pdf")
|
|
10586
|
+
finally:
|
|
10587
|
+
svc.stop()
|
|
10588
|
+
cli_ps = _sp.run([sys.executable, "-m", "gea", "pad-summary", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
10589
|
+
cli_kh = _sp.run([sys.executable, "-m", "gea", "dossier", "--workspace", wsp, "--key-history"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
10590
|
+
cli_rk = _sp.run([sys.executable, "-m", "gea", "dossier", "--workspace", wsp, "--rotate-key"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
10591
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
10592
|
+
service_src = (pkg / "service.py").read_text(encoding="utf-8")
|
|
10593
|
+
c5 = {
|
|
10594
|
+
"api": s_g == 200 and gj["settings"]["at"] == "11:00" and s_w == 200 and wj["report_id"].startswith("DAY-") and s_so == 403 and s_sa == 200 and sj["at"] == "12:30"
|
|
10595
|
+
and s_sb == 400 and s_ro == 403 and s_re == 400 and s_ra == 200 and raj["signed_by_old"] and s_dz == 200 and len(dzj["key_history"]["rotations"]) == 3
|
|
10596
|
+
and s_pdf == 200 and pdj.get("_ct") == "application/pdf" and pdj["_len"] > 1000,
|
|
10597
|
+
"cli": cli_ps.returncode == 0 and "printable:" in cli_ps.stdout and cli_kh.returncode == 0 and "CURRENT" in cli_kh.stdout and "retired" in cli_kh.stdout
|
|
10598
|
+
and cli_rk.returncode != 0 and "say why" in cli_rk.stderr,
|
|
10599
|
+
"poller": "_PS.tick(self)" in service_src,
|
|
10600
|
+
"page": all(x_ in page_src for x_ in ("psCard", "Daily pad summary", "/api/pad-summary", "dzRot", "/api/dossier/rotate", "Key history", "Distances from")),
|
|
10601
|
+
"help": all(any(t[0] == k for t in H.INDEX) and not H.check(k) for k in ("pad-summary", "dossier", "opseis", "report-history"))
|
|
10602
|
+
and "Changing the key" in (pkg / "help" / "dossier.md").read_text(encoding="utf-8") and "lateral" in (pkg / "help" / "opseis.md").read_text(encoding="utf-8")
|
|
10603
|
+
and "printable copy" in (pkg / "help" / "report-history.md").read_text(encoding="utf-8"),
|
|
10604
|
+
}
|
|
10605
|
+
failed5 = [k for k, v in c5.items() if not v]
|
|
10606
|
+
ok(not failed5,
|
|
10607
|
+
"CN5 on the API (anyone signed in reads the summary's settings; an operator writes one; only an administrator sets its hour or "
|
|
10608
|
+
"rotates the key, with a reason; the PDF served as a PDF), the CLI (the summary written, the key history, a rotation without a "
|
|
10609
|
+
"reason refused), the service's poller writing the summary, the cards on the Reports tab and the help pages"
|
|
10610
|
+
+ (f" [failed: {', '.join(failed5)}]" if failed5 else ""))
|
|
10611
|
+
|
|
10612
|
+
|
|
10334
10613
|
def main() -> int:
|
|
10335
10614
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
10336
10615
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -10425,6 +10704,7 @@ def main() -> int:
|
|
|
10425
10704
|
section_ck_operations_seismicity(tmp)
|
|
10426
10705
|
section_cl_history_and_dossier(tmp)
|
|
10427
10706
|
section_cm_watch_and_alerts(tmp)
|
|
10707
|
+
section_cn_four(tmp)
|
|
10428
10708
|
if _FAILS:
|
|
10429
10709
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
10430
10710
|
for f in _FAILS:
|
|
@@ -1889,8 +1889,11 @@ def guard_text(text: str, what: str = 'a client deliverable') -> str:
|
|
|
1889
1889
|
return text
|
|
1890
1890
|
|
|
1891
1891
|
|
|
1892
|
+
LAST_PDF_ERROR: Optional[str] = None
|
|
1893
|
+
|
|
1894
|
+
|
|
1892
1895
|
def write(doc: Document, out_dir: str, basename: str = 'gauge_drift_report') -> dict:
|
|
1893
|
-
"""Write markdown, HTML, the machine JSON, the records CSV and the tag
|
|
1896
|
+
"""Write markdown, HTML, the printable PDF (`pdf_report`), the machine JSON, the records CSV and the tag
|
|
1894
1897
|
catalogue CSV. Returns the paths. Raises if the rendered report contains
|
|
1895
1898
|
any internal-register term."""
|
|
1896
1899
|
os.makedirs(out_dir, exist_ok=True)
|
|
@@ -1908,6 +1911,17 @@ def write(doc: Document, out_dir: str, basename: str = 'gauge_drift_report') ->
|
|
|
1908
1911
|
f.write(md)
|
|
1909
1912
|
with open(paths['html'], 'w', encoding='utf-8', newline='') as f:
|
|
1910
1913
|
f.write(ht)
|
|
1914
|
+
global LAST_PDF_ERROR
|
|
1915
|
+
try: # the printable copy, from the same document
|
|
1916
|
+
from . import pdf_report as _PDF
|
|
1917
|
+
pdf = _PDF.render(doc)
|
|
1918
|
+
paths['pdf'] = os.path.join(out_dir, basename + '.pdf')
|
|
1919
|
+
with open(paths['pdf'], 'wb') as f:
|
|
1920
|
+
f.write(pdf)
|
|
1921
|
+
LAST_PDF_ERROR = None
|
|
1922
|
+
except Exception as e: # a layout fault costs the printable copy, never the report
|
|
1923
|
+
paths.pop('pdf', None)
|
|
1924
|
+
LAST_PDF_ERROR = f'{basename}: {type(e).__name__}: {e}'
|
|
1911
1925
|
for s in doc.sections:
|
|
1912
1926
|
for fg in s.figures:
|
|
1913
1927
|
fp = os.path.join(out_dir, fg.name + '.svg')
|