gea-program 0.33.0__tar.gz → 0.35.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.33.0/gea_program.egg-info → gea_program-0.35.0}/PKG-INFO +35 -2
- {gea_program-0.33.0 → gea_program-0.35.0}/README.md +34 -1
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/__init__.py +1 -1
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/__main__.py +60 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/acceptance_tests.py +311 -0
- gea_program-0.35.0/gea/control_room.py +509 -0
- gea_program-0.35.0/gea/help/control-room.md +11 -0
- gea_program-0.35.0/gea/help/map.md +11 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/helplib.py +3 -1
- gea_program-0.35.0/gea/reference/us_outline.json +1 -0
- gea_program-0.35.0/gea/reference/us_outline.provenance.json +22 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/service.py +125 -4
- gea_program-0.35.0/gea/site_map.py +295 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/web/app.html +294 -5
- {gea_program-0.33.0 → gea_program-0.35.0/gea_program.egg-info}/PKG-INFO +35 -2
- {gea_program-0.33.0 → gea_program-0.35.0}/gea_program.egg-info/SOURCES.txt +6 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/pyproject.toml +1 -1
- {gea_program-0.33.0 → gea_program-0.35.0}/LICENSE +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/backup.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/bench.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/blind_harness.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog_map.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/catalog_register.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/certificates.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/cli.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/client_reports.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/config_versioning.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/conformance.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/correlation.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/dashboard.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/deviation.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/dlis.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/dnp3.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/doctor.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/earth_model.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/edr.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/elevation.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/etp.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/example_register_map.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/fat_sat.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/files.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/follower.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/forward_model.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/friction_priors.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/gamma.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/geodesy.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/hartip.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/alarms.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/backup.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/catalog.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/charts.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/conformance.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/data-in.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/dlis.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/doctor.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/drift.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/edr.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/etp.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/files.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/historian.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/instruments.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/interval.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/month-end.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/notifications.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/osdu.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/patches.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/quality.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/seismic.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/setup.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/site.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/sites.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/slip.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/soak.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/sra.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/start.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/structure.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/tools.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/transients.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/vibration.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/housekeeping.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/interval.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/jobs.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/live_ports.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/loadtest.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/modbus.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/model_card.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/month_end.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/notify.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/opcua_port.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/operator_app.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/osdu.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/patches.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/permits.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/piwebapi.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/ports.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/ppdm.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/project.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/quakeml.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reconciler.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sample_record.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sbom.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seedlink.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/segy.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_array.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_film.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_response.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_track.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/series_cache.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/service_life.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/shapefile.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/shell.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/shut_in.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sla_report.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/slip.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/soak.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/sra.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/store_forward.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/strata_join.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/structure.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/supervisor.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/survey_view.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/telemetry.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/tool_library.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/transient.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/vibration.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/well_assembler.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/wits0.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/witsml.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea/workspace.py +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.33.0 → gea_program-0.35.0}/setup.cfg +0 -0
- {gea_program-0.33.0 → gea_program-0.35.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.35.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 (389 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]")
|
|
@@ -648,6 +648,39 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### The control room
|
|
652
|
+
|
|
653
|
+
The Control room tab opens the wall: a screen read from across the room, one
|
|
654
|
+
tile per well, worst first, each with its state in a shape and a word -
|
|
655
|
+
Serious, Watch, Good, No data - the reasons in plain words, the alarms, the
|
|
656
|
+
gauges, and for a drilling recorder the bottom, the bit, the rate of
|
|
657
|
+
penetration and the hours on bottom at the end of the record. It refreshes
|
|
658
|
+
itself every 30 seconds, says how old its last answer is, and puts a notice
|
|
659
|
+
across the screen when the service stops answering; a recorded well is
|
|
660
|
+
described at the end of its record and never called stale. F is full screen;
|
|
661
|
+
the screen is kept awake where the browser allows it; when the wells do not
|
|
662
|
+
fit, the pages turn, the worst first. The outgoing operator writes the shift
|
|
663
|
+
handover - what is open, what is being watched, what was done - and it stays
|
|
664
|
+
on the wall until the incoming operator acknowledges it; both go in the audit
|
|
665
|
+
log. A wall pass lets a screen show the wall and nothing else with no one's
|
|
666
|
+
account signed in. `gea wall`, `gea handover`, `gea help control-room`.
|
|
667
|
+
|
|
668
|
+
### The site map
|
|
669
|
+
|
|
670
|
+
The Map tab puts everything in the workspace that has a position in one
|
|
671
|
+
WGS84 frame: the wells at their declared surface positions (a NAD27
|
|
672
|
+
declaration moved by its datum shift, the shift shown), the structure layers
|
|
673
|
+
- faults and folds, formation contours, areas, points - each drawn at the
|
|
674
|
+
detail the view needs without a vertex moved, the World Stress Map's SHmax
|
|
675
|
+
directions, the seismic stations and the Seismicity Response Areas, over the
|
|
676
|
+
outline of the US states and counties. The outline ships with the program
|
|
677
|
+
(US Census Bureau 2017 cartographic boundaries by way of us-atlas, with its
|
|
678
|
+
provenance), so the map needs no network. A well without a declared position
|
|
679
|
+
is listed as not placed and is never drawn. Drag, the wheel or the keys pan
|
|
680
|
+
and zoom; click anywhere for what every layer says about that spot and the
|
|
681
|
+
distance to every placed well; rings draw distance circles around the wells.
|
|
682
|
+
`gea help map`.
|
|
683
|
+
|
|
651
684
|
### The charts
|
|
652
685
|
|
|
653
686
|
A well's page carries the Charts card: up to four channels stacked on one
|
|
@@ -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 (389 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]")
|
|
@@ -609,6 +609,39 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### The control room
|
|
613
|
+
|
|
614
|
+
The Control room tab opens the wall: a screen read from across the room, one
|
|
615
|
+
tile per well, worst first, each with its state in a shape and a word -
|
|
616
|
+
Serious, Watch, Good, No data - the reasons in plain words, the alarms, the
|
|
617
|
+
gauges, and for a drilling recorder the bottom, the bit, the rate of
|
|
618
|
+
penetration and the hours on bottom at the end of the record. It refreshes
|
|
619
|
+
itself every 30 seconds, says how old its last answer is, and puts a notice
|
|
620
|
+
across the screen when the service stops answering; a recorded well is
|
|
621
|
+
described at the end of its record and never called stale. F is full screen;
|
|
622
|
+
the screen is kept awake where the browser allows it; when the wells do not
|
|
623
|
+
fit, the pages turn, the worst first. The outgoing operator writes the shift
|
|
624
|
+
handover - what is open, what is being watched, what was done - and it stays
|
|
625
|
+
on the wall until the incoming operator acknowledges it; both go in the audit
|
|
626
|
+
log. A wall pass lets a screen show the wall and nothing else with no one's
|
|
627
|
+
account signed in. `gea wall`, `gea handover`, `gea help control-room`.
|
|
628
|
+
|
|
629
|
+
### The site map
|
|
630
|
+
|
|
631
|
+
The Map tab puts everything in the workspace that has a position in one
|
|
632
|
+
WGS84 frame: the wells at their declared surface positions (a NAD27
|
|
633
|
+
declaration moved by its datum shift, the shift shown), the structure layers
|
|
634
|
+
- faults and folds, formation contours, areas, points - each drawn at the
|
|
635
|
+
detail the view needs without a vertex moved, the World Stress Map's SHmax
|
|
636
|
+
directions, the seismic stations and the Seismicity Response Areas, over the
|
|
637
|
+
outline of the US states and counties. The outline ships with the program
|
|
638
|
+
(US Census Bureau 2017 cartographic boundaries by way of us-atlas, with its
|
|
639
|
+
provenance), so the map needs no network. A well without a declared position
|
|
640
|
+
is listed as not placed and is never drawn. Drag, the wheel or the keys pan
|
|
641
|
+
and zoom; click anywhere for what every layer says about that spot and the
|
|
642
|
+
distance to every placed well; rings draw distance circles around the wells.
|
|
643
|
+
`gea help map`.
|
|
644
|
+
|
|
612
645
|
### The charts
|
|
613
646
|
|
|
614
647
|
A well's page carries the Charts card: up to four channels stacked on one
|
|
@@ -475,6 +475,21 @@ def main(argv=None) -> int:
|
|
|
475
475
|
p_ck.add_argument("--note", type=str, default="", help="--confirm: what it was checked against (required)")
|
|
476
476
|
p_ck.add_argument("--actor", type=str, default="cli")
|
|
477
477
|
|
|
478
|
+
p_wl = sub.add_parser("wall", help="the control-room board as text: a line per well, worst first, its state and why (the same board the wall screen shows)")
|
|
479
|
+
p_wl.add_argument("--workspace", type=str, default=None, help="the workspace folder, e.g. C:\\site")
|
|
480
|
+
p_wl.add_argument("--json", action="store_true")
|
|
481
|
+
|
|
482
|
+
p_ho = sub.add_parser("handover", help="the shift handover: list the notes, sign one (--write) or acknowledge one (--ack)")
|
|
483
|
+
p_ho.add_argument("--workspace", type=str, default=None, help="the workspace folder, e.g. C:\\site")
|
|
484
|
+
p_ho.add_argument("--write", action="store_true", help="sign a note: give at least one of --open, --watching, --done")
|
|
485
|
+
p_ho.add_argument("--open", type=str, default="", help="--write: what the next shift must pick up")
|
|
486
|
+
p_ho.add_argument("--watching", type=str, default="", help="--write: what is being watched, and why")
|
|
487
|
+
p_ho.add_argument("--done", type=str, default="", help="--write: what this shift did")
|
|
488
|
+
p_ho.add_argument("--ack", type=str, default=None, help="acknowledge the note with this id (not your own)")
|
|
489
|
+
p_ho.add_argument("--comment", type=str, default="", help="--ack: an optional comment")
|
|
490
|
+
p_ho.add_argument("--actor", type=str, default=None, help="who signs or acknowledges (required with --write and --ack)")
|
|
491
|
+
p_ho.add_argument("--json", action="store_true")
|
|
492
|
+
|
|
478
493
|
p_sv = sub.add_parser("serve", help="serve the dashboard over a workspace: the page, the reports and the JSON API (standard library HTTP)")
|
|
479
494
|
p_sv.add_argument("--workspace", type=str, required=True, help="the workspace folder (gea workspace --action init)")
|
|
480
495
|
p_sv.add_argument("--host", type=str, default="127.0.0.1", help="bind address; 0.0.0.0 exposes it on the site network (put TLS in front)")
|
|
@@ -1922,6 +1937,51 @@ def main(argv=None) -> int:
|
|
|
1922
1937
|
else:
|
|
1923
1938
|
print(_SC.text(r))
|
|
1924
1939
|
return 0 if r["ready"] else 2
|
|
1940
|
+
elif a.cmd in ("wall", "handover"):
|
|
1941
|
+
import json as _json
|
|
1942
|
+
from . import control_room as _CR
|
|
1943
|
+
from .cli import _safe_stdout
|
|
1944
|
+
from .workspace import Workspace, WorkspaceError
|
|
1945
|
+
_safe_stdout()
|
|
1946
|
+
if not a.workspace:
|
|
1947
|
+
raise SystemExit(f"{a.cmd} needs --workspace, e.g. --workspace C:\\site")
|
|
1948
|
+
try:
|
|
1949
|
+
ws = Workspace(a.workspace)
|
|
1950
|
+
except WorkspaceError as e:
|
|
1951
|
+
raise SystemExit(f"{a.cmd}: {e}")
|
|
1952
|
+
if a.cmd == "wall":
|
|
1953
|
+
b = _CR.board(ws)
|
|
1954
|
+
print(_json.dumps(b, indent=1) if a.json else _CR.text(b))
|
|
1955
|
+
return 0
|
|
1956
|
+
try:
|
|
1957
|
+
if a.write:
|
|
1958
|
+
if not a.actor:
|
|
1959
|
+
raise SystemExit("handover --write needs --actor, the name the note is signed with")
|
|
1960
|
+
b = _CR.board(ws)
|
|
1961
|
+
counts = {**b["counts"], "alarms_unacked": sum(((t.get("alarms") or {}).get("unacked") or 0) for t in b["tiles"])}
|
|
1962
|
+
n = _CR.write_note(ws, a.actor, a.open, a.watching, a.done, counts)
|
|
1963
|
+
print(f"handover {n['id']} signed by {n['by']} at {n['utc']}")
|
|
1964
|
+
return 0
|
|
1965
|
+
if a.ack:
|
|
1966
|
+
if not a.actor:
|
|
1967
|
+
raise SystemExit("handover --ack needs --actor, the incoming operator's name")
|
|
1968
|
+
r = _CR.ack_note(ws, a.ack, a.actor, a.comment)
|
|
1969
|
+
print(f"handover {a.ack} acknowledged by {r['by']} at {r['utc']}")
|
|
1970
|
+
return 0
|
|
1971
|
+
except (ValueError, KeyError, PermissionError) as e:
|
|
1972
|
+
raise SystemExit(f"handover: {e.args[0] if e.args else e}")
|
|
1973
|
+
ns = _CR.notes(ws, 0)
|
|
1974
|
+
if a.json:
|
|
1975
|
+
print(_json.dumps(ns, indent=1))
|
|
1976
|
+
elif not ns:
|
|
1977
|
+
print("no handover notes yet")
|
|
1978
|
+
else:
|
|
1979
|
+
for n in ns:
|
|
1980
|
+
print(f"{n['id']} {n['utc']} by {n['by']} " + (f"acknowledged by {n['ack']['by']} at {n['ack']['utc']}" if n.get('ack') else "NOT YET ACKNOWLEDGED"))
|
|
1981
|
+
for k in _CR.NOTE_FIELDS:
|
|
1982
|
+
if n.get(k):
|
|
1983
|
+
print(f" {k}: {n[k]}")
|
|
1984
|
+
return 0
|
|
1925
1985
|
elif a.cmd == "serve":
|
|
1926
1986
|
from .service import Service
|
|
1927
1987
|
from .workspace import WorkspaceError
|
|
@@ -8304,6 +8304,315 @@ def section_cb_charts(tmp: str) -> None:
|
|
|
8304
8304
|
"kept while a window loads, the drilling progress chart with one colour per well in a fixed order in both themes; the help page"
|
|
8305
8305
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8306
8306
|
|
|
8307
|
+
|
|
8308
|
+
def section_cc_site_map(tmp: str) -> None:
|
|
8309
|
+
"""Section CC - the site map: the shipped state and county outline with its provenance, every positioned thing in one
|
|
8310
|
+
WGS84 frame (a NAD27 well moved by its datum shift and saying so, a well without a position never drawn), a layer
|
|
8311
|
+
thinned to the view's pixel without a vertex moved, what every layer says about a spot, the page."""
|
|
8312
|
+
import hashlib as _hl
|
|
8313
|
+
import http.cookiejar
|
|
8314
|
+
import math as _m
|
|
8315
|
+
import urllib.request
|
|
8316
|
+
import urllib.error
|
|
8317
|
+
from . import edr as E
|
|
8318
|
+
from . import helplib as H
|
|
8319
|
+
from . import shapefile as SF
|
|
8320
|
+
from . import site_map as SM
|
|
8321
|
+
from .workspace import Workspace
|
|
8322
|
+
from .service import Service, Users
|
|
8323
|
+
pkg = Path(__file__).parent
|
|
8324
|
+
d = Path(tmp, "cc"); d.mkdir()
|
|
8325
|
+
|
|
8326
|
+
ol = SM.outline_data()
|
|
8327
|
+
prov = json.loads((pkg / "reference" / "us_outline.provenance.json").read_text(encoding="utf-8"))
|
|
8328
|
+
cul = [c for c in ol["counties"] if c["name"] == "Culberson"]
|
|
8329
|
+
wide = SM.outline_view([-110.0, 25.0, -90.0, 37.0], 0.02)
|
|
8330
|
+
near = SM.outline_view([-105.2, 31.0, -104.0, 32.0], 0.002)
|
|
8331
|
+
c1 = {
|
|
8332
|
+
"outline": len(ol["counties"]) == 3231 and ol["states"].get("48") == "Texas" and cul and cul[0]["fips"] == "48109"
|
|
8333
|
+
and abs(cul[0]["label"][0] + 104.65) < 0.2 and abs(cul[0]["label"][1] - 31.47) < 0.2,
|
|
8334
|
+
"provenance": prov["sha256"] == _hl.sha256((pkg / "reference" / "us_outline.json").read_bytes()).hexdigest() and prov["package_license"] == "ISC"
|
|
8335
|
+
and "Copyright 2013-2019 Michael Bostock" in prov["package_license_text"] and "public domain" in prov["original_source"]
|
|
8336
|
+
and prov["counts"]["counties"] == 3231,
|
|
8337
|
+
"detail": all(a["s"] == 1 for a in wide["arcs"]) and not wide["labels"] and any(a["s"] == 0 for a in near["arcs"])
|
|
8338
|
+
and "Culberson" in [x["name"] for x in near["labels"]],
|
|
8339
|
+
}
|
|
8340
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
8341
|
+
ok(not failed1,
|
|
8342
|
+
"CC1 the outline ships with the program: the US Census Bureau's 2017 cartographic counties by way of us-atlas 3.0.1, 3,231 counties, the "
|
|
8343
|
+
"provenance with its hash, the package's ISC notice and the public-domain source; state lines only in a wide view, county lines and names "
|
|
8344
|
+
"once the view is close (Culberson, FIPS 48109)"
|
|
8345
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
8346
|
+
|
|
8347
|
+
lat1, lon1 = 31.42, -104.10
|
|
8348
|
+
kmlon = 1.0 / (111.195 * _m.cos(_m.radians(lat1)))
|
|
8349
|
+
faults = [([[(lon1 - 0.2 + 0.01 * i, lat1 - 0.3 + 0.0002 * j) for j in range(3001)]], {"TYPE": "normal fault", "NAME": f"F{i}"}) for i in range(0, 40, 8)]
|
|
8350
|
+
SF.write(str(d / "Faults"), "PolyLine", faults, [("TYPE", "C", 20, 0), ("NAME", "C", 10, 0)], SF.NAD83_GEOGCS)
|
|
8351
|
+
ctr = [([[(lon1 + (i - 1.5) * 4 * kmlon + 0.001 * k, lat1 - 0.3 + 0.006 * k) for k in range(100)]], {"ElevTop_ft": -8500.0 + 500.0 * i}) for i in range(4)]
|
|
8352
|
+
SF.write(str(d / "Top_A"), "PolyLine", ctr, [("ElevTop_ft", "F", 10, 1)], SF.NAD83_GEOGCS)
|
|
8353
|
+
SF.write(str(d / "Picks"), "Point", [([[(lon1 + 0.0001 * i, lat1 + 0.00005 * i)]], {"NAME": f"P{i}"}) for i in range(6000)], [("NAME", "C", 10, 0)], SF.NAD83_GEOGCS)
|
|
8354
|
+
with open(d / "wsm.csv", "w", encoding="utf-8") as f:
|
|
8355
|
+
f.write("ID,LAT,LON,AZI,TYPE,DEPTH,QUALITY,REGIME,LOCALITY\n")
|
|
8356
|
+
for i in range(40):
|
|
8357
|
+
f.write(f"W{i},{lat1 - 0.1 + 0.005 * i:.4f},{lon1 - 0.05:.4f},{60 + i},BO,2.5,{'DCBA'[i % 4]},NF,test\n")
|
|
8358
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
8359
|
+
empty_view = SM.overview(ws)["view"]
|
|
8360
|
+
ws.add_layer(str(d / "Faults.shp"), "Basement faults", actor="tester", citation="test")
|
|
8361
|
+
ws.add_layer(str(d / "Top_A.shp"), "Wolfcamp top", actor="tester", formation="Wolfcamp", role="top", citation="test")
|
|
8362
|
+
ws.add_layer(str(d / "Picks.shp"), "Picks", actor="tester", citation="test")
|
|
8363
|
+
ws.add_stress_map(str(d / "wsm.csv"), actor="tester", citation="test WSM")
|
|
8364
|
+
layer_view0 = SM.overview(ws)["view"]
|
|
8365
|
+
a = E.selftest(str(d)); b = E.selftest_events(str(d))
|
|
8366
|
+
wa = ws.add_well_file(a["path"], display="Well A", actor="tester"); wb = ws.add_well_file(b["path"], display="Well B", actor="tester")
|
|
8367
|
+
wc = ws.add_well_file(a["path"], display="Well C", actor="tester")
|
|
8368
|
+
ws.set_well(wa["id"], surface={"lat": 31.41, "lon": -104.12, "datum": "NAD27"}, actor="tester")
|
|
8369
|
+
ws.set_well(wb["id"], surface={"lat": 31.43, "lon": -104.08, "datum": "NAD83"}, actor="tester")
|
|
8370
|
+
o = SM.overview(ws)
|
|
8371
|
+
wl = {w["id"]: w for w in o["wells"]}
|
|
8372
|
+
view = o["view"]["bbox"]
|
|
8373
|
+
fine = SM.layer_view(ws, "Basement-faults", [-105, 30, -103, 32], 0.0)
|
|
8374
|
+
coarse = SM.layer_view(ws, "Basement-faults", [-105, 30, -103, 32], 0.01)
|
|
8375
|
+
raw = SM._layer(ws, "Basement-faults")
|
|
8376
|
+
src_pts = {(round(float(x), 5), round(float(y), 5)) for f in raw["feats"] for pp in f["parts"] for x, y in pp}
|
|
8377
|
+
moved = [p for f in coarse["features"] for pp in f["parts"] for p in pp if (p[0], p[1]) not in src_pts]
|
|
8378
|
+
ends_kept = all(f["parts"][0][0] == fine["features"][k]["parts"][0][0] and f["parts"][0][-1] == fine["features"][k]["parts"][0][-1] for k, f in enumerate(coarse["features"]))
|
|
8379
|
+
pts = SM.layer_view(ws, "Picks", [-105, 30, -103, 32], 0.0, max_points=1000)
|
|
8380
|
+
wh = SM.what_here(ws, 31.42, -104.10)
|
|
8381
|
+
sv = SM.stress_view(ws, [-104.3, 31.2, -103.9, 31.6])
|
|
8382
|
+
c2 = {
|
|
8383
|
+
"frame": "WGS84" in o["frame"] and o["view"]["why"] == "the placed wells" and empty_view["why"].startswith("nothing in the workspace")
|
|
8384
|
+
and layer_view0["why"].startswith("the area most of the layers cover (the median extent of 3 of the 3)"),
|
|
8385
|
+
"wells": wl[wa["id"]]["placed"] and 30.0 < wl[wa["id"]]["shift_m"] < 90.0 and wl[wa["id"]]["declared"]["datum"] == "NAD27"
|
|
8386
|
+
and wl[wb["id"]]["placed"] and wl[wb["id"]]["shift_m"] == 0.0 and not wl[wc["id"]]["placed"] and "lat" not in wl[wc["id"]]
|
|
8387
|
+
and view[0] < -104.12 < view[2] and view[1] < 31.43 < view[3],
|
|
8388
|
+
"layers": [(l["id"], l["kind"]) for l in o["layers"]] == [("Basement-faults", "structure"), ("Wolfcamp-top", "contours"), ("Picks", "points")]
|
|
8389
|
+
and o["layers"][0]["classes"] == ["fault"] and o["layers"][0]["n_points"] == 5 * 3001 and o["stress_map"]["n_records"] == 40,
|
|
8390
|
+
"thinning": fine["vertices"] == 5 * 3001 and coarse["vertices"] < fine["vertices"] / 10 and not moved and ends_kept
|
|
8391
|
+
and all(f.get("cls") == "fault" for f in coarse["features"]),
|
|
8392
|
+
"points": pts["in_view"] == 6000 and pts["shown"] == 1000 and pts["kind"] == "points",
|
|
8393
|
+
"what_here": wh["answered"] >= 2 and any("Nearest mapped fault" in ln for l_ in wh["layers"] for ln in (l_["lines"] or []))
|
|
8394
|
+
and any("contours" in ln for l_ in wh["layers"] for ln in (l_["lines"] or []))
|
|
8395
|
+
and [w["id"] for w in wh["wells"]] == sorted([wa["id"], wb["id"]], key=lambda i: next(x["distance_km"] for x in wh["wells"] if x["id"] == i))
|
|
8396
|
+
and len(wh["wells"]) == 2,
|
|
8397
|
+
"stress": sv["n_in_view"] == 40 and sv["records"][0]["quality"] == "A" and sv["records"][-1]["quality"] == "D" and sv["citation"] == "test WSM",
|
|
8398
|
+
}
|
|
8399
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
8400
|
+
ok(not failed2,
|
|
8401
|
+
"CC2 one frame: a well declared on NAD27 moved to WGS84 by its datum shift and the shift kept with it, a well declared on NAD83 not moved, a "
|
|
8402
|
+
"well with no position never placed; the map opens on the placed wells (else the area most layers cover, else Texas, said so); a layer "
|
|
8403
|
+
"thinned to the view's pixel keeps its ends and moves no vertex, points sampled evenly past the limit; each layer's own statement for a spot "
|
|
8404
|
+
"and the distance to every placed well; the stress records in view, best quality first"
|
|
8405
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
8406
|
+
|
|
8407
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer")
|
|
8408
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
8409
|
+
base_url = svc.url.rstrip("/")
|
|
8410
|
+
op = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
8411
|
+
|
|
8412
|
+
def call(path):
|
|
8413
|
+
req = urllib.request.Request(base_url + path, method="GET")
|
|
8414
|
+
try:
|
|
8415
|
+
with op.open(req, timeout=120) as rr_:
|
|
8416
|
+
return rr_.status, json.loads(rr_.read())
|
|
8417
|
+
except urllib.error.HTTPError as e:
|
|
8418
|
+
return e.code, json.loads(e.read())
|
|
8419
|
+
try:
|
|
8420
|
+
lreq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": "v", "password": "viewer-pass-1"}).encode())
|
|
8421
|
+
lreq.add_header("Content-Type", "application/json"); lreq.add_header("X-GEA-Action", "1"); op.open(lreq, timeout=30)
|
|
8422
|
+
bb = ",".join(f"{x:.5f}" for x in view)
|
|
8423
|
+
st_o, ov = call("/api/map")
|
|
8424
|
+
st_l, lv = call(f"/api/map/layer/Wolfcamp-top?bbox={bb}&tol=0.001")
|
|
8425
|
+
st_ol, olv = call(f"/api/map/outline?bbox={bb}&tol=0.001")
|
|
8426
|
+
st_s, svv = call(f"/api/map/stress?bbox={bb}")
|
|
8427
|
+
st_a, at = call("/api/map/at?lat=31.42&lon=-104.10")
|
|
8428
|
+
st_bad, _ = call("/api/map/layer/Wolfcamp-top?bbox=1,2,3")
|
|
8429
|
+
st_nol, _ = call(f"/api/map/layer/no-such-layer?bbox={bb}")
|
|
8430
|
+
st_noll, _ = call("/api/map/at?lat=x")
|
|
8431
|
+
finally:
|
|
8432
|
+
svc.stop()
|
|
8433
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
8434
|
+
help_md = (pkg / "help" / "map.md").read_text(encoding="utf-8")
|
|
8435
|
+
c3 = {
|
|
8436
|
+
"api": st_o == 200 and len(ov["wells"]) == 3 and st_l == 200 and lv["kind"] == "contours" and all("value" in f for f in lv["features"])
|
|
8437
|
+
and st_ol == 200 and st_s == 200 and st_a == 200 and len(at["wells"]) == 2 and st_bad == 400 and st_nol == 404 and st_noll == 400,
|
|
8438
|
+
"page": all(x in page_src for x in ("VIEWS.map", "['#/map', 'Map']", "/api/map/layer/", "/api/map/outline", "/api/map/stress", "/api/map/at",
|
|
8439
|
+
"Not placed", "declare it", "data-fit", "mpRings", "ArrowUp", "'Home' || k === 'Escape'", "scale bar", "'wheel'",
|
|
8440
|
+
"What is at", "setPointerCapture")),
|
|
8441
|
+
"help": any(t["topic"] == "map" for t in H.topics()) and not H.check("map") and H.VIEW_TOPIC.get("map") == "map"
|
|
8442
|
+
and "never moved" in help_md.split("will not call a measurement")[1],
|
|
8443
|
+
}
|
|
8444
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
8445
|
+
ok(not failed3,
|
|
8446
|
+
"CC3 the API (the contents, a layer with its contour values, the outline, the stress records, what is at a spot; a malformed box, an unknown "
|
|
8447
|
+
"layer and a missing position named) and the Map tab: pan and zoom by pointer, wheel and keys, layer toggles and fit, distance rings, a scale "
|
|
8448
|
+
"bar, the not-placed wells with the way to place them, what is at a click; the help page"
|
|
8449
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8450
|
+
|
|
8451
|
+
|
|
8452
|
+
def section_cd_control_room(tmp: str) -> None:
|
|
8453
|
+
"""Section CD - the control room: the wall board judged by its stated rule (a shape and a word for each state, worst
|
|
8454
|
+
first, a recorded well never called stale, a live feed's age measured), the shift handover signed by one operator and
|
|
8455
|
+
acknowledged by another with both in the audit log, wall passes that show the wall and nothing else, the page."""
|
|
8456
|
+
import http.cookiejar
|
|
8457
|
+
import urllib.request
|
|
8458
|
+
import urllib.error
|
|
8459
|
+
from datetime import datetime as _dt, timedelta as _td, timezone as _tz
|
|
8460
|
+
from . import control_room as CR
|
|
8461
|
+
from . import edr as E
|
|
8462
|
+
from . import helplib as H
|
|
8463
|
+
from .workspace import Workspace
|
|
8464
|
+
from .service import Service, Users
|
|
8465
|
+
pkg = Path(__file__).parent
|
|
8466
|
+
d = Path(tmp, "cd"); d.mkdir()
|
|
8467
|
+
|
|
8468
|
+
ev = E.selftest_events(str(d))
|
|
8469
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
8470
|
+
wd = ws.add_well_file(ev["path"], display="Drilling well", actor="tester")
|
|
8471
|
+
wa = ws.add_well_file(ev["path"], display="Alarm well", actor="tester")
|
|
8472
|
+
wc = ws.add_well_catalog("volve_f12_f14_production_excerpt", "15/9-F-12", 10000.0, actor="tester")
|
|
8473
|
+
ws.refresh_dashboard(actor="tester")
|
|
8474
|
+
wn = ws.add_well_file(ev["path"], display="New well", actor="tester") # added after the refresh: no reports yet
|
|
8475
|
+
ra = Path(ws.reports_dir, "wells", wa["id"])
|
|
8476
|
+
(ra / "alarm_event_report.json").write_text(json.dumps({"active": [
|
|
8477
|
+
{"alarm_id": "SPP.HH_RANGE", "state": "ACTIVE_UNACKED", "priority": "P2", "tag_id": "SPP"},
|
|
8478
|
+
{"alarm_id": "HKLD.QUALITY", "state": "ACTIVE_ACKED", "priority": "P4", "tag_id": "HKLD"}], "shelved": []}), encoding="utf-8")
|
|
8479
|
+
now = _dt(2026, 10, 15, 12, 0, 0, tzinfo=_tz.utc)
|
|
8480
|
+
iso = lambda s: (now - _td(seconds=s)).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
8481
|
+
feed = lambda age, status="CONNECTED", wid=wn["id"]: [{"name": "p1", "well_id": wid, "status": status, "last_sample_utc": (iso(age) if age is not None else None), "patch": {"stale_after_s": 120.0}}]
|
|
8482
|
+
b0 = CR.board(ws, [], now=now)
|
|
8483
|
+
t0 = {t["well_id"]: t for t in b0["tiles"]}
|
|
8484
|
+
dr = t0[wd["id"]]["drilling"]
|
|
8485
|
+
live = {k: {t["well_id"]: t for t in CR.board(ws, feed(*a), now=now)["tiles"]}[wn["id"]] for k, a in
|
|
8486
|
+
{"fresh": (30,), "late": (400,), "silent": (1000,), "down": (60, "DOWN"), "none": (None,)}.items()}
|
|
8487
|
+
c1 = {
|
|
8488
|
+
"states": t0[wa["id"]]["state"] == "SERIOUS" and t0[wa["id"]]["reasons"][:2] == ["1 unacknowledged P2 alarm(s)", "1 other active alarm(s)"]
|
|
8489
|
+
and t0[wc["id"]]["state"] == "SERIOUS" and "no explanation" in t0[wc["id"]]["reasons"][0]
|
|
8490
|
+
and t0[wd["id"]]["state"] == "GOOD" and t0[wd["id"]]["reasons"] == ["nothing needs attention"]
|
|
8491
|
+
and t0[wn["id"]]["state"] == "NO_DATA",
|
|
8492
|
+
"shape_word": all(t["shape"] == CR.SHAPE[t["state"]] and t["word"] == CR.WORD[t["state"]] for t in b0["tiles"])
|
|
8493
|
+
and len(set(CR.SHAPE.values())) == 4 and len(set(CR.WORD.values())) == 4,
|
|
8494
|
+
"order": [t["state"] for t in b0["tiles"]] == sorted((t["state"] for t in b0["tiles"]), key=CR.STATES.index)
|
|
8495
|
+
and b0["counts"] == {"SERIOUS": 2, "WATCH": 0, "GOOD": 1, "NO_DATA": 1},
|
|
8496
|
+
"recorded": t0[wd["id"]]["live"] is None and t0[wd["id"]]["recorded"]["record_end_utc"] == dr["record_end_utc"]
|
|
8497
|
+
and "never called stale" in b0["rule"],
|
|
8498
|
+
"read_only": not Path(wsp, "handover").exists() and not Path(wsp, "wall").exists(),
|
|
8499
|
+
"drilling": dr["cache"] and dr["td_md_ft"] == 5750.0 and dr["bottom_ft"] == 5750.0 and dr["on_bottom_hours"] is not None
|
|
8500
|
+
and (dr["rop_ft_h"] is not None) == bool(dr["drilling_now"]),
|
|
8501
|
+
"live": live["fresh"]["state"] == "GOOD" and live["late"]["state"] == "WATCH" and "past the 2 min stale limit" in live["late"]["reasons"][0]
|
|
8502
|
+
and live["silent"]["state"] == "SERIOUS" and live["silent"]["reasons"][0].startswith("no live sample for 16 min")
|
|
8503
|
+
and live["down"]["state"] == "SERIOUS" and live["none"]["state"] == "WATCH" and abs(live["fresh"]["live"]["age_s"] - 30) < 1e-6,
|
|
8504
|
+
}
|
|
8505
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
8506
|
+
ok(not failed1,
|
|
8507
|
+
"CD1 the wall board by its stated rule, read without writing anything: an unacknowledged P2 alarm and a gauge off with no explanation SERIOUS, a well with reports and "
|
|
8508
|
+
"nothing else GOOD, a well with neither reports nor a live sample NO DATA, each state its own shape and word, worst first; a recorded well "
|
|
8509
|
+
"described at the end of its record and never called stale (bottom 5,750 ft, the ROP only while drilling at the end); a live feed's "
|
|
8510
|
+
"measured age against its stale limit - 30 s good, 400 s watch, 1,000 s serious, a patch down serious"
|
|
8511
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
8512
|
+
|
|
8513
|
+
users = Users(str(Path(wsp, "users.json")))
|
|
8514
|
+
for n_, r_ in (("adm", "admin"), ("op1", "operator"), ("op2", "operator"), ("v", "viewer")):
|
|
8515
|
+
users.add(n_, f"{n_}-pass-123", r_)
|
|
8516
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
8517
|
+
base_url = svc.url.rstrip("/")
|
|
8518
|
+
|
|
8519
|
+
def opener():
|
|
8520
|
+
return urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
8521
|
+
|
|
8522
|
+
def call(op, method, path, body=None):
|
|
8523
|
+
req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
8524
|
+
if method == "POST":
|
|
8525
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
8526
|
+
try:
|
|
8527
|
+
with op.open(req, timeout=120) as rr_:
|
|
8528
|
+
return rr_.status, json.loads(rr_.read())
|
|
8529
|
+
except urllib.error.HTTPError as e:
|
|
8530
|
+
return e.code, json.loads(e.read())
|
|
8531
|
+
|
|
8532
|
+
def signin(name):
|
|
8533
|
+
op = opener(); call(op, "POST", "/api/login", {"name": name, "password": f"{name}-pass-123"}); return op
|
|
8534
|
+
try:
|
|
8535
|
+
anon = opener()
|
|
8536
|
+
st_anon, _ = call(anon, "GET", "/api/wall")
|
|
8537
|
+
V, O1, O2, A = signin("v"), signin("op1"), signin("op2"), signin("adm")
|
|
8538
|
+
st_v, bv = call(V, "GET", "/api/wall")
|
|
8539
|
+
st_vw, _ = call(V, "POST", "/api/handover", {"open": "x"})
|
|
8540
|
+
st_empty, _ = call(O1, "POST", "/api/handover", {"open": " ", "watching": "", "done": ""})
|
|
8541
|
+
st_long, _ = call(O1, "POST", "/api/handover", {"open": "x" * 2001})
|
|
8542
|
+
st_w, note = call(O1, "POST", "/api/handover", {"open": "Pump 2 liner swap not finished.", "watching": "SPP on the alarm well.", "done": "Reamed to bottom."})
|
|
8543
|
+
_, bdg1 = call(V, "GET", "/api/badges")
|
|
8544
|
+
_, wall1 = call(V, "GET", "/api/wall")
|
|
8545
|
+
st_self, self_ = call(O1, "POST", "/api/handover/ack", {"id": note.get("id")})
|
|
8546
|
+
st_ack, ack = call(O2, "POST", "/api/handover/ack", {"id": note.get("id"), "comment": "read at 06:05"})
|
|
8547
|
+
st_ack2, _ = call(O2, "POST", "/api/handover/ack", {"id": note.get("id")})
|
|
8548
|
+
st_nf, _ = call(O2, "POST", "/api/handover/ack", {"id": "H-none"})
|
|
8549
|
+
_, bdg2 = call(V, "GET", "/api/badges")
|
|
8550
|
+
_, hv = call(V, "GET", "/api/handover")
|
|
8551
|
+
# wall passes
|
|
8552
|
+
st_pv, _ = call(V, "POST", "/api/wall/pass", {"label": "doghouse"})
|
|
8553
|
+
st_pnl, _ = call(A, "POST", "/api/wall/pass", {"label": " "})
|
|
8554
|
+
st_pc, wp = call(A, "POST", "/api/wall/pass", {"label": "doghouse wall", "days": 30})
|
|
8555
|
+
_, plist = call(A, "GET", "/api/wall/passes")
|
|
8556
|
+
W = opener()
|
|
8557
|
+
st_bad, _ = call(W, "POST", "/api/wall/enter", {"code": wp["id"] + ".wrong"})
|
|
8558
|
+
st_in, _ = call(W, "POST", "/api/wall/enter", {"code": wp["code"]})
|
|
8559
|
+
st_ww, wwall = call(W, "GET", "/api/wall")
|
|
8560
|
+
_, wsess = call(W, "GET", "/api/session")
|
|
8561
|
+
st_wo, _ = call(W, "GET", "/api/overview")
|
|
8562
|
+
st_wh, _ = call(W, "GET", "/api/handover")
|
|
8563
|
+
st_wpost, _ = call(W, "POST", "/api/handover", {"open": "x"})
|
|
8564
|
+
st_rv, _ = call(A, "POST", "/api/wall/pass/revoke", {"id": wp["id"]})
|
|
8565
|
+
st_after, _ = call(W, "GET", "/api/wall")
|
|
8566
|
+
_, plist2 = call(A, "GET", "/api/wall/passes")
|
|
8567
|
+
L = opener()
|
|
8568
|
+
lock = [call(L, "POST", "/api/wall/enter", {"code": "W000000.nope"})[0] for _ in range(6)]
|
|
8569
|
+
finally:
|
|
8570
|
+
svc.stop()
|
|
8571
|
+
audit = [e["action"] for e in ws.audit_log(5000)]
|
|
8572
|
+
raw = (Path(wsp, "wall", "passes.json")).read_text(encoding="utf-8")
|
|
8573
|
+
lines = Path(wsp, "handover", "notes.jsonl").read_text(encoding="utf-8").splitlines()
|
|
8574
|
+
c2 = {
|
|
8575
|
+
"roles": st_anon == 401 and st_v == 200 and len(bv["tiles"]) == 4 and st_vw == 403,
|
|
8576
|
+
"note": st_empty == 400 and st_long == 400 and st_w == 200 and note["by"] == "op1" and note["ack"] is None
|
|
8577
|
+
and note["board"]["SERIOUS"] == 2 and note["board"]["alarms_unacked"] == 1
|
|
8578
|
+
and wall1["handover"]["id"] == note["id"] and wall1["handover"]["ack"] is None and bdg1["handover_unacked"] == 1,
|
|
8579
|
+
"ack": st_self == 403 and "author cannot acknowledge" in self_["error"] and st_ack == 200 and ack["by"] == "op2"
|
|
8580
|
+
and st_ack2 == 400 and st_nf == 404 and bdg2["handover_unacked"] == 0
|
|
8581
|
+
and hv["notes"][0]["ack"]["by"] == "op2" and hv["notes"][0]["ack"]["comment"] == "read at 06:05",
|
|
8582
|
+
"append_only": len(lines) == 2 and json.loads(lines[0])["kind"] == "note" and json.loads(lines[1])["kind"] == "ack",
|
|
8583
|
+
"audit": all(a_ in audit for a_ in ("handover.write", "handover.ack", "wall.pass.create", "wall.pass.failed", "wall.pass.enter", "wall.pass.revoke")),
|
|
8584
|
+
}
|
|
8585
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
8586
|
+
ok(not failed2,
|
|
8587
|
+
"CD2 the shift handover: a viewer reads the wall and cannot write; an empty or over-long note refused; a signed note keeps its author, its "
|
|
8588
|
+
"time and the board's counts (2 serious, 1 unacknowledged alarm), shows on the wall and badges the tab until acknowledged; the author "
|
|
8589
|
+
"cannot acknowledge it, the next operator can (once, with a comment); notes and acknowledgements appended, never rewritten; every step in "
|
|
8590
|
+
"the audit log"
|
|
8591
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
8592
|
+
|
|
8593
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
8594
|
+
help_md = (pkg / "help" / "control-room.md").read_text(encoding="utf-8")
|
|
8595
|
+
c3 = {
|
|
8596
|
+
"pass": st_pv == 403 and st_pnl == 400 and st_pc == 200 and wp["code"].startswith(wp["id"] + ".") and wp["code"].split(".", 1)[1] not in raw
|
|
8597
|
+
and "hash" not in json.dumps(plist) and plist["passes"][0]["status"] == "ACTIVE",
|
|
8598
|
+
"wall_only": st_bad == 401 and st_in == 200 and st_ww == 200 and len(wwall["tiles"]) == 4 and wsess["user"] is None
|
|
8599
|
+
and wsess["wall_pass"]["label"] == "doghouse wall" and st_wo == 401 and st_wh == 401 and st_wpost == 401,
|
|
8600
|
+
"revoke": st_rv == 200 and st_after == 401 and plist2["passes"][0]["status"] == "REVOKED" and lock[-1] == 429,
|
|
8601
|
+
"page": all(x in page_src for x in ("VIEWS.wall", "VIEWS.control", "['#/control', 'Control room']", "/api/wall", "/api/handover/ack",
|
|
8602
|
+
"NOT UPDATING", "wakeLock", "requestFullscreen", "SIGNED OUT", "This is a wall screen",
|
|
8603
|
+
"crShape", "data-age0", "turns pages", "/api/wall/pass/revoke", "handover_unacked")),
|
|
8604
|
+
"help": any(t["topic"] == "control-room" for t in H.topics()) and not H.check("control-room") and H.VIEW_TOPIC.get("control") == "control-room"
|
|
8605
|
+
and "never called stale" in help_md.split("will not call a measurement")[1],
|
|
8606
|
+
}
|
|
8607
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
8608
|
+
ok(not failed3,
|
|
8609
|
+
"CD3 wall passes: issued by an administrator only and named for the screen, the code shown once and only its hash kept; a wrong pass "
|
|
8610
|
+
"refused and locked out after repeats; a pass shows the wall and nothing else - no other page, no handover list, no writing - until it is "
|
|
8611
|
+
"revoked; the page: the wall full screen with its shapes and words, the age of its last answer, NOT UPDATING across the screen when the "
|
|
8612
|
+
"service stops, the screen kept awake, pages turned when the wells do not fit, the handover written and acknowledged; the help page"
|
|
8613
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8614
|
+
|
|
8615
|
+
|
|
8307
8616
|
def main() -> int:
|
|
8308
8617
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
8309
8618
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -8387,6 +8696,8 @@ def main() -> int:
|
|
|
8387
8696
|
section_bz_tool_library(tmp)
|
|
8388
8697
|
section_ca_setup_checklist(tmp)
|
|
8389
8698
|
section_cb_charts(tmp)
|
|
8699
|
+
section_cc_site_map(tmp)
|
|
8700
|
+
section_cd_control_room(tmp)
|
|
8390
8701
|
if _FAILS:
|
|
8391
8702
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
8392
8703
|
for f in _FAILS:
|