gea-program 0.17.1__tar.gz → 0.19.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.17.1 → gea_program-0.19.0}/PKG-INFO +28 -2
- {gea_program-0.17.1 → gea_program-0.19.0}/README.md +27 -1
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/__init__.py +1 -1
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/__main__.py +36 -2
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/acceptance_tests.py +279 -0
- gea_program-0.19.0/gea/backup.py +262 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/doctor.py +16 -1
- gea_program-0.19.0/gea/help/backup.md +11 -0
- gea_program-0.19.0/gea/help/month-end.md +11 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/helplib.py +2 -0
- gea_program-0.19.0/gea/month_end.py +290 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/service.py +87 -1
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/web/app.html +18 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/workspace.py +8 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/PKG-INFO +28 -2
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/SOURCES.txt +4 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/pyproject.toml +1 -1
- {gea_program-0.17.1 → gea_program-0.19.0}/LICENSE +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/bench.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/blind_harness.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/certificates.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/cli.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/client_reports.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/config_versioning.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/conformance.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/correlation.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/dashboard.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/deviation.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/earth_model.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/etp.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/example_register_map.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/fat_sat.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/files.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/follower.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/forward_model.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/gamma.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/geodesy.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/alarms.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/conformance.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/data-in.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/doctor.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/drift.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/etp.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/files.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/instruments.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/notifications.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/osdu.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/patches.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/quality.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/seismic.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/site.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/sites.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/sra.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/start.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/transients.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/vibration.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/housekeeping.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/jobs.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/live_ports.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/loadtest.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/modbus.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/model_card.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/notify.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/opcua_port.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/operator_app.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/osdu.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/patches.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/permits.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/ports.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/ppdm.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/project.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/quakeml.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reconciler.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sample_record.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sbom.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seedlink.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/segy.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_array.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_film.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_response.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_track.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/service_life.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/shell.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/shut_in.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sla_report.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/sra.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/store_forward.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/strata_join.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/supervisor.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/survey_view.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/telemetry.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/tool_library.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/transient.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/vibration.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/well_assembler.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/wits0.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea/witsml.py +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.17.1 → gea_program-0.19.0}/setup.cfg +0 -0
- {gea_program-0.17.1 → gea_program-0.19.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.19.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 (344 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 backup --workspace C:\site --out D:\gea-backups --keep 14` copies the
|
|
744
|
+
whole site into one dated archive with a manifest of every file's hash, onto
|
|
745
|
+
another disk or a share; schedule it daily from Audit / Update, verify an
|
|
746
|
+
archive with `--verify`, and restore one onto an empty folder with
|
|
747
|
+
`--restore ... --to` and open it once before the first well is live - the
|
|
748
|
+
records are the product, and a backup nobody has restored is a hope. The
|
|
749
|
+
doctor warns when there has never been a backup or the last is more than a
|
|
750
|
+
day old. `gea help backup`.
|
|
725
751
|
|
|
726
752
|
## The standalone install kit
|
|
727
753
|
|
|
@@ -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 (344 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 backup --workspace C:\site --out D:\gea-backups --keep 14` copies the
|
|
705
|
+
whole site into one dated archive with a manifest of every file's hash, onto
|
|
706
|
+
another disk or a share; schedule it daily from Audit / Update, verify an
|
|
707
|
+
archive with `--verify`, and restore one onto an empty folder with
|
|
708
|
+
`--restore ... --to` and open it once before the first well is live - the
|
|
709
|
+
records are the product, and a backup nobody has restored is a hope. The
|
|
710
|
+
doctor warns when there has never been a backup or the last is more than a
|
|
711
|
+
day old. `gea help backup`.
|
|
686
712
|
|
|
687
713
|
## The standalone install kit
|
|
688
714
|
|
|
@@ -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")
|
|
@@ -411,6 +413,16 @@ def main(argv=None) -> int:
|
|
|
411
413
|
p_es.add_argument("--frames", type=int, default=600)
|
|
412
414
|
p_es.add_argument("--interval", type=float, default=1.0, help="seconds between samples")
|
|
413
415
|
p_es.add_argument("--seed", type=int, default=1)
|
|
416
|
+
p_bk = sub.add_parser("backup", help="the site copied off the machine: one dated archive with a manifest; --verify an archive; --restore one onto an empty folder; --status")
|
|
417
|
+
p_bk.add_argument("--workspace", type=str, default=None, help="the site folder (for a backup, or --status)")
|
|
418
|
+
p_bk.add_argument("--out", type=str, default=None, help="the folder the archive goes to - another disk or a share, never inside the workspace")
|
|
419
|
+
p_bk.add_argument("--keep", type=int, default=None, help="keep only the newest N archives of this site in --out")
|
|
420
|
+
p_bk.add_argument("--label", type=str, default="", help="a note carried in the manifest (e.g. 'before the gauge swap')")
|
|
421
|
+
p_bk.add_argument("--verify", type=str, default=None, help="check this archive against its manifest and its .sha256")
|
|
422
|
+
p_bk.add_argument("--restore", type=str, default=None, help="restore this archive ...")
|
|
423
|
+
p_bk.add_argument("--to", type=str, default=None, help="... onto this empty folder")
|
|
424
|
+
p_bk.add_argument("--status", action="store_true", help="when the last backup of --workspace was taken, and whether that is too long ago")
|
|
425
|
+
p_bk.add_argument("--actor", type=str, default="cli")
|
|
414
426
|
p_sl = sub.add_parser("seedlink", help="the seismic leg's live port: a SeedLink (3 / 4.0) station's records as it writes them, byte for byte into day files; --selftest against the simulator")
|
|
415
427
|
p_sl.add_argument("--config", type=str, default=None, help="the SeedLink config JSON (host, port, streams)")
|
|
416
428
|
p_sl.add_argument("--write-example-config", type=str, default=None, help="write an example config here and exit")
|
|
@@ -1934,6 +1946,10 @@ def main(argv=None) -> int:
|
|
|
1934
1946
|
raise SystemExit("well-identity needs --well <id>")
|
|
1935
1947
|
r = ws.well_identity(a.well)
|
|
1936
1948
|
print(_ppdm_text(r)); return 0
|
|
1949
|
+
elif a.action == "month-end":
|
|
1950
|
+
from .month_end import report_text as _me_text
|
|
1951
|
+
r = ws.month_end(a.period, actor=a.actor, refresh=not a.no_refresh, catalog_csv=a.catalog)
|
|
1952
|
+
print(_me_text(r)); return 0 if not r["errors"] else 1
|
|
1937
1953
|
elif a.action == "add-live-station":
|
|
1938
1954
|
if not a.seedlink_config or not a.name:
|
|
1939
1955
|
raise SystemExit("add-live-station needs --name <display> and --seedlink-config <json>")
|
|
@@ -2017,6 +2033,24 @@ def main(argv=None) -> int:
|
|
|
2017
2033
|
stop.set()
|
|
2018
2034
|
print("wits0-sim: stopped")
|
|
2019
2035
|
return 0
|
|
2036
|
+
elif a.cmd == "backup":
|
|
2037
|
+
from . import backup as _B
|
|
2038
|
+
try:
|
|
2039
|
+
if a.verify:
|
|
2040
|
+
r = _B.verify(a.verify); print(_B.report_text(r, "verify")); return 0 if r["status"] == "OK" else 1
|
|
2041
|
+
if a.restore:
|
|
2042
|
+
if not a.to:
|
|
2043
|
+
raise SystemExit("backup --restore needs --to <empty folder>")
|
|
2044
|
+
print(_B.report_text(_B.restore(a.restore, a.to, a.actor), "restore")); return 0
|
|
2045
|
+
if not a.workspace:
|
|
2046
|
+
raise SystemExit("backup needs --workspace <site folder> (with --out <another disk>), or --verify / --restore an archive")
|
|
2047
|
+
if a.status:
|
|
2048
|
+
r = _B.status(a.workspace); print(_B.report_text(r, "status")); return 0 if not (r["never"] or r["stale"]) else 1
|
|
2049
|
+
if not a.out:
|
|
2050
|
+
raise SystemExit("backup needs --out <a folder on another disk or a share>")
|
|
2051
|
+
print(_B.report_text(_B.make(a.workspace, a.out, a.actor, keep=a.keep, label=a.label), "make")); return 0
|
|
2052
|
+
except (ValueError, OSError) as e:
|
|
2053
|
+
raise SystemExit(f"backup: {e}")
|
|
2020
2054
|
elif a.cmd == "seedlink":
|
|
2021
2055
|
import json as _json
|
|
2022
2056
|
from . import seedlink as _L
|
|
@@ -5719,6 +5719,283 @@ def section_bk_seedlink(tmp: str) -> None:
|
|
|
5719
5719
|
"names the simulator to start; the help page says a gap is counted, never bridged"
|
|
5720
5720
|
+ (f" [failed: {', '.join(failed)}; row={json.dumps({k: row.get(k) for k in ('status', 'protocol', 'records', 'latency_p95_s', 'last_error')})}]" if failed else ""))
|
|
5721
5721
|
|
|
5722
|
+
|
|
5723
|
+
def section_bl_backup(tmp: str) -> None:
|
|
5724
|
+
"""Section BL - the site copied off the machine: the archive and its manifest, verify, a tampered archive named,
|
|
5725
|
+
restore onto an empty folder and the workspace that comes back, the refusals, the doctor's line, the page's
|
|
5726
|
+
card, the job and the daily schedule."""
|
|
5727
|
+
import http.cookiejar
|
|
5728
|
+
import subprocess as _sp
|
|
5729
|
+
import urllib.request
|
|
5730
|
+
import urllib.error
|
|
5731
|
+
import zipfile
|
|
5732
|
+
from . import backup as B
|
|
5733
|
+
from . import doctor as D
|
|
5734
|
+
from . import helplib as H
|
|
5735
|
+
from .telemetry import TelemetryRecorder, TelemetryConfig
|
|
5736
|
+
from .downhole_engine import DownholeEngine, SimulatorConfig
|
|
5737
|
+
from .workspace import Workspace
|
|
5738
|
+
from .service import Service, Users
|
|
5739
|
+
pkg = Path(__file__).parent
|
|
5740
|
+
d = Path(tmp, "bl"); d.mkdir()
|
|
5741
|
+
hist = str(d / "hist.csv")
|
|
5742
|
+
TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=0.5, seed=5)).run().export_csv(hist)
|
|
5743
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
5744
|
+
w = ws.add_well_file(hist, display="SWD 1", actor="tester")
|
|
5745
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator"); users.add("adm", "admin-pass-1", "admin")
|
|
5746
|
+
(Path(wsp) / "jobs" / "run-0001").mkdir(parents=True); (Path(wsp) / "jobs" / "run-0001" / "log.txt").write_text("a finished run")
|
|
5747
|
+
(Path(wsp) / "jobs" / "_uploads").mkdir(); (Path(wsp) / "jobs" / "_uploads" / "x.bin").write_bytes(b"scratch")
|
|
5748
|
+
(Path(wsp) / "jobs" / "schedule.json").write_text(json.dumps({"entries": []}))
|
|
5749
|
+
|
|
5750
|
+
# -- make, verify, status, restore; the refusals; the pruning ---------------------------------------------------
|
|
5751
|
+
never = B.status(wsp)
|
|
5752
|
+
out = str(d / "off-disk")
|
|
5753
|
+
r1 = B.make(wsp, out, "tester", keep=2, label="first")
|
|
5754
|
+
v1 = B.verify(r1["path"])
|
|
5755
|
+
side1 = os.path.isfile(r1["path"] + ".sha256") # before the later --keep prunes this first archive and its hash file
|
|
5756
|
+
st1 = B.status(wsp)
|
|
5757
|
+
with zipfile.ZipFile(r1["path"]) as z:
|
|
5758
|
+
names = z.namelist(); man = json.loads(z.read("MANIFEST.json"))
|
|
5759
|
+
inside_refused = None
|
|
5760
|
+
try:
|
|
5761
|
+
B.make(wsp, os.path.join(wsp, "records", "bk"), "tester")
|
|
5762
|
+
except ValueError as e:
|
|
5763
|
+
inside_refused = str(e)
|
|
5764
|
+
rr = B.restore(r1["path"], str(d / "restored"), "tester")
|
|
5765
|
+
ws2 = Workspace(str(d / "restored"))
|
|
5766
|
+
nonempty_refused = None
|
|
5767
|
+
try:
|
|
5768
|
+
B.restore(r1["path"], str(d / "restored"), "tester")
|
|
5769
|
+
except ValueError as e:
|
|
5770
|
+
nonempty_refused = str(e)
|
|
5771
|
+
# tamper: rewrite one member in a copy; verify names it and restore refuses it
|
|
5772
|
+
bad = str(d / "tampered.zip")
|
|
5773
|
+
with zipfile.ZipFile(r1["path"]) as zin, zipfile.ZipFile(bad, "w") as zout:
|
|
5774
|
+
for info in zin.infolist():
|
|
5775
|
+
data = zin.read(info.filename)
|
|
5776
|
+
if info.filename == "workspace.json":
|
|
5777
|
+
data = data.replace(b"Pad 3", b"Pad 4")
|
|
5778
|
+
zout.writestr(info, data)
|
|
5779
|
+
vbad = B.verify(bad)
|
|
5780
|
+
tampered_refused = None
|
|
5781
|
+
try:
|
|
5782
|
+
B.restore(bad, str(d / "restored2"), "tester")
|
|
5783
|
+
except ValueError as e:
|
|
5784
|
+
tampered_refused = str(e)
|
|
5785
|
+
r2 = B.make(wsp, out, "tester", keep=2); r3 = B.make(wsp, out, "tester", keep=2)
|
|
5786
|
+
left = sorted(n for n in os.listdir(out) if n.endswith(".zip"))
|
|
5787
|
+
rec_files = [n for n in names if n.startswith(f"wells/{w['id']}/")]
|
|
5788
|
+
c1 = {
|
|
5789
|
+
"never_then_current": never["never"] and never["stale"] and st1["never"] is False and st1["stale"] is False and st1["present"] is True and st1["count"] == 1,
|
|
5790
|
+
"manifest": r1["n_files"] == man["n_files"] and man["protocol"] == "gea.backup/1" and man["site"] == "Pad 3" and man["wells"] == [w["id"]] and man["users_json"] is True,
|
|
5791
|
+
"members": "users.json" in names and "records/audit.jsonl" in names and "workspace.json" in names and "jobs/schedule.json" in names and bool(rec_files)
|
|
5792
|
+
and not any(n.startswith("jobs/run-") or "_uploads" in n for n in names),
|
|
5793
|
+
"verify_ok": v1["status"] == "OK" and v1["n_files"] == r1["n_files"] and v1["archive_sha256_matches"] is True and side1,
|
|
5794
|
+
"inside_refused": bool(inside_refused) and "inside the workspace" in inside_refused,
|
|
5795
|
+
"restored_site": rr["site"] == "Pad 3" and rr["wells"] == [w["id"]] and rr["users_json"] is True and ws2.manifest["name"] == "Pad 3" and ws2.wells()[0]["id"] == w["id"],
|
|
5796
|
+
"restored_note_audit": os.path.isfile(d / "restored" / "restored_from.json") and any(a["action"] == "backup.restore" for a in ws2.audit_log()),
|
|
5797
|
+
"restored_accounts": Users(str(d / "restored" / "users.json")).check("op", "operator-pass-1") is not None,
|
|
5798
|
+
"nonempty_refused": bool(nonempty_refused) and "empty folder" in nonempty_refused,
|
|
5799
|
+
"tampered": vbad["status"] == "FAIL" and vbad["mismatched"] == ["workspace.json"] and bool(tampered_refused) and "does not verify" in tampered_refused,
|
|
5800
|
+
"pruned": len(left) == 2 and os.path.basename(r3["path"]) in left and os.path.basename(r1["path"]) not in left and r3["pruned"] == [os.path.basename(r1["path"])],
|
|
5801
|
+
"audited": any(a["action"] == "backup.make" for a in ws.audit_log()),
|
|
5802
|
+
}
|
|
5803
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
5804
|
+
ok(not failed1,
|
|
5805
|
+
"BL1 the site copied off the machine: one dated archive carrying every well's source and records, users.json, the audit log, the manifest "
|
|
5806
|
+
"and the schedule - never the job run folders or the upload scratch - with MANIFEST.json (site, version, every file's SHA-256) inside and the "
|
|
5807
|
+
"archive's own hash beside it; verify reads OK; status turns from NEVER to current; a folder inside the workspace is refused; the archive "
|
|
5808
|
+
"restored onto an empty folder opens as the same site with the same well, the same accounts and a restored_from note, and a second restore "
|
|
5809
|
+
"onto it is refused; a tampered archive fails verify naming the file and is refused by restore; --keep prunes the oldest; both in the audit"
|
|
5810
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
5811
|
+
|
|
5812
|
+
# -- the doctor, the service, the page, the CLI ------------------------------------------------------------------
|
|
5813
|
+
wsp2 = str(d / "ws2"); ws3 = Workspace.create(wsp2, "Pad 3", actor="tester")
|
|
5814
|
+
users3 = Users(str(Path(wsp2, "users.json"))); users3.add("adm", "admin-pass-1", "admin"); users3.add("op", "operator-pass-1", "operator")
|
|
5815
|
+
f_never = [x for x in D.check_workspace(wsp2, port=0) if "backup" in x["what"]]
|
|
5816
|
+
svc = Service(wsp2, port=0, scheduler=False).start()
|
|
5817
|
+
base = svc.url.rstrip("/")
|
|
5818
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
5819
|
+
|
|
5820
|
+
def call(method, path, body=None):
|
|
5821
|
+
req = urllib.request.Request(base + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
5822
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
5823
|
+
try:
|
|
5824
|
+
with opener.open(req, timeout=120) as rr_:
|
|
5825
|
+
return rr_.status, json.loads(rr_.read())
|
|
5826
|
+
except urllib.error.HTTPError as e:
|
|
5827
|
+
return e.code, json.loads(e.read())
|
|
5828
|
+
try:
|
|
5829
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
5830
|
+
st_op = call("GET", "/api/backup")[0]
|
|
5831
|
+
call("POST", "/api/login", {"name": "adm", "password": "admin-pass-1"})
|
|
5832
|
+
st_v0, v0 = call("GET", "/api/backup")
|
|
5833
|
+
st_run0 = call("POST", "/api/backup/run", {})[0]
|
|
5834
|
+
st_in = call("POST", "/api/backup/config", {"out_dir": os.path.join(wsp2, "records", "bk")})[0]
|
|
5835
|
+
st_bad_t = call("POST", "/api/backup/config", {"out_dir": str(d / "off2"), "daily_at": "2:30"})[0]
|
|
5836
|
+
st_cfg, cfg = call("POST", "/api/backup/config", {"out_dir": str(d / "off2"), "keep": 7, "daily_at": "02:30"})
|
|
5837
|
+
st_run, job = call("POST", "/api/backup/run", {})
|
|
5838
|
+
done = svc.app.runner.wait(job["id"], 120)
|
|
5839
|
+
st_v1, v1s = call("GET", "/api/backup")
|
|
5840
|
+
st_sch, sch = call("POST", "/api/backup/schedule", {})
|
|
5841
|
+
st_v2, v2s = call("GET", "/api/backup")
|
|
5842
|
+
finally:
|
|
5843
|
+
svc.stop()
|
|
5844
|
+
f_after = [x for x in D.check_workspace(wsp2, port=0) if "backup" in x["what"]]
|
|
5845
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
5846
|
+
help_md = (pkg / "help" / "backup.md").read_text(encoding="utf-8")
|
|
5847
|
+
r_st0 = _sp.run([sys.executable, "-m", "gea", "backup", "--workspace", str(d / "ws3-none"), "--status"], capture_output=True, text=True)
|
|
5848
|
+
r_st = _sp.run([sys.executable, "-m", "gea", "backup", "--workspace", wsp2, "--status"], capture_output=True, text=True)
|
|
5849
|
+
r_v = _sp.run([sys.executable, "-m", "gea", "backup", "--verify", v1s["status"]["last"]["path"]], capture_output=True, text=True)
|
|
5850
|
+
r_vbad = _sp.run([sys.executable, "-m", "gea", "backup", "--verify", bad], capture_output=True, text=True)
|
|
5851
|
+
r_rs = _sp.run([sys.executable, "-m", "gea", "backup", "--restore", v1s["status"]["last"]["path"], "--to", str(d / "restored3")], capture_output=True, text=True)
|
|
5852
|
+
c2 = {
|
|
5853
|
+
"doctor_never": len(f_never) == 1 and f_never[0]["level"] == "warn" and "no backup has ever been taken" in f_never[0]["what"] and "gea backup" in f_never[0]["fix"],
|
|
5854
|
+
"api_admin_only_and_empty": st_op == 403 and st_v0 == 200 and v0["status"]["never"] is True and v0["schedule"] is None and v0["config"]["out_dir"] == "",
|
|
5855
|
+
"config_refusals": st_run0 == 400 and st_in == 400 and st_bad_t == 400 and st_cfg == 200 and cfg["keep"] == 7,
|
|
5856
|
+
"job_ran": st_run == 200 and done["status"] == "DONE" and st_v1 == 200 and v1s["status"]["never"] is False and v1s["status"]["present"] is True
|
|
5857
|
+
and os.path.isfile(v1s["status"]["last"]["path"]) and v1s["status"]["last"]["actor"] == "adm",
|
|
5858
|
+
"scheduled": st_sch == 200 and sch["name"] == "backup-daily" and sch["daily_at"] == "02:30" and sch["args"][0] == "backup" and "--keep" in sch["args"]
|
|
5859
|
+
and v2s["schedule"] is not None and v2s["schedule"]["name"] == "backup-daily",
|
|
5860
|
+
"doctor_after": len(f_after) == 1 and f_after[0]["level"] == "ok" and "last backup" in f_after[0]["what"],
|
|
5861
|
+
"page": "The backup" in page_src and "/api/backup/run" in page_src and "/api/backup/schedule" in page_src,
|
|
5862
|
+
"cli_status": r_st0.returncode == 1 and "NEVER" in r_st0.stdout and r_st.returncode == 0 and "current" in r_st.stdout,
|
|
5863
|
+
"cli_verify": r_v.returncode == 0 and "OK" in r_v.stdout and r_vbad.returncode == 1 and "workspace.json" in r_vbad.stdout,
|
|
5864
|
+
"cli_restore": r_rs.returncode == 0 and "users.json present" in r_rs.stdout,
|
|
5865
|
+
"help": any(t["topic"] == "backup" for t in H.topics()) and not H.check("backup") and "never restored" in help_md.split("will not call a measurement")[1],
|
|
5866
|
+
}
|
|
5867
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
5868
|
+
ok(not failed2,
|
|
5869
|
+
"BL2 the doctor warns NEVER until a backup exists and says when the last one was afterwards; the Audit / Update page's backup card (admin only) "
|
|
5870
|
+
"refuses a folder inside the workspace and a malformed hour, saves the folder and the keep count, runs the backup as a job that the status then "
|
|
5871
|
+
"shows with its actor, and schedules it daily as the `backup-daily` entry; `gea backup --status` exits 1 on NEVER and 0 when current, "
|
|
5872
|
+
"`--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
|
+
+ (f" [failed: {', '.join(failed2)}; r_st0={r_st0.stdout[-120:]!r} {r_st0.stderr[-120:]!r}]" if failed2 else ""))
|
|
5874
|
+
|
|
5875
|
+
|
|
5876
|
+
def section_bm_month_end(tmp: str) -> None:
|
|
5877
|
+
"""Section BM - the month-end packet: the refresh for the period, every report collected with INCLUDED or NOT
|
|
5878
|
+
AVAILABLE and the reason, the index and the manifest, the archive, the records; the page, the job and the
|
|
5879
|
+
monthly schedule."""
|
|
5880
|
+
import http.cookiejar
|
|
5881
|
+
import subprocess as _sp
|
|
5882
|
+
import urllib.request
|
|
5883
|
+
import urllib.error
|
|
5884
|
+
import zipfile
|
|
5885
|
+
from . import month_end as ME
|
|
5886
|
+
from . import helplib as H
|
|
5887
|
+
from .telemetry import TelemetryRecorder, TelemetryConfig
|
|
5888
|
+
from .downhole_engine import DownholeEngine, SimulatorConfig
|
|
5889
|
+
from .workspace import Workspace
|
|
5890
|
+
from .service import Service, Users
|
|
5891
|
+
pkg = Path(__file__).parent
|
|
5892
|
+
d = Path(tmp, "bm"); d.mkdir()
|
|
5893
|
+
hist = str(d / "hist.csv")
|
|
5894
|
+
TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=2.0, seed=5)).run().export_csv(hist)
|
|
5895
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
5896
|
+
w = ws.add_well_file(hist, display="SWD 1", actor="tester")
|
|
5897
|
+
site = ws.add_site("Pad 3 site", wells=[w["id"]], actor="tester")
|
|
5898
|
+
bad_period = None
|
|
5899
|
+
try:
|
|
5900
|
+
ME.period_bounds("2026-9")
|
|
5901
|
+
except ValueError as e:
|
|
5902
|
+
bad_period = str(e)
|
|
5903
|
+
pb = ME.period_bounds("2026-02")
|
|
5904
|
+
rec = ME.assemble(ws, "2026-09", actor="tester")
|
|
5905
|
+
idx = json.loads((Path(rec["folder"]) / "INDEX.json").read_text(encoding="utf-8"))
|
|
5906
|
+
man = json.loads((Path(rec["folder"]) / "MANIFEST.json").read_text(encoding="utf-8"))
|
|
5907
|
+
by_name = {i["name"]: i for i in idx["items"]}
|
|
5908
|
+
v_folder = ME.verify(rec["folder"]); v_zip = ME.verify(rec["archive"])
|
|
5909
|
+
with zipfile.ZipFile(rec["archive"]) as z:
|
|
5910
|
+
znames = set(z.namelist())
|
|
5911
|
+
# a second assembly of the same period rebuilds whole; a future month is OPEN
|
|
5912
|
+
rec2 = ME.assemble(ws, "2026-09", actor="tester", refresh=False)
|
|
5913
|
+
from datetime import datetime as _dt, timezone as _tz
|
|
5914
|
+
now = _dt.now(_tz.utc)
|
|
5915
|
+
cur = f"{now.year:04d}-{now.month:02d}"
|
|
5916
|
+
rec3 = ME.assemble(ws, cur, actor="tester", refresh=False)
|
|
5917
|
+
pk = ME.packets(ws)
|
|
5918
|
+
audit = [a["action"] for a in ws.audit_log()]
|
|
5919
|
+
c1 = {
|
|
5920
|
+
"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,
|
|
5921
|
+
"included": by_name["SWD 1: gauge drift report"]["status"] == "INCLUDED" and by_name["SWD 1: alarm event report"]["status"] == "INCLUDED"
|
|
5922
|
+
and by_name["SLA month 2026-09"]["status"] == "INCLUDED" and by_name["Accuracy statement"]["status"] == "INCLUDED"
|
|
5923
|
+
and by_name["Pad 3 site: site report"]["status"] == "INCLUDED" and by_name["Model cards and SBOM"]["status"] == "INCLUDED",
|
|
5924
|
+
"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"]
|
|
5925
|
+
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"]
|
|
5926
|
+
and "no certificate register" in by_name["SWD 1: certificate conformity (ISO/IEC 17025 7.8)"]["reason"],
|
|
5927
|
+
"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
|
|
5928
|
+
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"],
|
|
5929
|
+
"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"])
|
|
5930
|
+
and os.path.isfile(rec["archive"] + "") and rec["archive"].endswith("month_end_Pad-3_2026-09.zip"),
|
|
5931
|
+
"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,
|
|
5932
|
+
"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")
|
|
5933
|
+
and len(pk) == 3 and all(p["present"] for p in pk) and pk[-1]["period"] == cur,
|
|
5934
|
+
"audited": audit.count("month_end.assemble") == 3 and "dashboard.refresh" in audit and "site.report" in audit,
|
|
5935
|
+
"not_a_measurement": "does not call a measurement" in (Path(rec["folder"]) / "INDEX.md").read_text(encoding="utf-8"),
|
|
5936
|
+
}
|
|
5937
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
5938
|
+
ok(not failed1,
|
|
5939
|
+
"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 "
|
|
5940
|
+
"collected - the well's drift and alarm reports, the SLA month, the accuracy statement, the site report, the model cards - INCLUDED with "
|
|
5941
|
+
"hashed files, and the vibration report, the SRA packet and the certificate conformity NOT AVAILABLE with the reason that names what the "
|
|
5942
|
+
"site lacks; INDEX and MANIFEST written, the archive verifies against the manifest, a second assembly rebuilds the period whole, the "
|
|
5943
|
+
"current month is marked OPEN, every packet on record, every assembly in the audit"
|
|
5944
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
5945
|
+
|
|
5946
|
+
# -- the page, the job, the schedule, the CLI, the help --------------------------------------------------------
|
|
5947
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator"); users.add("adm", "admin-pass-1", "admin")
|
|
5948
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
5949
|
+
base = svc.url.rstrip("/")
|
|
5950
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
5951
|
+
|
|
5952
|
+
def call(method, path, body=None):
|
|
5953
|
+
req = urllib.request.Request(base + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
5954
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
5955
|
+
try:
|
|
5956
|
+
with opener.open(req, timeout=300) as rr_:
|
|
5957
|
+
return rr_.status, json.loads(rr_.read())
|
|
5958
|
+
except urllib.error.HTTPError as e:
|
|
5959
|
+
return e.code, json.loads(e.read())
|
|
5960
|
+
try:
|
|
5961
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
5962
|
+
st_l, lst = call("GET", "/api/month-end")
|
|
5963
|
+
st_bad = call("POST", "/api/month-end/run", {"period": "Sept 2026"})[0]
|
|
5964
|
+
st_sched_op = call("POST", "/api/month-end/schedule", {"day": 1, "at": "03:00"})[0]
|
|
5965
|
+
st_run, job = call("POST", "/api/month-end/run", {"period": "2026-08", "no_refresh": True})
|
|
5966
|
+
done = svc.app.runner.wait(job["id"], 300)
|
|
5967
|
+
st_l2, lst2 = call("GET", "/api/month-end")
|
|
5968
|
+
req = urllib.request.Request(base + "/reports/month_end/2026-08/INDEX.html"); req.add_header("X-GEA-Action", "1")
|
|
5969
|
+
with opener.open(req, timeout=60) as rr_:
|
|
5970
|
+
html_ok = rr_.status == 200 and b"Month-End Packet" in rr_.read()
|
|
5971
|
+
call("POST", "/api/login", {"name": "adm", "password": "admin-pass-1"})
|
|
5972
|
+
st_sched_bad = call("POST", "/api/month-end/schedule", {"day": 31, "at": "03:00"})[0]
|
|
5973
|
+
st_sched, sch = call("POST", "/api/month-end/schedule", {"day": 2, "at": "03:15"})
|
|
5974
|
+
st_l3, lst3 = call("GET", "/api/month-end")
|
|
5975
|
+
finally:
|
|
5976
|
+
svc.stop()
|
|
5977
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
5978
|
+
help_md = (pkg / "help" / "month-end.md").read_text(encoding="utf-8")
|
|
5979
|
+
r_cli = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "month-end", "--period", "2026-07", "--no-refresh"], capture_output=True, text=True)
|
|
5980
|
+
r_prev = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "month-end", "--no-refresh"], capture_output=True, text=True)
|
|
5981
|
+
c2 = {
|
|
5982
|
+
"list": st_l == 200 and len(lst["packets"]) == 3 and lst["previous"] == ME.previous_period() and lst["schedule"] is None,
|
|
5983
|
+
"refusals": st_bad == 400 and st_sched_op == 403 and st_sched_bad == 400,
|
|
5984
|
+
"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,
|
|
5985
|
+
"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"]
|
|
5986
|
+
and st_l3 == 200 and lst3["schedule"]["name"] == "month-end-monthly",
|
|
5987
|
+
"page": "Month-end packet" in page_src and "/api/month-end/run" in page_src and "/api/month-end/schedule" in page_src,
|
|
5988
|
+
"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,
|
|
5989
|
+
"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],
|
|
5990
|
+
}
|
|
5991
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
5992
|
+
ok(not failed2,
|
|
5993
|
+
"BM2 the Reports page carries the packet card: the packets on record with their index and archive, the period defaulting to the last closed "
|
|
5994
|
+
"month, Assemble as a job (a malformed period refused) whose INDEX the page then serves, Schedule monthly for an administrator only (day 1..28) "
|
|
5995
|
+
"as the `month-end-monthly` entry for the previous month; `gea workspace --action month-end` with a period or with 'previous'; the help page "
|
|
5996
|
+
"says the index collects and does not recompute"
|
|
5997
|
+
+ (f" [failed: {', '.join(failed2)}; cli={r_cli.stdout[-200:]!r} {r_cli.stderr[-200:]!r}]" if failed2 else ""))
|
|
5998
|
+
|
|
5722
5999
|
def main() -> int:
|
|
5723
6000
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
5724
6001
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -5785,6 +6062,8 @@ def main() -> int:
|
|
|
5785
6062
|
section_bi_conformance(tmp)
|
|
5786
6063
|
section_bj_etp(tmp)
|
|
5787
6064
|
section_bk_seedlink(tmp)
|
|
6065
|
+
section_bl_backup(tmp)
|
|
6066
|
+
section_bm_month_end(tmp)
|
|
5788
6067
|
if _FAILS:
|
|
5789
6068
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
5790
6069
|
for f in _FAILS:
|