gea-program 0.18.0__tar.gz → 0.20.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.18.0 → gea_program-0.20.0}/PKG-INFO +28 -2
- {gea_program-0.18.0 → gea_program-0.20.0}/README.md +27 -1
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/__init__.py +1 -1
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/__main__.py +30 -2
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/acceptance_tests.py +236 -10
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/blind_harness.py +1 -1
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/client_reports.py +9 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/correlation.py +1 -1
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/TESTER_GUIDE.md +2 -2
- gea_program-0.20.0/gea/help/month-end.md +11 -0
- gea_program-0.20.0/gea/help/soak.md +11 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/helplib.py +2 -0
- gea_program-0.20.0/gea/month_end.py +290 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/service.py +32 -1
- gea_program-0.20.0/gea/soak.py +403 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/survey_cmd.py +12 -10
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/web/app.html +8 -2
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/wits0.py +45 -27
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/workspace.py +8 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/PKG-INFO +28 -2
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/SOURCES.txt +4 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/pyproject.toml +1 -1
- {gea_program-0.18.0 → gea_program-0.20.0}/LICENSE +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/backup.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/bench.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/certificates.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/cli.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/config_versioning.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/conformance.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/dashboard.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/deviation.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/doctor.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/earth_model.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/etp.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/example_register_map.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/fat_sat.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/files.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/follower.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/forward_model.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/gamma.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/geodesy.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/alarms.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/backup.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/conformance.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/data-in.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/doctor.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/drift.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/etp.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/files.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/instruments.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/notifications.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/osdu.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/patches.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/quality.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/seismic.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/site.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/sites.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/sra.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/start.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/transients.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/vibration.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/housekeeping.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/jobs.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/live_ports.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/loadtest.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/modbus.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/model_card.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/notify.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/opcua_port.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/operator_app.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/osdu.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/patches.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/permits.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/ports.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/ppdm.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/project.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/quakeml.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reconciler.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sample_record.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sbom.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seedlink.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/segy.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_array.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_film.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_response.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_track.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/service_life.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/shell.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/shut_in.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sla_report.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/sra.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/store_forward.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/strata_join.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/supervisor.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/survey_view.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/telemetry.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/tool_library.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/transient.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/vibration.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/well_assembler.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea/witsml.py +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.18.0 → gea_program-0.20.0}/setup.cfg +0 -0
- {gea_program-0.18.0 → gea_program-0.20.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.20.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
|
|
@@ -125,7 +125,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
125
125
|
gea client-report --report accuracy --out client_report
|
|
126
126
|
gea model-cards --out model_cards
|
|
127
127
|
gea sbom --out sbom
|
|
128
|
-
gea accept # the product gate (
|
|
128
|
+
gea accept # the product gate (347 checks)
|
|
129
129
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
130
130
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
131
131
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -710,6 +710,24 @@ after the report was written - and "Update every report from its source"
|
|
|
710
710
|
dashboard for every well, then every seismic station, errors collected and
|
|
711
711
|
listed, never hidden.
|
|
712
712
|
|
|
713
|
+
### The month-end packet
|
|
714
|
+
|
|
715
|
+
A client's month was five or six commands and a walk through the reports
|
|
716
|
+
folder. `gea workspace --path C:\site --action month-end --period 2026-09`
|
|
717
|
+
(or `--period previous`) runs the refresh for the period and collects every
|
|
718
|
+
report the program writes - each well's drift report, alarm month, well-test
|
|
719
|
+
validation, shut-in report, data resilience, machine vibration and the
|
|
720
|
+
certificate register's ISO/IEC 17025 7.8 conformity; each site's SRA packet
|
|
721
|
+
for the period and site report; every seismic station and track; the
|
|
722
|
+
accuracy statement, the SLA month, the FAT/SAT protocol, the model cards and
|
|
723
|
+
the SBOM - into `reports/month_end/<period>/` with `INDEX` (html, md, json:
|
|
724
|
+
every item INCLUDED with its files or NOT AVAILABLE with the reason that
|
|
725
|
+
names what the site lacks; every failed step; OPEN for a month that has not
|
|
726
|
+
ended) and `MANIFEST.json` (every file's SHA-256), zipped beside it. Nothing
|
|
727
|
+
is recomputed in the index. On the Reports page: the packets on record,
|
|
728
|
+
Assemble as a job, and Schedule monthly (day and hour, the previous month).
|
|
729
|
+
`gea help month-end`.
|
|
730
|
+
|
|
713
731
|
### Before a site goes live
|
|
714
732
|
|
|
715
733
|
`deploy/README.md` is the checklist: keep the service on the loopback
|
|
@@ -722,6 +740,14 @@ headers on every response, and `gea housekeeping --workspace C:\site --apply`
|
|
|
722
740
|
than truncating them and prunes finished job folders and old recordings. `gea
|
|
723
741
|
loadtest --patches 16 --seconds 60 --with-service` tells you whether the site
|
|
724
742
|
machine carries the patches it will be given, with the page still answering.
|
|
743
|
+
`gea soak --hours 72 --patches 4 --with-service --out C:\site\reports\soak`
|
|
744
|
+
runs the site's patch count, an ETP patch and a SeedLink station through
|
|
745
|
+
link relays that are cut on a schedule, and writes the Soak Report from the
|
|
746
|
+
supervisor's own numbers: seconds to reconnect per source after every
|
|
747
|
+
outage, what each protocol lost while the link was down (WITS0 has no
|
|
748
|
+
replay; SeedLink resumes by sequence number, so its gap count must be
|
|
749
|
+
zero), and the page's answer time through it; Verification runs a 15-minute
|
|
750
|
+
one from the page. `gea help soak`.
|
|
725
751
|
`gea backup --workspace C:\site --out D:\gea-backups --keep 14` copies the
|
|
726
752
|
whole site into one dated archive with a manifest of every file's hash, onto
|
|
727
753
|
another disk or a share; schedule it daily from Audit / Update, verify an
|
|
@@ -86,7 +86,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
86
86
|
gea client-report --report accuracy --out client_report
|
|
87
87
|
gea model-cards --out model_cards
|
|
88
88
|
gea sbom --out sbom
|
|
89
|
-
gea accept # the product gate (
|
|
89
|
+
gea accept # the product gate (347 checks)
|
|
90
90
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
91
91
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
92
92
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -671,6 +671,24 @@ after the report was written - and "Update every report from its source"
|
|
|
671
671
|
dashboard for every well, then every seismic station, errors collected and
|
|
672
672
|
listed, never hidden.
|
|
673
673
|
|
|
674
|
+
### The month-end packet
|
|
675
|
+
|
|
676
|
+
A client's month was five or six commands and a walk through the reports
|
|
677
|
+
folder. `gea workspace --path C:\site --action month-end --period 2026-09`
|
|
678
|
+
(or `--period previous`) runs the refresh for the period and collects every
|
|
679
|
+
report the program writes - each well's drift report, alarm month, well-test
|
|
680
|
+
validation, shut-in report, data resilience, machine vibration and the
|
|
681
|
+
certificate register's ISO/IEC 17025 7.8 conformity; each site's SRA packet
|
|
682
|
+
for the period and site report; every seismic station and track; the
|
|
683
|
+
accuracy statement, the SLA month, the FAT/SAT protocol, the model cards and
|
|
684
|
+
the SBOM - into `reports/month_end/<period>/` with `INDEX` (html, md, json:
|
|
685
|
+
every item INCLUDED with its files or NOT AVAILABLE with the reason that
|
|
686
|
+
names what the site lacks; every failed step; OPEN for a month that has not
|
|
687
|
+
ended) and `MANIFEST.json` (every file's SHA-256), zipped beside it. Nothing
|
|
688
|
+
is recomputed in the index. On the Reports page: the packets on record,
|
|
689
|
+
Assemble as a job, and Schedule monthly (day and hour, the previous month).
|
|
690
|
+
`gea help month-end`.
|
|
691
|
+
|
|
674
692
|
### Before a site goes live
|
|
675
693
|
|
|
676
694
|
`deploy/README.md` is the checklist: keep the service on the loopback
|
|
@@ -683,6 +701,14 @@ headers on every response, and `gea housekeeping --workspace C:\site --apply`
|
|
|
683
701
|
than truncating them and prunes finished job folders and old recordings. `gea
|
|
684
702
|
loadtest --patches 16 --seconds 60 --with-service` tells you whether the site
|
|
685
703
|
machine carries the patches it will be given, with the page still answering.
|
|
704
|
+
`gea soak --hours 72 --patches 4 --with-service --out C:\site\reports\soak`
|
|
705
|
+
runs the site's patch count, an ETP patch and a SeedLink station through
|
|
706
|
+
link relays that are cut on a schedule, and writes the Soak Report from the
|
|
707
|
+
supervisor's own numbers: seconds to reconnect per source after every
|
|
708
|
+
outage, what each protocol lost while the link was down (WITS0 has no
|
|
709
|
+
replay; SeedLink resumes by sequence number, so its gap count must be
|
|
710
|
+
zero), and the page's answer time through it; Verification runs a 15-minute
|
|
711
|
+
one from the page. `gea help soak`.
|
|
686
712
|
`gea backup --workspace C:\site --out D:\gea-backups --keep 14` copies the
|
|
687
713
|
whole site into one dated archive with a manifest of every file's hash, onto
|
|
688
714
|
another disk or a share; schedule it daily from Audit / Update, verify an
|
|
@@ -285,7 +285,7 @@ def main(argv=None) -> int:
|
|
|
285
285
|
"add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
|
|
286
286
|
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
|
|
287
287
|
"set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity", "vibration-report",
|
|
288
|
-
"add-live-station", "live-fold", "live-run"])
|
|
288
|
+
"add-live-station", "live-fold", "live-run", "month-end"])
|
|
289
289
|
p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
|
|
290
290
|
p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
|
|
291
291
|
p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
|
|
@@ -307,7 +307,7 @@ def main(argv=None) -> int:
|
|
|
307
307
|
p_ws.add_argument("--checkpoint-months", type=int, default=3, help="sra-define: months between checkpoints with Commission staff (3)")
|
|
308
308
|
p_ws.add_argument("--formation-boundary", type=str, default="base of the Wolfcamp", help="sra-define: the stratigraphic boundary between the two tiers")
|
|
309
309
|
p_ws.add_argument("--triggering-event", type=str, default="", help="sra-define: the catalogue id of the event the plan answers")
|
|
310
|
-
p_ws.add_argument("--catalog", type=str, default=None, help="sra-report: a TexNet earthquake-catalogue export (CSV)")
|
|
310
|
+
p_ws.add_argument("--catalog", type=str, default=None, help="sra-report / month-end: a TexNet earthquake-catalogue export (CSV)")
|
|
311
311
|
p_ws.add_argument("--aftershocks", type=str, nargs="*", default=None, help="sra-report: catalogue ids the operator declares as aftershocks (exempt)")
|
|
312
312
|
p_ws.add_argument("--start", type=str, default=None, help="sra-report: first day of the record, YYYY-MM-DD")
|
|
313
313
|
p_ws.add_argument("--end", type=str, default=None, help="sra-report: last day of the record, YYYY-MM-DD")
|
|
@@ -359,6 +359,8 @@ def main(argv=None) -> int:
|
|
|
359
359
|
p_ws.add_argument("--seedlink-config", type=str, default=None, help="add-live-station: the SeedLink config JSON (gea seedlink --write-example-config)")
|
|
360
360
|
p_ws.add_argument("--include-open", action="store_true", help="live-fold: fold the current day's files too (they are still being written)")
|
|
361
361
|
p_ws.add_argument("--seconds", type=float, default=None, help="live-run: how long to run (default: until Ctrl+C)")
|
|
362
|
+
p_ws.add_argument("--period", type=str, default="previous", help="month-end: the month, YYYY-MM, or 'previous' (the last closed month)")
|
|
363
|
+
p_ws.add_argument("--no-refresh", action="store_true", help="month-end: collect the reports as they stand without refreshing first")
|
|
362
364
|
p_ws.add_argument("--name", type=str, default=None, help="init: site name; add-*: display name")
|
|
363
365
|
p_ws.add_argument("--file", type=str, default=None, help="add-file: the client's data file; add-live: the port configuration JSON")
|
|
364
366
|
p_ws.add_argument("--entry", type=str, default=None, help="add-catalog: catalogue entry")
|
|
@@ -390,6 +392,19 @@ def main(argv=None) -> int:
|
|
|
390
392
|
p_hk.add_argument("--actor", type=str, default="housekeeping")
|
|
391
393
|
p_hk.add_argument("--json", action="store_true")
|
|
392
394
|
|
|
395
|
+
p_sk = sub.add_parser("soak", help="the patches through link outages for hours, measured: N WITS0 patches, an ETP patch and a SeedLink station through relays that are cut on a schedule; the Soak Report")
|
|
396
|
+
p_sk.add_argument("--hours", type=float, default=None, help="how long (72 for the go-live soak); or --seconds")
|
|
397
|
+
p_sk.add_argument("--seconds", type=float, default=None)
|
|
398
|
+
p_sk.add_argument("--patches", type=int, default=2, help="WITS0 patches (the site's own count)")
|
|
399
|
+
p_sk.add_argument("--no-etp", action="store_true"); p_sk.add_argument("--no-seedlink", action="store_true")
|
|
400
|
+
p_sk.add_argument("--outage-every", type=float, default=600.0, help="seconds between link outages")
|
|
401
|
+
p_sk.add_argument("--outage-len", type=float, default=60.0, help="seconds each outage lasts")
|
|
402
|
+
p_sk.add_argument("--interval", type=float, default=1.0, help="simulator frame interval (s)")
|
|
403
|
+
p_sk.add_argument("--reconnect-budget", type=float, default=90.0, help="seconds a source may take to be back and receiving after the link returns")
|
|
404
|
+
p_sk.add_argument("--with-service", action="store_true", help="also serve the page and time its answers through the outages")
|
|
405
|
+
p_sk.add_argument("--out", type=str, default=None, help="where soak.json and the Soak Report go (default: the throw-away workspace's reports/soak)")
|
|
406
|
+
p_sk.add_argument("--keep", action="store_true", help="keep the throw-away workspace")
|
|
407
|
+
p_sk.add_argument("--json", action="store_true")
|
|
393
408
|
p_lt = sub.add_parser("loadtest", help="how many supervised patches this machine carries: N WITS0 simulators and patches for a while, with the service answering")
|
|
394
409
|
p_lt.add_argument("--patches", type=int, default=8)
|
|
395
410
|
p_lt.add_argument("--seconds", type=float, default=30.0)
|
|
@@ -913,6 +928,15 @@ def main(argv=None) -> int:
|
|
|
913
928
|
for x in r['records_removed'][:20]:
|
|
914
929
|
print(f" record {x['well_id']}/{x['file']} {x['age_days']} d")
|
|
915
930
|
return 0
|
|
931
|
+
elif a.cmd == "soak":
|
|
932
|
+
import json as _json
|
|
933
|
+
from .soak import run as _soak
|
|
934
|
+
secs = a.seconds if a.seconds is not None else (a.hours * 3600.0 if a.hours is not None else 600.0)
|
|
935
|
+
r = _soak(seconds=secs, patches=a.patches, etp=not a.no_etp, seedlink=not a.no_seedlink, outage_every_s=a.outage_every, outage_s=a.outage_len,
|
|
936
|
+
interval_s=a.interval, with_service=a.with_service, keep=a.keep, out_dir=a.out, reconnect_budget_s=a.reconnect_budget, verbose=not a.json)
|
|
937
|
+
if a.json:
|
|
938
|
+
print(_json.dumps({k: v for k, v in r.items() if k != "samples_timeline"}, indent=1, default=str))
|
|
939
|
+
return 0 if r["ok"] else 1
|
|
916
940
|
elif a.cmd == "loadtest":
|
|
917
941
|
import json as _json
|
|
918
942
|
from .loadtest import run as _lt
|
|
@@ -1944,6 +1968,10 @@ def main(argv=None) -> int:
|
|
|
1944
1968
|
raise SystemExit("well-identity needs --well <id>")
|
|
1945
1969
|
r = ws.well_identity(a.well)
|
|
1946
1970
|
print(_ppdm_text(r)); return 0
|
|
1971
|
+
elif a.action == "month-end":
|
|
1972
|
+
from .month_end import report_text as _me_text
|
|
1973
|
+
r = ws.month_end(a.period, actor=a.actor, refresh=not a.no_refresh, catalog_csv=a.catalog)
|
|
1974
|
+
print(_me_text(r)); return 0 if not r["errors"] else 1
|
|
1947
1975
|
elif a.action == "add-live-station":
|
|
1948
1976
|
if not a.seedlink_config or not a.name:
|
|
1949
1977
|
raise SystemExit("add-live-station needs --name <display> and --seedlink-config <json>")
|
|
@@ -646,7 +646,7 @@ def section_s_correlation() -> None:
|
|
|
646
646
|
and all("continuity" in r["continuity_claim"].lower()
|
|
647
647
|
or r["continuity_claim"] == "TWIN_HOLE_ELIGIBLE"
|
|
648
648
|
for r in df["refused"] + df["ok"])
|
|
649
|
-
and "
|
|
649
|
+
and "census as it stands" in df["finding"],
|
|
650
650
|
"S2 correlation: every cross-site pair carries a continuity claim "
|
|
651
651
|
"bounded by distance, and thin pairs refuse with counts")
|
|
652
652
|
|
|
@@ -767,26 +767,27 @@ def section_y_survey() -> None:
|
|
|
767
767
|
"""Section Y - the one-command user path: gea survey."""
|
|
768
768
|
from .survey_cmd import run_survey
|
|
769
769
|
txt, d = run_survey(demo=True)
|
|
770
|
-
|
|
771
|
-
|
|
770
|
+
from .client_reports import forbidden_terms
|
|
771
|
+
ok('one answer with its provenance' in txt and d['n_stations'] == 65
|
|
772
|
+
and d['exclusions']['washout_or_null_stations'] == 46 and not forbidden_terms(txt),
|
|
772
773
|
"Y1 survey demo: end-to-end on the bundled KTB excerpt - 65 "
|
|
773
|
-
"stations, 46 exclusions DISCLOSED, one readable report")
|
|
774
|
+
"stations, 46 exclusions DISCLOSED, one readable report that passes the same internal-register guard every client report passes")
|
|
774
775
|
ok('vp_m_s' in d and abs(d.get('vp_cross_check_pct', 99)) < 5.0,
|
|
775
776
|
"Y2 survey self-grading: the file carries its own sonic and the "
|
|
776
777
|
"estimate lands within 5 pct of measured (demo: ~+0.7 pct) - the "
|
|
777
778
|
"tool grades itself when the data allows")
|
|
778
|
-
ok('ASSUMPTION' in txt and '
|
|
779
|
-
"Y3 survey
|
|
780
|
-
"it acts and the
|
|
779
|
+
ok('ASSUMPTION' in txt and 'does not call a measurement' in txt,
|
|
780
|
+
"Y3 survey: the prior-family assumption is printed where "
|
|
781
|
+
"it acts and the what-this-tool-does-not-call-a-measurement section is always present")
|
|
781
782
|
import tempfile, os as _os
|
|
782
783
|
with tempfile.TemporaryDirectory() as td:
|
|
783
784
|
f = _os.path.join(td, 'empty.las')
|
|
784
785
|
open(f, 'w').write('~Version\n VERS. 2.0:\n~Well\n~Curve\n'
|
|
785
786
|
'DEPT.M : depth\n~ASCII\n1.0\n2.0\n')
|
|
786
787
|
txt2, d2 = run_survey(path=f)
|
|
787
|
-
ok(d2['refusals'] and '
|
|
788
|
-
"Y4 survey
|
|
789
|
-
"
|
|
788
|
+
ok(d2['refusals'] and 'NOT ESTIMATED' in txt2 and not forbidden_terms(txt2),
|
|
789
|
+
"Y4 survey: a LAS with no density and no gravity gets "
|
|
790
|
+
"NOT ESTIMATED naming the unlocking channel, not an "
|
|
790
791
|
"invented answer")
|
|
791
792
|
|
|
792
793
|
|
|
@@ -5872,6 +5873,229 @@ def section_bl_backup(tmp: str) -> None:
|
|
|
5872
5873
|
"`--verify` says OK or names the tampered file, `--restore` brings the accounts back; the help page says a backup never restored is a hope"
|
|
5873
5874
|
+ (f" [failed: {', '.join(failed2)}; r_st0={r_st0.stdout[-120:]!r} {r_st0.stderr[-120:]!r}]" if failed2 else ""))
|
|
5874
5875
|
|
|
5876
|
+
|
|
5877
|
+
def section_bm_month_end(tmp: str) -> None:
|
|
5878
|
+
"""Section BM - the month-end packet: the refresh for the period, every report collected with INCLUDED or NOT
|
|
5879
|
+
AVAILABLE and the reason, the index and the manifest, the archive, the records; the page, the job and the
|
|
5880
|
+
monthly schedule."""
|
|
5881
|
+
import http.cookiejar
|
|
5882
|
+
import subprocess as _sp
|
|
5883
|
+
import urllib.request
|
|
5884
|
+
import urllib.error
|
|
5885
|
+
import zipfile
|
|
5886
|
+
from . import month_end as ME
|
|
5887
|
+
from . import helplib as H
|
|
5888
|
+
from .telemetry import TelemetryRecorder, TelemetryConfig
|
|
5889
|
+
from .downhole_engine import DownholeEngine, SimulatorConfig
|
|
5890
|
+
from .workspace import Workspace
|
|
5891
|
+
from .service import Service, Users
|
|
5892
|
+
pkg = Path(__file__).parent
|
|
5893
|
+
d = Path(tmp, "bm"); d.mkdir()
|
|
5894
|
+
hist = str(d / "hist.csv")
|
|
5895
|
+
TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=2.0, seed=5)).run().export_csv(hist)
|
|
5896
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
5897
|
+
w = ws.add_well_file(hist, display="SWD 1", actor="tester")
|
|
5898
|
+
site = ws.add_site("Pad 3 site", wells=[w["id"]], actor="tester")
|
|
5899
|
+
bad_period = None
|
|
5900
|
+
try:
|
|
5901
|
+
ME.period_bounds("2026-9")
|
|
5902
|
+
except ValueError as e:
|
|
5903
|
+
bad_period = str(e)
|
|
5904
|
+
pb = ME.period_bounds("2026-02")
|
|
5905
|
+
rec = ME.assemble(ws, "2026-09", actor="tester")
|
|
5906
|
+
idx = json.loads((Path(rec["folder"]) / "INDEX.json").read_text(encoding="utf-8"))
|
|
5907
|
+
man = json.loads((Path(rec["folder"]) / "MANIFEST.json").read_text(encoding="utf-8"))
|
|
5908
|
+
by_name = {i["name"]: i for i in idx["items"]}
|
|
5909
|
+
v_folder = ME.verify(rec["folder"]); v_zip = ME.verify(rec["archive"])
|
|
5910
|
+
with zipfile.ZipFile(rec["archive"]) as z:
|
|
5911
|
+
znames = set(z.namelist())
|
|
5912
|
+
# a second assembly of the same period rebuilds whole; a future month is OPEN
|
|
5913
|
+
rec2 = ME.assemble(ws, "2026-09", actor="tester", refresh=False)
|
|
5914
|
+
from datetime import datetime as _dt, timezone as _tz
|
|
5915
|
+
now = _dt.now(_tz.utc)
|
|
5916
|
+
cur = f"{now.year:04d}-{now.month:02d}"
|
|
5917
|
+
rec3 = ME.assemble(ws, cur, actor="tester", refresh=False)
|
|
5918
|
+
pk = ME.packets(ws)
|
|
5919
|
+
audit = [a["action"] for a in ws.audit_log()]
|
|
5920
|
+
c1 = {
|
|
5921
|
+
"period_checked": bool(bad_period) and "YYYY-MM" in bad_period and pb["days"] == 28 and pb["start"] == "2026-02-01T00:00:00Z" and pb["end"] == "2026-02-28T23:59:59Z" and pb["closed"] is True,
|
|
5922
|
+
"included": by_name["SWD 1: gauge drift report"]["status"] == "INCLUDED" and by_name["SWD 1: alarm event report"]["status"] == "INCLUDED"
|
|
5923
|
+
and by_name["SLA month 2026-09"]["status"] == "INCLUDED" and by_name["Accuracy statement"]["status"] == "INCLUDED"
|
|
5924
|
+
and by_name["Pad 3 site: site report"]["status"] == "INCLUDED" and by_name["Model cards and SBOM"]["status"] == "INCLUDED",
|
|
5925
|
+
"not_available_with_reason": by_name["SWD 1: machine vibration"]["status"] == "NOT AVAILABLE" and "no machine record" in by_name["SWD 1: machine vibration"]["reason"]
|
|
5926
|
+
and by_name["Pad 3 site: SRA packet for 2026-09"]["status"] == "NOT AVAILABLE" and "no Seismicity Response Area" in by_name["Pad 3 site: SRA packet for 2026-09"]["reason"]
|
|
5927
|
+
and "no certificate register" in by_name["SWD 1: certificate conformity (ISO/IEC 17025 7.8)"]["reason"],
|
|
5928
|
+
"files_hashed": all(f["sha256"] and f["bytes"] > 0 for i in idx["items"] for f in i["files"]) and man["n_files"] == len(man["files"]) and man["n_files"] >= 20
|
|
5929
|
+
and "wells/SWD-1/gauge_drift_report.html" in man["files"] and "site/sla_report_2026-09.html" in man["files"] and "INDEX.html" in man["files"],
|
|
5930
|
+
"record": rec["included"] == len([i for i in idx["items"] if i["status"] == "INCLUDED"]) and rec["closed"] is True and rec["errors"] == [] and os.path.isfile(rec["archive"])
|
|
5931
|
+
and os.path.isfile(rec["archive"] + "") and rec["archive"].endswith("month_end_Pad-3_2026-09.zip"),
|
|
5932
|
+
"verify": v_folder["status"] == "OK" and v_zip["status"] == "OK" and v_zip["n_files"] == man["n_files"] and "MANIFEST.json" in znames and "INDEX.html" in znames,
|
|
5933
|
+
"rebuilt_whole_and_open": rec2["generated_utc"] >= rec["generated_utc"] and rec2["refresh"] is False and rec3["closed"] is False and "OPEN" in (Path(rec3["folder"]) / "INDEX.md").read_text(encoding="utf-8")
|
|
5934
|
+
and len(pk) == 3 and all(p["present"] for p in pk) and pk[-1]["period"] == cur,
|
|
5935
|
+
"audited": audit.count("month_end.assemble") == 3 and "dashboard.refresh" in audit and "site.report" in audit,
|
|
5936
|
+
"not_a_measurement": "does not call a measurement" in (Path(rec["folder"]) / "INDEX.md").read_text(encoding="utf-8"),
|
|
5937
|
+
}
|
|
5938
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
5939
|
+
ok(not failed1,
|
|
5940
|
+
"BM1 the month-end packet: the period checked (YYYY-MM; February's 28 days; a past month closed), the refresh run for it, every report "
|
|
5941
|
+
"collected - the well's drift and alarm reports, the SLA month, the accuracy statement, the site report, the model cards - INCLUDED with "
|
|
5942
|
+
"hashed files, and the vibration report, the SRA packet and the certificate conformity NOT AVAILABLE with the reason that names what the "
|
|
5943
|
+
"site lacks; INDEX and MANIFEST written, the archive verifies against the manifest, a second assembly rebuilds the period whole, the "
|
|
5944
|
+
"current month is marked OPEN, every packet on record, every assembly in the audit"
|
|
5945
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
5946
|
+
|
|
5947
|
+
# -- the page, the job, the schedule, the CLI, the help --------------------------------------------------------
|
|
5948
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator"); users.add("adm", "admin-pass-1", "admin")
|
|
5949
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
5950
|
+
base = svc.url.rstrip("/")
|
|
5951
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
5952
|
+
|
|
5953
|
+
def call(method, path, body=None):
|
|
5954
|
+
req = urllib.request.Request(base + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
5955
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
5956
|
+
try:
|
|
5957
|
+
with opener.open(req, timeout=300) as rr_:
|
|
5958
|
+
return rr_.status, json.loads(rr_.read())
|
|
5959
|
+
except urllib.error.HTTPError as e:
|
|
5960
|
+
return e.code, json.loads(e.read())
|
|
5961
|
+
try:
|
|
5962
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
5963
|
+
st_l, lst = call("GET", "/api/month-end")
|
|
5964
|
+
st_bad = call("POST", "/api/month-end/run", {"period": "Sept 2026"})[0]
|
|
5965
|
+
st_sched_op = call("POST", "/api/month-end/schedule", {"day": 1, "at": "03:00"})[0]
|
|
5966
|
+
st_run, job = call("POST", "/api/month-end/run", {"period": "2026-08", "no_refresh": True})
|
|
5967
|
+
done = svc.app.runner.wait(job["id"], 300)
|
|
5968
|
+
st_l2, lst2 = call("GET", "/api/month-end")
|
|
5969
|
+
req = urllib.request.Request(base + "/reports/month_end/2026-08/INDEX.html"); req.add_header("X-GEA-Action", "1")
|
|
5970
|
+
with opener.open(req, timeout=60) as rr_:
|
|
5971
|
+
html_ok = rr_.status == 200 and b"Month-End Packet" in rr_.read()
|
|
5972
|
+
call("POST", "/api/login", {"name": "adm", "password": "admin-pass-1"})
|
|
5973
|
+
st_sched_bad = call("POST", "/api/month-end/schedule", {"day": 31, "at": "03:00"})[0]
|
|
5974
|
+
st_sched, sch = call("POST", "/api/month-end/schedule", {"day": 2, "at": "03:15"})
|
|
5975
|
+
st_l3, lst3 = call("GET", "/api/month-end")
|
|
5976
|
+
finally:
|
|
5977
|
+
svc.stop()
|
|
5978
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
5979
|
+
help_md = (pkg / "help" / "month-end.md").read_text(encoding="utf-8")
|
|
5980
|
+
r_cli = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "month-end", "--period", "2026-07", "--no-refresh"], capture_output=True, text=True)
|
|
5981
|
+
r_prev = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "month-end", "--no-refresh"], capture_output=True, text=True)
|
|
5982
|
+
c2 = {
|
|
5983
|
+
"list": st_l == 200 and len(lst["packets"]) == 3 and lst["previous"] == ME.previous_period() and lst["schedule"] is None,
|
|
5984
|
+
"refusals": st_bad == 400 and st_sched_op == 403 and st_sched_bad == 400,
|
|
5985
|
+
"job": st_run == 200 and done["status"] == "DONE" and st_l2 == 200 and any(p["period"] == "2026-08" and p["present"] for p in lst2["packets"]) and html_ok,
|
|
5986
|
+
"schedule": st_sched == 200 and sch["name"] == "month-end-monthly" and sch["monthly_day"] == 2 and sch["daily_at"] == "03:15" and "previous" in sch["args"]
|
|
5987
|
+
and st_l3 == 200 and lst3["schedule"]["name"] == "month-end-monthly",
|
|
5988
|
+
"page": "Month-end packet" in page_src and "/api/month-end/run" in page_src and "/api/month-end/schedule" in page_src,
|
|
5989
|
+
"cli": r_cli.returncode == 0 and "month-end 2026-07 (closed)" in r_cli.stdout and r_prev.returncode == 0 and f"month-end {ME.previous_period()} (closed)" in r_prev.stdout,
|
|
5990
|
+
"help": any(t["topic"] == "month-end" for t in H.topics()) and not H.check("month-end") and "does not recompute" in help_md.split("will not call a measurement")[1],
|
|
5991
|
+
}
|
|
5992
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
5993
|
+
ok(not failed2,
|
|
5994
|
+
"BM2 the Reports page carries the packet card: the packets on record with their index and archive, the period defaulting to the last closed "
|
|
5995
|
+
"month, Assemble as a job (a malformed period refused) whose INDEX the page then serves, Schedule monthly for an administrator only (day 1..28) "
|
|
5996
|
+
"as the `month-end-monthly` entry for the previous month; `gea workspace --action month-end` with a period or with 'previous'; the help page "
|
|
5997
|
+
"says the index collects and does not recompute"
|
|
5998
|
+
+ (f" [failed: {', '.join(failed2)}; cli={r_cli.stdout[-200:]!r} {r_cli.stderr[-200:]!r}]" if failed2 else ""))
|
|
5999
|
+
|
|
6000
|
+
|
|
6001
|
+
def section_bn_soak(tmp: str) -> None:
|
|
6002
|
+
"""Section BN - the soak: the link relay cuts and restores a connection; the patches, the ETP store and the
|
|
6003
|
+
SeedLink station through relays under scheduled outages, every one back and receiving, SeedLink without a gap,
|
|
6004
|
+
the page answering; the Soak Report; the CLI, the page row, the guard on plain-text deliverables."""
|
|
6005
|
+
import socket as _socket
|
|
6006
|
+
import subprocess as _sp
|
|
6007
|
+
from . import soak as SK
|
|
6008
|
+
from . import helplib as H
|
|
6009
|
+
from .client_reports import forbidden_terms, guard_text
|
|
6010
|
+
pkg = Path(__file__).parent
|
|
6011
|
+
d = Path(tmp, "bn"); d.mkdir()
|
|
6012
|
+
|
|
6013
|
+
# -- the relay alone: bytes through, a cut severs, a restore accepts again on the same port -------------------------
|
|
6014
|
+
echo = _socket.socket(); echo.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1); echo.bind(("127.0.0.1", 0)); echo.listen(4); echo.settimeout(5)
|
|
6015
|
+
eport = echo.getsockname()[1]
|
|
6016
|
+
|
|
6017
|
+
def echo_once():
|
|
6018
|
+
try:
|
|
6019
|
+
c, _ = echo.accept(); c.settimeout(5)
|
|
6020
|
+
while True:
|
|
6021
|
+
b = c.recv(4096)
|
|
6022
|
+
if not b:
|
|
6023
|
+
break
|
|
6024
|
+
c.sendall(b.upper())
|
|
6025
|
+
except OSError:
|
|
6026
|
+
pass
|
|
6027
|
+
threading.Thread(target=echo_once, daemon=True).start()
|
|
6028
|
+
rl = SK.LinkRelay(("127.0.0.1", eport)); rl.start()
|
|
6029
|
+
cl = _socket.create_connection(("127.0.0.1", rl.port), timeout=5); cl.sendall(b"ping"); back = cl.recv(16)
|
|
6030
|
+
rl.cut()
|
|
6031
|
+
severed = False
|
|
6032
|
+
try:
|
|
6033
|
+
cl.settimeout(2); cl.sendall(b"x"); r_ = cl.recv(16); severed = r_ == b""
|
|
6034
|
+
except OSError:
|
|
6035
|
+
severed = True
|
|
6036
|
+
refused = False
|
|
6037
|
+
try:
|
|
6038
|
+
_socket.create_connection(("127.0.0.1", rl.port), timeout=1).close()
|
|
6039
|
+
except OSError:
|
|
6040
|
+
refused = True
|
|
6041
|
+
threading.Thread(target=echo_once, daemon=True).start()
|
|
6042
|
+
rl.restore(); time.sleep(0.3)
|
|
6043
|
+
cl2 = _socket.create_connection(("127.0.0.1", rl.port), timeout=5); cl2.sendall(b"again"); back2 = cl2.recv(16); cl2.close()
|
|
6044
|
+
rl.stop(); echo.close()
|
|
6045
|
+
ok(back == b"PING" and severed and refused and back2 == b"AGAIN" and rl.stats["cuts"] >= 1 and rl.stats["connections"] == 2 and rl.stats["bytes_to_target"] >= 9,
|
|
6046
|
+
"BN1 the link relay: bytes flow both ways to the target; a cut closes the listener and severs every carried connection (a client's next "
|
|
6047
|
+
"read sees the link gone and a new connection is refused); a restore listens again on the same port and carries a new connection")
|
|
6048
|
+
|
|
6049
|
+
# -- the soak itself, short: two WITS0 patches, the ETP store, the SeedLink station, two outages, the page ----------
|
|
6050
|
+
res = SK.run(seconds=75, patches=2, etp=True, seedlink=True, outage_every_s=22, outage_s=5, interval_s=0.5, with_service=True, out_dir=str(d / "out"),
|
|
6051
|
+
reconnect_budget_s=30, verbose=False)
|
|
6052
|
+
per = {p["protocol"]: p for p in res["per_source"]}
|
|
6053
|
+
rep_md = (d / "out" / "soak_report.md").read_text(encoding="utf-8") if (d / "out" / "soak_report.md").exists() else ""
|
|
6054
|
+
c = {
|
|
6055
|
+
"ran_two_outages": len(res["outages"]) >= 2 and all(o["connections_cut"] >= 4 for o in res["outages"]),
|
|
6056
|
+
"every_source_back": all(o["all_back"] for o in res["outages"]) and res["checks"]["every_outage_recovered"],
|
|
6057
|
+
"down_during": all(set(o["down_during"]) >= {"station"} or len(o["down_during"]) >= 1 for o in res["outages"]),
|
|
6058
|
+
"wits0_loss_counted": per["wits0"]["lost"] is not None and per["wits0"]["lost"] >= 1 and ("no replay" in per["wits0"]["resume"] or "gone" in per["wits0"]["resume"]),
|
|
6059
|
+
"etp_no_replay_named": per["etp"]["lost"] is None and "store" in per["etp"]["resume"] and per["etp"]["received"] > 0,
|
|
6060
|
+
"seedlink_resumed": per["seedlink"]["gaps"] == 0 and per["seedlink"]["lost"] == 0 and per["seedlink"]["reconnects"] >= 1 and "sequence" in per["seedlink"]["resume"],
|
|
6061
|
+
"all_connected_at_end": res["checks"]["all_connected_at_end"] and all(p["status"] == "CONNECTED" for p in res["per_source"]),
|
|
6062
|
+
"page_answered": res["service"]["probes_during_outage"] >= 2 and res["checks"]["page_answered_through_outages"],
|
|
6063
|
+
"verdict_ok": res["ok"] and "OK" in res["verdict"] and "no gap" in res["verdict"],
|
|
6064
|
+
"report_written": res.get("report") and os.path.isfile(res["report"]) and "Outages and recovery" in rep_md and "does not call a measurement" in rep_md
|
|
6065
|
+
and not forbidden_terms(rep_md) and os.path.isfile(d / "out" / "soak.json"),
|
|
6066
|
+
}
|
|
6067
|
+
failed = [k for k, v in c.items() if not v]
|
|
6068
|
+
ok(not failed,
|
|
6069
|
+
"BN2 the soak: two WITS0 patches, the ETP store and the SeedLink station through relays, the link cut twice on schedule - every source "
|
|
6070
|
+
"down during the cut and back, CONNECTED and receiving, within the budget after it; WITS0's loss counted as the protocol's own (no replay), "
|
|
6071
|
+
"the ETP store's new session named, SeedLink resumed by sequence number with no gap; the page answered through the outages; all connected "
|
|
6072
|
+
"at the end; the Soak Report written with the outage table and what it does not call a measurement, clean of internal-register terms"
|
|
6073
|
+
+ (f" [failed: {', '.join(failed)}; verdict={res['verdict']!r}; outages={json.dumps(res['outages'], default=str)[:600]}]" if failed else ""))
|
|
6074
|
+
|
|
6075
|
+
# -- the CLI, the page, the help, the guard on plain text -----------------------------------------------------------
|
|
6076
|
+
r_cli = _sp.run([sys.executable, "-m", "gea", "soak", "--seconds", "30", "--patches", "1", "--no-etp", "--no-seedlink", "--outage-every", "10", "--outage-len", "3", "--json"],
|
|
6077
|
+
capture_output=True, text=True)
|
|
6078
|
+
try:
|
|
6079
|
+
cli = json.loads(r_cli.stdout)
|
|
6080
|
+
except ValueError:
|
|
6081
|
+
cli = {}
|
|
6082
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
6083
|
+
help_md = (pkg / "help" / "soak.md").read_text(encoding="utf-8")
|
|
6084
|
+
guard_err = None
|
|
6085
|
+
try:
|
|
6086
|
+
guard_text("a line that calls itself honest", "a test text")
|
|
6087
|
+
except ValueError as e:
|
|
6088
|
+
guard_err = str(e)
|
|
6089
|
+
from .survey_cmd import run_survey
|
|
6090
|
+
txt, _ = run_survey(demo=True)
|
|
6091
|
+
ok(r_cli.returncode == 0 and cli.get("ok") is True and len(cli.get("outages", [])) >= 1 and cli["config"]["patches_wits0"] == 1 and cli["config"]["seedlink"] is False
|
|
6092
|
+
and "data-v=\"soak\"" in page_src and "Soak" in page_src and "172 checks" not in page_src
|
|
6093
|
+
and any(t["topic"] == "soak" for t in H.topics()) and not H.check("soak") and "declared, not observed" in help_md.split("will not call a measurement")[1]
|
|
6094
|
+
and guard_err and "internal-register" in guard_err and not forbidden_terms(txt) and "does not call a measurement" in txt,
|
|
6095
|
+
"BN3 `gea soak --json` runs the schedule and exits 0 on OK; the Verification page carries the soak row and button and no longer names a "
|
|
6096
|
+
"gate count typed by hand; `gea help soak`; `guard_text` is the client-report guard for plain text, and the strata survey's report passes it "
|
|
6097
|
+
"(its closing line and its NOT ESTIMATED section reworded)")
|
|
6098
|
+
|
|
5875
6099
|
def main() -> int:
|
|
5876
6100
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
5877
6101
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -5939,6 +6163,8 @@ def main() -> int:
|
|
|
5939
6163
|
section_bj_etp(tmp)
|
|
5940
6164
|
section_bk_seedlink(tmp)
|
|
5941
6165
|
section_bl_backup(tmp)
|
|
6166
|
+
section_bm_month_end(tmp)
|
|
6167
|
+
section_bn_soak(tmp)
|
|
5942
6168
|
if _FAILS:
|
|
5943
6169
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
5944
6170
|
for f in _FAILS:
|
|
@@ -103,6 +103,6 @@ def accuracy_report() -> Dict:
|
|
|
103
103
|
'worst_mae_pct': (ok[-1]['mae_pct'] if ok else None),
|
|
104
104
|
'median_coverage': (statistics.median(cov) if cov else None),
|
|
105
105
|
'method': ('leave-one-out over the same machinery clients get; '
|
|
106
|
-
'coverage targets ~0.68 (
|
|
106
|
+
'coverage targets ~0.68 (a true 1-sigma); refusals '
|
|
107
107
|
'listed, never hidden; regenerated live so the '
|
|
108
108
|
'accuracy table can never be a stale snapshot')}
|
|
@@ -1784,6 +1784,15 @@ def forbidden_terms(text: str) -> List[str]:
|
|
|
1784
1784
|
return [t for t in FORBIDDEN_TERMS if t in low]
|
|
1785
1785
|
|
|
1786
1786
|
|
|
1787
|
+
def guard_text(text: str, what: str = 'a client deliverable') -> str:
|
|
1788
|
+
"""The guard `write` applies to rendered reports, for any other text the program hands a client - a plain-text
|
|
1789
|
+
report, a printed summary: raise if an internal-register term is in it. Returns the text unchanged."""
|
|
1790
|
+
bad = forbidden_terms(text)
|
|
1791
|
+
if bad:
|
|
1792
|
+
raise ValueError(f'{what} contains internal-register terms: {sorted(set(bad))}')
|
|
1793
|
+
return text
|
|
1794
|
+
|
|
1795
|
+
|
|
1787
1796
|
def write(doc: Document, out_dir: str, basename: str = 'gauge_drift_report') -> dict:
|
|
1788
1797
|
"""Write markdown, HTML, the machine JSON, the records CSV and the tag
|
|
1789
1798
|
catalogue CSV. Returns the paths. Raises if the rendered report contains
|
|
@@ -151,5 +151,5 @@ def depth_frame_pairs(em: EarthModel = None, target_bins: int = TARGET_BINS,
|
|
|
151
151
|
'n_refused': len(refused),
|
|
152
152
|
'finding': ('the library currently supports %d cross-site '
|
|
153
153
|
'depth-frame correlations; %d candidate pairs '
|
|
154
|
-
'refused thin - the
|
|
154
|
+
'refused thin - the census as it stands, not a failure'
|
|
155
155
|
% (len(ok), len(refused)))}
|
|
@@ -57,7 +57,7 @@ BROWSER" below. `verify.cmd` runs the full test from the kit.
|
|
|
57
57
|
|
|
58
58
|
16. In a few seconds a report appears that starts with:
|
|
59
59
|
|
|
60
|
-
GEA SURVEY - one well, one
|
|
60
|
+
GEA SURVEY - one well, one answer with its provenance
|
|
61
61
|
|
|
62
62
|
## PART 5 — YOU'RE DONE. CHECK THESE THREE THINGS:
|
|
63
63
|
|
|
@@ -65,7 +65,7 @@ BROWSER" below. `verify.cmd` runs the full test from the kit.
|
|
|
65
65
|
scientific well, public archive).
|
|
66
66
|
18. Find the line starting with **CROSS-CHECK**. The program checks its own
|
|
67
67
|
answer against the well's real measurement — it should say about **+0.7%**.
|
|
68
|
-
19. Find the section **"what this tool
|
|
68
|
+
19. Find the section **"what this tool does not call a measurement."** That is on
|
|
69
69
|
purpose. This program tells you when it doesn't know something instead
|
|
70
70
|
of making it up.
|
|
71
71
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The month-end packet
|
|
2
|
+
|
|
3
|
+
> A client's month was five or six commands and a walk through the reports folder. The packet is that month assembled once: the refresh for the period, then every report the program writes for every well, site, station and track, collected into one dated folder and one archive, with an index that says item by item what is there and what is not, and a manifest of every file's hash.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea workspace --path C:\site --action month-end --period 2026-09` (or `--period previous`, the last closed month; `--no-refresh` collects the reports as they stand; `--catalog` hands the regulator's catalogue CSV to the SRA packet). On the dashboard: Reports -> Month-end packet -> the period and Assemble (a job), and for an administrator Schedule monthly (a schedule entry, `month-end-monthly`, on the day and hour given, for the previous month). The result is `reports/month_end/<period>/` with `INDEX.html` / `.md` / `.json` and `MANIFEST.json`, and `reports/month_end/month_end_<site>_<period>.zip`; each packet is logged in `records/month_end.jsonl` and in the audit (`month_end.assemble`).
|
|
6
|
+
|
|
7
|
+
**What it writes**: for the site - the dashboard index, the accuracy statement, the SLA month for that period, the FAT/SAT protocol, the model cards and the SBOM; for every well - the gauge drift report, the alarm event report, the well-test validation, the shut-in / build-up report, the data resilience report, the machine vibration report, and the certificate register's conformity against ISO/IEC 17025 7.8 as it stands; for every site - the SRA packet for the period (when a Seismicity Response Area is defined) and the site report; for every seismic station and track - their reports. Each item is INCLUDED with its files or NOT AVAILABLE with the reason (no machine record, no SRA defined, no certificate register, no well test detected, never refreshed); every step that failed is listed; a month that has not ended is marked OPEN.
|
|
8
|
+
|
|
9
|
+
**The number to check**: `N INCLUDED, M NOT AVAILABLE, 0 FAILED` on the index's front page, and `closed: yes` for a month that has ended.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: an item that is NOT AVAILABLE - the reason names what the site lacks, and the packet never stands in for it. A number in any of the reports - each carries its own method and declarations; the index collects and does not recompute. A month that has not ended - the packet says OPEN and the one assembled after the month closes supersedes it. A packet whose files no longer match its manifest - `MANIFEST.json` is the check, and a packet is rebuilt whole, never merged.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The soak
|
|
2
|
+
|
|
3
|
+
> The load test says whether the machine carries the site's patches with the page still answering. The soak says what happens to those patches when the link goes down and comes back, again and again, over hours - how long each protocol takes to reconnect, what it loses while the link is down, whether the resume leaves a gap, whether the page keeps answering - with the supervisor's own numbers, not a model's.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea soak --hours 72 --patches 4 --outage-every 600 --outage-len 60 --with-service --out C:\site\reports\soak` runs N WITS0 patches, an ETP patch and a SeedLink live station through link relays on this machine, cuts every relay for the declared seconds on the declared interval, and writes `soak.json` and the Soak Report into `--out`; `--patches` is the site's own patch count. On the dashboard, Verification -> Soak (15 min) runs a short one as a job at the site's patch count with outages of 20 s every 2 min; the 72-hour run is a command-line job for the machine that will serve the site. `gea help soak`.
|
|
6
|
+
|
|
7
|
+
**What it writes**: per outage - when the link was cut, for how long, how many connections it carried, which sources were down during it, and how many seconds after the link came back each source was CONNECTED and receiving again (or NOT BACK within the budget); per source - received, produced, lost (WITS0's frames sent while the link was down, which the protocol does not replay), gaps (SeedLink's, which must be zero: the resume by sequence number is the proof), reconnection attempts, latency p95, and how the protocol resumes; the page's answer time outside and during the outages when `--with-service`; the checks - all connected at the start and at the end, every outage recovered within the budget, no SeedLink gap, the page answering - and the verdict.
|
|
8
|
+
|
|
9
|
+
**The number to check**: `every source back within N s`, with N under the budget; `SeedLink resumed with no gap`; the page p95 during outages under a second; `OK`.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: the field link - these are relays on the loopback and the outage lengths were declared, not observed. A protocol's loss recovered - WITS0 and ETP frames sent while the link was down are gone by the protocol's nature; they are counted, never recovered, and the store-and-forward buffer the Data Resilience report models is the answer to that, not this soak. A soak shorter than asked - the summary says how long it ran. A site's real patch count - run it at the number the site will carry.
|
|
@@ -46,9 +46,11 @@ INDEX: List[tuple] = [
|
|
|
46
46
|
('conformance', 'Conformity, in the standards\' words', 'certificates against ISO/IEC 17025 7.8 and the bias against its class under a named ILAC-G8 decision rule'),
|
|
47
47
|
('etp', 'WITSML 2.x over ETP', 'the live port on Energistics ETP v1.2: subscribe to the store\'s channels and it pushes'),
|
|
48
48
|
('seedlink', 'Live stations (SeedLink)', 'the seismic leg\'s live port: a station\'s records as it writes them, resumed by sequence number, folded into its record list'),
|
|
49
|
+
('month-end', 'The month-end packet', 'the month\'s deliverable assembled once: every report collected, INCLUDED or NOT AVAILABLE with the reason, hashed'),
|
|
49
50
|
('backup', 'The backup', 'the site copied off the machine, verified, and restored once before it is trusted'),
|
|
50
51
|
('doctor', 'Which code is running', 'gea doctor and the serve start-up gate'),
|
|
51
52
|
('audit-update', 'Audit / Update', 'the whole audit log with filters, the program against PyPI, every report against its source'),
|
|
53
|
+
('soak', 'The soak', 'the patches through link outages for hours: reconnection, resume, loss by protocol, the page answering - measured, not modelled'),
|
|
52
54
|
('upkeep', 'Upkeep', 'housekeeping, sessions, the TLS proxy, the load test'),
|
|
53
55
|
]
|
|
54
56
|
# the four lines every page must carry, in this order
|