gea-program 0.59.0__tar.gz → 0.60.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.59.0 → gea_program-0.60.0}/PKG-INFO +11 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/README.md +10 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/__init__.py +1 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/__main__.py +10 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/acceptance_tests.py +175 -2
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/dashboard.py +149 -128
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/files.py +2 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/catalog.md +1 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/month-end.md +1 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/invoice_check.py +3 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/month_end.py +20 -2
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/rig_state.py +4 -2
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/service.py +32 -9
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/tls.py +3 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/web/app.html +16 -10
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/workspace.py +38 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/PKG-INFO +11 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/pyproject.toml +1 -1
- {gea_program-0.59.0 → gea_program-0.60.0}/LICENSE +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/alerts.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/backup.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/bench.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/blind_harness.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog_map.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/catalog_register.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/certificates.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/cli.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/client_reports.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/collision.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/config_versioning.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/conformance.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/control_room.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/correlation.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/declarations.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/decline.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/deviation.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/dlis.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/dnp3.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/doctor.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/dossier.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/dossier_verify.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/drill_string.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/earth_model.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/economics.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/ed25519.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/edr.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/elevation.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/etp.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/example_register_map.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fat_sat.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/follower.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/forward_model.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/frac_hits.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/friction_priors.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/gamma.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/geodesy.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/geomag.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/hartip.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/IT_SECURITY.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/alarms.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/alerts.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/backup.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/charts.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/collision.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/company-sign-in.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/conformance.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/control-room.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/data-in.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/declare.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/decline.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/dlis.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/doctor.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/dossier.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/drift.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/economics.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/edr.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/etp.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/files.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/frac-hits.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/historian.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/https.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/hydraulics.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/instruments.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/interval.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/invoice-check.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/map.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/notifications.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/opseis.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/osdu.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/pad-summary.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/patches.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/plan.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/pore-pressure.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/quality.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/report-history.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/rig-time.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/seismic.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/setup.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/simulation.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/site.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/sites.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/slip.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/soak.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/sra.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/start.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/structure.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/tools.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/torque-drag.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/transients.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/vibration.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/well-report.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/help/well3d.md +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/helplib.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/housekeeping.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/hydraulics.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/interval.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/jobs.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/live_ports.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/loadtest.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/modbus.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/model_card.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/notify.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/oidc.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/oidc_mock.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/op_seismicity.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/opcua_port.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/operator_app.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/osdu.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/pad_summary.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/patches.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/pdf_report.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/penetrator.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/permits.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/piwebapi.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/pore_pressure.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/ports.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/ppdm.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/project.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/quakeml.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reconciler.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/WMM2025.COF +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/iscwsa_rev4_example.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/iscwsa_rev5_example.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/owsg_tool_models.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/report_history.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sample_record.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sbom.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seedlink.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/segy.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_array.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_film.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_response.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_track.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/seismic_watch.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/series_cache.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/service_life.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/shapefile.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/shell.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/shut_in.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/simulation.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/site_map.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sla_report.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/slip.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/soak.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/sra.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/store_forward.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/strata_join.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/structure.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/supervisor.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/survey_view.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/telemetry.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/tool_library.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/torque_drag.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/transient.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/vibration.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/well3d.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/well_assembler.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/well_report.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/wellplan.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/wellpos.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/wits0.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/witsml.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea/xlsx_read.py +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/SOURCES.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.59.0 → gea_program-0.60.0}/setup.cfg +0 -0
- {gea_program-0.59.0 → gea_program-0.60.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.60.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
|
|
@@ -673,6 +673,16 @@ and the decline terms. A well with no directional survey is read on the
|
|
|
673
673
|
drilling recorder's own Interpolated TVD where one is needed, labelled as
|
|
674
674
|
the recorder's interpolation. `gea help declare`.
|
|
675
675
|
|
|
676
|
+
### When one well cannot be reported
|
|
677
|
+
|
|
678
|
+
Refresh every report goes on past a well it cannot read: that well is
|
|
679
|
+
written down with why (`reports\wells\<well>\refresh_failed.json`), shown
|
|
680
|
+
NOT REPORTED on the overview and the printed dashboard, and named by the
|
|
681
|
+
refresh job and the month-end packet; every other well is reported. A
|
|
682
|
+
catalogue well is added only from an entry that carries a downhole gauge
|
|
683
|
+
series, with a well inside it and its gauge depth; a live seismic station
|
|
684
|
+
whose feed has written nothing yet is listed as having no recording yet.
|
|
685
|
+
|
|
676
686
|
### Hydraulics and ECD
|
|
677
687
|
|
|
678
688
|
The pressures the mud makes while it circulates, from the six-speed
|
|
@@ -634,6 +634,16 @@ and the decline terms. A well with no directional survey is read on the
|
|
|
634
634
|
drilling recorder's own Interpolated TVD where one is needed, labelled as
|
|
635
635
|
the recorder's interpolation. `gea help declare`.
|
|
636
636
|
|
|
637
|
+
### When one well cannot be reported
|
|
638
|
+
|
|
639
|
+
Refresh every report goes on past a well it cannot read: that well is
|
|
640
|
+
written down with why (`reports\wells\<well>\refresh_failed.json`), shown
|
|
641
|
+
NOT REPORTED on the overview and the printed dashboard, and named by the
|
|
642
|
+
refresh job and the month-end packet; every other well is reported. A
|
|
643
|
+
catalogue well is added only from an entry that carries a downhole gauge
|
|
644
|
+
series, with a well inside it and its gauge depth; a live seismic station
|
|
645
|
+
whose feed has written nothing yet is listed as having no recording yet.
|
|
646
|
+
|
|
637
647
|
### Hydraulics and ECD
|
|
638
648
|
|
|
639
649
|
The pressures the mud makes while it circulates, from the six-speed
|
|
@@ -3226,7 +3226,16 @@ def main(argv=None) -> int:
|
|
|
3226
3226
|
print(_json.dumps(ws.migrate_out_dir(a.from_dir, actor=a.actor), indent=1)); return 0
|
|
3227
3227
|
elif a.action == "refresh":
|
|
3228
3228
|
r = ws.refresh_dashboard(actor=a.actor, outages=a.outage, month=a.month, run_sat=a.sat)
|
|
3229
|
-
print("dashboard:", r.get("index") if isinstance(r, dict) else r)
|
|
3229
|
+
print("dashboard:", r.get("index") if isinstance(r, dict) else r)
|
|
3230
|
+
for wname, kinds in sorted(((r.get("reports") or {}) if isinstance(r, dict) else {}).items()):
|
|
3231
|
+
if "refresh_failed" in kinds:
|
|
3232
|
+
try:
|
|
3233
|
+
with open(os.path.join(ws.reports_dir, "wells", wname, "refresh_failed.json"), encoding="utf-8") as fh:
|
|
3234
|
+
why = _json.load(fh).get("reason", "")
|
|
3235
|
+
except (OSError, ValueError):
|
|
3236
|
+
why = ""
|
|
3237
|
+
print(f"well {wname}: its reports could not be written - {why} (the other wells were reported)")
|
|
3238
|
+
return 0
|
|
3230
3239
|
elif a.action == "audit":
|
|
3231
3240
|
for row in ws.audit_log(limit=50):
|
|
3232
3241
|
print(row["utc"], row["actor"], row["action"], _json.dumps(row["detail"]))
|
|
@@ -10472,8 +10472,9 @@ def section_cn_four(tmp: str) -> None:
|
|
|
10472
10472
|
t0 = now.replace(hour=10, minute=59, second=0) if now.hour < 23 else now.replace(hour=10, minute=59, second=0)
|
|
10473
10473
|
due_before = PS.due_now(ws, t0)
|
|
10474
10474
|
s1 = PS.write(ws, __version__, "tester", nt, now + timedelta(minutes=2))
|
|
10475
|
-
|
|
10476
|
-
|
|
10475
|
+
w1 = now + timedelta(minutes=2) # the summary's own day: two minutes on may already be tomorrow (UTC)
|
|
10476
|
+
due_after = PS.due_now(ws, w1.replace(hour=23, minute=0))
|
|
10477
|
+
nxt = (w1 + timedelta(days=1)).replace(hour=11, minute=1, second=0)
|
|
10477
10478
|
due_next = PS.due_now(ws, nxt)
|
|
10478
10479
|
ws.audit("tester", "test.after", {})
|
|
10479
10480
|
s2 = PS.write(ws, __version__, "tester", nt, now + timedelta(minutes=5), send=False)
|
|
@@ -13138,6 +13139,177 @@ def section_db_declarations_engineering(tmp: str) -> None:
|
|
|
13138
13139
|
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
13139
13140
|
|
|
13140
13141
|
|
|
13142
|
+
def section_dc_every_tab(tmp: str) -> None:
|
|
13143
|
+
"""Section DC - every tab and button answers: the Verification tab reads the site reports itself; the Rig time
|
|
13144
|
+
tab and the charts with a live well whose stream has not arrived; a catalogue well only from a gauge series, its
|
|
13145
|
+
well and its gauge depth; one well that cannot be read never stops the refresh or the month-end, and is shown
|
|
13146
|
+
with why; a live seismic station with no recording yet is a state, not a failure; a field left empty is answered
|
|
13147
|
+
in the form's words."""
|
|
13148
|
+
import http.cookiejar
|
|
13149
|
+
import urllib.error
|
|
13150
|
+
import urllib.request
|
|
13151
|
+
from . import dashboard as D
|
|
13152
|
+
from . import hydraulics as HY
|
|
13153
|
+
from . import month_end as ME
|
|
13154
|
+
from .service import Service, Users, friendly_error
|
|
13155
|
+
from .workspace import Workspace, WorkspaceError, catalog_gauge_wells, gauge_entries
|
|
13156
|
+
pkg = Path(__file__).parent
|
|
13157
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
13158
|
+
d = Path(tmp, "every_tab"); d.mkdir()
|
|
13159
|
+
hy = HY.selftest_files(str(d / "hy"))
|
|
13160
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
13161
|
+
w_edr = ws.add_well_file(hy["record"], display="HY-1", actor="tester")["id"]
|
|
13162
|
+
w_cat = ws.add_well_catalog("volve_f12_f14_production_excerpt", "15/9-F-12", 10000.0, actor="tester")["id"]
|
|
13163
|
+
|
|
13164
|
+
# -- DC1 a catalogue well: a gauge series, a well inside it, a gauge depth -------------------------------------------
|
|
13165
|
+
def answer(*a):
|
|
13166
|
+
try:
|
|
13167
|
+
ws.add_well_catalog(*a, actor="tester")
|
|
13168
|
+
return ""
|
|
13169
|
+
except WorkspaceError as e:
|
|
13170
|
+
return str(e)
|
|
13171
|
+
other = next(k for k in __import__("gea").CATALOG if k not in gauge_entries())
|
|
13172
|
+
m_series, m_empty = answer(other, "", 10000.0), answer("volve_f12_f14_production_excerpt", "", 10000.0)
|
|
13173
|
+
m_wrong, m_md0 = answer("volve_f12_f14_production_excerpt", "15/9-F-99", 10000.0), answer("volve_f12_f14_production_excerpt", "15/9-F-14", 0)
|
|
13174
|
+
m_none = answer("volve_f12_f14_production_excerpt", "15/9-F-14", None)
|
|
13175
|
+
n_wells = len(ws.wells())
|
|
13176
|
+
c1 = {
|
|
13177
|
+
"gauges": gauge_entries() == ["volve_f12_f14_production_excerpt"] and catalog_gauge_wells("volve_f12_f14_production_excerpt") == ["15/9-F-12", "15/9-F-14"]
|
|
13178
|
+
and catalog_gauge_wells(other) == [],
|
|
13179
|
+
"answered": "is not a downhole gauge series" in m_series and "volve_f12_f14_production_excerpt" in m_series
|
|
13180
|
+
and "choose the well inside" in m_empty and "15/9-F-14" in m_empty and "has no well '15/9-F-99'" in m_wrong
|
|
13181
|
+
and "greater than zero" in m_md0 and "the station MD is needed" in m_none,
|
|
13182
|
+
"nothing_added": n_wells == 2,
|
|
13183
|
+
"form": "entries.filter(e => (e.gauge_wells || []).length)" in page_src and '<select id="cWell">' in page_src
|
|
13184
|
+
and "Enter the station MD in feet" in page_src,
|
|
13185
|
+
}
|
|
13186
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
13187
|
+
ok(not failed1,
|
|
13188
|
+
"DC1 a catalogue well is added only from an entry that carries a downhole gauge series, naming a well inside it and a gauge depth above zero: "
|
|
13189
|
+
"an entry that is not a series, a well left empty or not in the entry, and a depth of zero or none are each answered with what to choose, and "
|
|
13190
|
+
"nothing is added; the form lists only the series and their wells" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
13191
|
+
|
|
13192
|
+
# -- DC2 one well that cannot be read: the refresh and the month-end go on, and say which and why ----------------------
|
|
13193
|
+
bad = ws._register({"id": "old_entry__", "display": "old entry", "kind": "catalog", "files": [],
|
|
13194
|
+
"source": {"entry": other, "well": ""}, "station_md_ft": 10000.0}, "tester")["id"] # as an older version could add it
|
|
13195
|
+
res = ws.refresh_dashboard(actor="tester")
|
|
13196
|
+
rep = res.get("reports") or {}
|
|
13197
|
+
data = D.collect(ws.reports_dir)
|
|
13198
|
+
failed_row = next((w for w in data["wells"] if w["slug"].startswith(other)), {})
|
|
13199
|
+
me = ME.assemble(ws, "2026-09", actor="tester")
|
|
13200
|
+
me_err = " | ".join(me.get("errors") or [])
|
|
13201
|
+
cat_name = f"volve_f12_f14_production_excerpt__15/9-F-12".replace("/", "-")
|
|
13202
|
+
c2 = {
|
|
13203
|
+
"others_reported": "drift" in rep.get(cat_name, []) and "drilling_record" in rep.get(w_edr, []),
|
|
13204
|
+
"failed_named": any("refresh_failed" in v for k, v in rep.items() if k.startswith(other))
|
|
13205
|
+
and os.path.isfile(os.path.join(ws.reports_dir, "wells", f"{other}__", "refresh_failed.json")),
|
|
13206
|
+
"shown": bool(failed_row.get("failed")) and D._drift_status(failed_row)[1].startswith("NOT REPORTED"),
|
|
13207
|
+
"month_end": me.get("included", 0) > 0 and f"well {other}__" in me_err and "dashboard refresh" not in me_err,
|
|
13208
|
+
"page": "w.failed ? statusPill('serious', 'not reported')" in page_src,
|
|
13209
|
+
}
|
|
13210
|
+
ws.remove_well(bad, actor="tester")
|
|
13211
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
13212
|
+
ok(not failed2,
|
|
13213
|
+
"DC2 a well that cannot be read (a catalogue entry an older version accepted) never stops the refresh: every other well is reported, the "
|
|
13214
|
+
"failed one is written down with why and shown NOT REPORTED on the overview; the month-end packet is assembled and names that one well"
|
|
13215
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
13216
|
+
|
|
13217
|
+
# -- DC3 a live well with no stream yet, a live station with no recording yet ------------------------------------------
|
|
13218
|
+
from .wits0 import EXAMPLE_CONFIG as _WITS
|
|
13219
|
+
wits_cfg = d / "wits0.json"
|
|
13220
|
+
wits_cfg.write_text(json.dumps(_WITS), encoding="utf-8")
|
|
13221
|
+
w_live = ws.add_well_live("Rig feed", "wits0", str(wits_cfg), actor="tester")["id"]
|
|
13222
|
+
st = ws.add_live_station("Pad 3 seismometer", {"host": "127.0.0.1", "port": 18000, "streams": [{"network": "XX", "station": "PAD3"}]}, actor="tester")
|
|
13223
|
+
try:
|
|
13224
|
+
ws.refresh_seismic(st["id"], actor="tester")
|
|
13225
|
+
m_seis = ""
|
|
13226
|
+
except WorkspaceError as e:
|
|
13227
|
+
m_seis = str(e)
|
|
13228
|
+
me2 = ME.assemble(ws, "2026-09", actor="tester", refresh=True)
|
|
13229
|
+
with open(os.path.splitext(me2["index"])[0] + ".json", encoding="utf-8") as fh_:
|
|
13230
|
+
me2_items = json.load(fh_).get("items") or []
|
|
13231
|
+
seis_item = next((i for i in me2_items if i["kind"] == "seismic" and i["id"] == st["id"]), {})
|
|
13232
|
+
|
|
13233
|
+
Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
13234
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
13235
|
+
base_url = svc.url.rstrip("/")
|
|
13236
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
13237
|
+
|
|
13238
|
+
def call(method, path, body=None):
|
|
13239
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
13240
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
13241
|
+
try:
|
|
13242
|
+
with o.open(rq, timeout=120) as rr_:
|
|
13243
|
+
return rr_.status, json.loads(rr_.read())
|
|
13244
|
+
except urllib.error.HTTPError as e:
|
|
13245
|
+
return e.code, json.loads(e.read())
|
|
13246
|
+
try:
|
|
13247
|
+
call("POST", "/api/login", {"name": "op", "password": "op-pass-123"})
|
|
13248
|
+
st_rig, rig = call("GET", "/api/rig-time")
|
|
13249
|
+
st_cm, cm = call("GET", f"/api/wells/{w_live}/chart/meta")
|
|
13250
|
+
st_cx, _ = call("GET", "/api/wells/no-such-well/chart/meta")
|
|
13251
|
+
st_cat, cat = call("GET", "/api/catalog")
|
|
13252
|
+
st_add, add = call("POST", "/api/wells/catalog", {"entry": other, "well": "", "station_md": 10000})
|
|
13253
|
+
st_pp, pp = call("POST", "/api/pore-pressure/measure", {"well_id": w_edr, "depth_m": "", "value": "", "unit": "psi", "kind": "pore", "source": "x"})
|
|
13254
|
+
st_cf, cf = call("GET", "/api/config/")
|
|
13255
|
+
st_cd, cd = call("GET", "/api/config/defaults/")
|
|
13256
|
+
st_fw, fw = call("POST", "/api/files/watch", {"root": ""})
|
|
13257
|
+
st_sl, sl = call("GET", "/api/seismic")
|
|
13258
|
+
st_tp, tp = call("POST", f"/api/wells/{w_edr}/transient-params", {"params": {"mu_cp": 0.8}})
|
|
13259
|
+
st_tp0, tp0 = call("POST", f"/api/wells/{w_edr}/transient-params", {"params": {}})
|
|
13260
|
+
finally:
|
|
13261
|
+
svc.stop()
|
|
13262
|
+
gauges_api = {e["entry"]: e["gauge_wells"] for e in cat.get("entries", []) if e.get("gauge_wells")}
|
|
13263
|
+
c3 = {
|
|
13264
|
+
"seismic": "has no recording yet" in m_seis and seis_item.get("status") == "NOT AVAILABLE" and "no recording yet" in seis_item.get("reason", "")
|
|
13265
|
+
and not any(st["id"] in e for e in me2.get("errors") or []),
|
|
13266
|
+
"rig_time": st_rig == 200 and isinstance(rig, dict),
|
|
13267
|
+
"chart": st_cm == 200 and cm.get("available") is False and bool(cm.get("reason")) and st_cx == 404
|
|
13268
|
+
and "meta.available === false" in page_src,
|
|
13269
|
+
"catalog_api": st_cat == 200 and gauges_api == {"volve_f12_f14_production_excerpt": ["15/9-F-12", "15/9-F-14"]}
|
|
13270
|
+
and st_add == 400 and "is not a downhole gauge series" in add.get("error", ""),
|
|
13271
|
+
"site_reports": st_sl == 200 and "site_reports" in sl,
|
|
13272
|
+
}
|
|
13273
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
13274
|
+
ok(not failed3,
|
|
13275
|
+
"DC3 a live seismic station whose feed has written nothing is answered as having no recording yet and is NOT AVAILABLE in the month-end, not "
|
|
13276
|
+
"a failure; the Rig time tab answers with a live well whose stream has not arrived; that well's chart says there is nothing to chart yet "
|
|
13277
|
+
"(an unknown well is still not found); the catalogue API names each series' wells and answers a well that is not one with what to choose"
|
|
13278
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
13279
|
+
|
|
13280
|
+
# -- DC4 the form's words, and the Verification tab's own data -------------------------------------------------------------
|
|
13281
|
+
def _tls_msg():
|
|
13282
|
+
from . import tls as TLS
|
|
13283
|
+
try:
|
|
13284
|
+
TLS.use_site_certificate(ws, "tester", "", "")
|
|
13285
|
+
return ""
|
|
13286
|
+
except ValueError as e:
|
|
13287
|
+
return str(e)
|
|
13288
|
+
verify = page_src[page_src.index("VIEWS.verify = async"):]
|
|
13289
|
+
verify = verify[:verify.index("\nVIEWS.", 10)] if "\nVIEWS." in verify[10:] else verify
|
|
13290
|
+
c4 = {
|
|
13291
|
+
"words": friendly_error("could not convert string to float: ''") == "a number is needed: a field was left empty"
|
|
13292
|
+
and friendly_error("could not convert string to float: 'abc'") == "'abc' is not a number"
|
|
13293
|
+
and friendly_error("no such well") == "no such well",
|
|
13294
|
+
"pore": st_pp == 400 and pp.get("error") == "a number is needed: a field was left empty",
|
|
13295
|
+
"config": st_cf == 404 and "choose a configuration by name first" in cf.get("error", "")
|
|
13296
|
+
and st_cd == 404 and "choose a configuration by name first" in cd.get("error", "")
|
|
13297
|
+
and page_src.count("if (!$('#cfgName').value.trim()) return toast('Choose a configuration by name first', 'err');") == 3,
|
|
13298
|
+
"watch": st_fw == 400 and "add an import root under Administration first" in fw.get("error", "")
|
|
13299
|
+
and "w.title = 'Add an import root under Administration first'" in page_src,
|
|
13300
|
+
"params": st_tp == 200 and st_tp0 == 200 and "unknown parameter" not in json.dumps(tp0),
|
|
13301
|
+
"tls_path": _tls_msg().startswith("give the certificate file's full path"),
|
|
13302
|
+
"verify": "let siteReports = {}; try { siteReports = (await get('/api/seismic')).site_reports || {}; }" in verify
|
|
13303
|
+
and verify.index("let siteReports") < verify.index("siteReports.seismic_dataset_report"),
|
|
13304
|
+
}
|
|
13305
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
13306
|
+
ok(not failed4,
|
|
13307
|
+
"DC4 a field left empty, or text where a number goes, is answered in the form's words rather than the program's; a configuration button with "
|
|
13308
|
+
"no name chosen, a folder watch with no import root and the site's certificate with no path given say what to do first; a well's transient "
|
|
13309
|
+
"parameters saved empty are cleared; the Verification tab reads the site reports itself"
|
|
13310
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
13311
|
+
|
|
13312
|
+
|
|
13141
13313
|
def PR_read_ok(path) -> bool:
|
|
13142
13314
|
from . import pdf_report as PR
|
|
13143
13315
|
try:
|
|
@@ -13255,6 +13427,7 @@ def main() -> int:
|
|
|
13255
13427
|
section_cz_it_review(tmp)
|
|
13256
13428
|
section_da_hydraulics(tmp)
|
|
13257
13429
|
section_db_declarations_engineering(tmp)
|
|
13430
|
+
section_dc_every_tab(tmp)
|
|
13258
13431
|
if _FAILS:
|
|
13259
13432
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
13260
13433
|
for f in _FAILS:
|
|
@@ -63,6 +63,9 @@ def collect(out_dir: str) -> dict:
|
|
|
63
63
|
continue
|
|
64
64
|
meta = _load(os.path.join(d, 'well.json')) or {}
|
|
65
65
|
w = {'name': meta.get('display', name), 'slug': name, 'dir': d, 'source': meta.get('source', '')}
|
|
66
|
+
rf = _load(os.path.join(d, 'refresh_failed.json'))
|
|
67
|
+
if rf or meta.get('ingest_failed'): # a well whose reports could not be written is shown, with why
|
|
68
|
+
w['failed'] = (rf or {}).get('reason') or meta.get('ingest_failed')
|
|
66
69
|
gd = _load(os.path.join(d, 'gauge_drift_report.json'))
|
|
67
70
|
if gd:
|
|
68
71
|
ev = gd.get('evaluation', {})
|
|
@@ -157,6 +160,8 @@ def _sev(cls: str) -> int:
|
|
|
157
160
|
|
|
158
161
|
def _drift_status(w: dict) -> tuple:
|
|
159
162
|
d = w.get('drift')
|
|
163
|
+
if not d and w.get('failed'):
|
|
164
|
+
return 'critical', 'NOT REPORTED - ' + str(w['failed'])[:160]
|
|
160
165
|
if not d:
|
|
161
166
|
if w.get('drilling'):
|
|
162
167
|
dl = w['drilling']
|
|
@@ -369,142 +374,158 @@ def orchestrate(out_dir: str, catalog_wells: List[dict], file_wells: List[dict],
|
|
|
369
374
|
name = f"{cw['entry']}__{cw['well']}".replace('/', '-')
|
|
370
375
|
d = os.path.join(out_dir, 'wells', name)
|
|
371
376
|
os.makedirs(d, exist_ok=True)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
377
|
+
if os.path.exists(os.path.join(d, 'refresh_failed.json')):
|
|
378
|
+
os.remove(os.path.join(d, 'refresh_failed.json'))
|
|
379
|
+
try:
|
|
380
|
+
stream, smap = production_live_stream(cw['entry'], cw['well'], cw['md_ft'])
|
|
381
|
+
with open(os.path.join(d, 'well.json'), 'w', encoding='utf-8') as f:
|
|
382
|
+
json.dump({'display': cw['well'], 'source': f"{cw['entry']} (station MD {cw['md_ft']:g} ft, caller-supplied)"}, f)
|
|
383
|
+
ms = None
|
|
384
|
+
if monitor_root:
|
|
385
|
+
mon = DriftMonitor(cfg, os.path.join(monitor_root, name), well_name=cw['well'])
|
|
386
|
+
r = mon.run_scheduled(stream, station_map=smap, force=True)
|
|
387
|
+
ev = r['evaluation']
|
|
388
|
+
ms = mon.status(); ms['history'] = mon.history(); ms['change_log'] = mon.change_log()
|
|
389
|
+
else:
|
|
390
|
+
ev = Reconciler(cfg).reconcile(stream, station_map=smap)
|
|
391
|
+
write(gauge_drift_report(ev, stream, well_name=cw['well'], program_version=_v, gauge_spec=gauge_spec, monitor_status=ms), d)
|
|
392
|
+
made.setdefault(name, []).append('drift')
|
|
393
|
+
crit = load_criteria(criteria_path)
|
|
394
|
+
src = CATALOG[cw['entry']].stream()
|
|
395
|
+
det = WellTestValidator(crit, volve_channel_map(cw['well'])).detect(src)
|
|
396
|
+
trail = ApprovalTrail(os.path.join(d, 'well_test_records'), levels=crit.get('approval_levels'))
|
|
397
|
+
write(well_test_report(det, approvals=trail, well_name=cw['well'], program_version=_v), d, basename='well_test_validation')
|
|
398
|
+
made[name].append('well_tests')
|
|
399
|
+
except Exception as ex: # one well that cannot be read never blocks the other wells' reports
|
|
400
|
+
with open(os.path.join(d, 'refresh_failed.json'), 'w', encoding='utf-8') as f:
|
|
401
|
+
json.dump({'well': name, 'entry': cw.get('entry'), 'reason': f'{type(ex).__name__}: {ex}',
|
|
402
|
+
'evaluated_at_utc': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}, f, indent=1)
|
|
403
|
+
made.setdefault(name, []).append('refresh_failed')
|
|
391
404
|
for fw in file_wells:
|
|
392
405
|
name = fw.get('name') or os.path.splitext(os.path.basename(fw['path']))[0]
|
|
393
406
|
d = os.path.join(out_dir, 'wells', name)
|
|
394
407
|
os.makedirs(d, exist_ok=True)
|
|
395
|
-
|
|
408
|
+
if os.path.exists(os.path.join(d, 'refresh_failed.json')):
|
|
409
|
+
os.remove(os.path.join(d, 'refresh_failed.json'))
|
|
396
410
|
try:
|
|
397
|
-
|
|
398
|
-
except Exception as ex: # one unreadable file never blocks the other wells' reports
|
|
399
|
-
with open(os.path.join(d, 'well.json'), 'w', encoding='utf-8') as f:
|
|
400
|
-
json.dump({'display': name, 'source': fw['path'], 'ingest_failed': f'{type(ex).__name__}: {ex}'}, f)
|
|
401
|
-
made.setdefault(name, []).append('ingest_failed')
|
|
402
|
-
continue
|
|
403
|
-
with open(os.path.join(d, 'well.json'), 'w', encoding='utf-8') as f:
|
|
404
|
-
json.dump({'display': name, 'source': fw['path'], 'index_kind': stream.index_kind, 'source_format': stream.source_format}, f)
|
|
405
|
-
cache_dir = os.path.join(series_cache_root, name) if series_cache_root else None
|
|
406
|
-
if stream.source_format == 'edr': # a drilling recorder's export: the drilling record, not the gauge pipeline
|
|
407
|
-
from .edr import write_record
|
|
408
|
-
try:
|
|
409
|
-
write_record(stream, d, _v, cache_dir=cache_dir)
|
|
410
|
-
made.setdefault(name, []).append('drilling_record')
|
|
411
|
-
except Exception as ex:
|
|
412
|
-
with open(os.path.join(d, 'drilling_record_not_written.json'), 'w', encoding='utf-8') as f:
|
|
413
|
-
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}'}, f, indent=1)
|
|
414
|
-
made.setdefault(name, []).append('drilling_record_not_written')
|
|
415
|
-
continue
|
|
416
|
-
if cache_dir:
|
|
411
|
+
from .files import read_any
|
|
417
412
|
try:
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
413
|
+
stream = read_any(fw['path'])
|
|
414
|
+
except Exception as ex: # one unreadable file never blocks the other wells' reports
|
|
415
|
+
with open(os.path.join(d, 'well.json'), 'w', encoding='utf-8') as f:
|
|
416
|
+
json.dump({'display': name, 'source': fw['path'], 'ingest_failed': f'{type(ex).__name__}: {ex}'}, f)
|
|
417
|
+
made.setdefault(name, []).append('ingest_failed')
|
|
418
|
+
continue
|
|
419
|
+
with open(os.path.join(d, 'well.json'), 'w', encoding='utf-8') as f:
|
|
420
|
+
json.dump({'display': name, 'source': fw['path'], 'index_kind': stream.index_kind, 'source_format': stream.source_format}, f)
|
|
421
|
+
cache_dir = os.path.join(series_cache_root, name) if series_cache_root else None
|
|
422
|
+
if stream.source_format == 'edr': # a drilling recorder's export: the drilling record, not the gauge pipeline
|
|
423
|
+
from .edr import write_record
|
|
424
|
+
try:
|
|
425
|
+
write_record(stream, d, _v, cache_dir=cache_dir)
|
|
426
|
+
made.setdefault(name, []).append('drilling_record')
|
|
427
|
+
except Exception as ex:
|
|
428
|
+
with open(os.path.join(d, 'drilling_record_not_written.json'), 'w', encoding='utf-8') as f:
|
|
429
|
+
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}'}, f, indent=1)
|
|
430
|
+
made.setdefault(name, []).append('drilling_record_not_written')
|
|
431
|
+
continue
|
|
432
|
+
if cache_dir:
|
|
433
|
+
try:
|
|
434
|
+
from . import series_cache as _SCH
|
|
435
|
+
_SCH.build(stream, cache_dir)
|
|
436
|
+
except Exception:
|
|
437
|
+
pass # the cache is a convenience; the page builds it on first view
|
|
438
|
+
if stream.index_kind != 'time_s': # a depth log is a survey input: run the strata survey, not the gauge pipeline
|
|
439
|
+
try:
|
|
440
|
+
from .survey_cmd import run_survey
|
|
441
|
+
txt, machine = run_survey(path=fw['path'])
|
|
442
|
+
with open(os.path.join(d, 'survey_report.txt'), 'w', encoding='utf-8') as f:
|
|
443
|
+
f.write(txt)
|
|
444
|
+
with open(os.path.join(d, 'survey.json'), 'w', encoding='utf-8') as f:
|
|
445
|
+
json.dump(machine, f, indent=1, default=str)
|
|
446
|
+
made.setdefault(name, []).append('survey')
|
|
447
|
+
except Exception as ex:
|
|
448
|
+
with open(os.path.join(d, 'survey_not_run.json'), 'w', encoding='utf-8') as f:
|
|
449
|
+
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}', 'index_kind': stream.index_kind}, f, indent=1)
|
|
450
|
+
made.setdefault(name, []).append('survey_not_run')
|
|
451
|
+
continue
|
|
452
|
+
cat = TagCatalogue.from_stream(stream, gauge_spec=gauge_spec)
|
|
453
|
+
# instruments: swaps segment the fit; certificates print beside the bias (Band 2)
|
|
454
|
+
inst = None
|
|
455
|
+
rdir = fw.get('records_dir')
|
|
456
|
+
fit_stream = stream
|
|
457
|
+
if rdir and os.path.isdir(rdir):
|
|
458
|
+
from .sensor_swap import SwapRegister, segment_mask, detect_swaps
|
|
459
|
+
from .certificates import CertificateRegister
|
|
460
|
+
reg = SwapRegister(os.path.join(rdir, 'sensor_swaps.jsonl'))
|
|
461
|
+
fit_stream, notes = segment_mask(stream, reg)
|
|
462
|
+
latest = reg.latest_by_tag()
|
|
463
|
+
certs = CertificateRegister(os.path.join(rdir, 'certificates.jsonl'))
|
|
464
|
+
ptags = [c for c in stream.channels if c.lower().startswith('p_')]
|
|
465
|
+
cert_rows = certs.status(ptags, serial_by_tag={k: v['new_serial'] for k, v in latest.items()}) if certs.list() else []
|
|
466
|
+
try:
|
|
467
|
+
def _unrecorded(c):
|
|
468
|
+
for tg in [c['tag_id']] + [x.strip() for x in (c.get('ambiguous_with') or '').split(',') if x.strip()]:
|
|
469
|
+
e = latest.get(tg)
|
|
470
|
+
if e and abs(_epoch_s(e['swap_utc']) - _epoch_s(c.get('swap_utc'))) <= 2 * 3600 * 24:
|
|
471
|
+
return False # this step is explained by a recorded swap (on it or on its ambiguous peer)
|
|
472
|
+
return True
|
|
473
|
+
cands = [c for c in detect_swaps(stream) if _unrecorded(c)]
|
|
474
|
+
except Exception:
|
|
475
|
+
cands = []
|
|
476
|
+
inst = {'swaps': reg.list(), 'swap_notes': notes, 'certificates': cert_rows, 'candidates': cands[:10],
|
|
477
|
+
'certificate_summary': CertificateRegister.summary(cert_rows) if cert_rows else None}
|
|
478
|
+
with open(os.path.join(d, 'instruments.json'), 'w', encoding='utf-8') as f:
|
|
479
|
+
json.dump(inst, f, indent=1, default=str)
|
|
423
480
|
try:
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
with open(os.path.join(d, '
|
|
429
|
-
json.dump(
|
|
430
|
-
made.setdefault(name, []).append('
|
|
431
|
-
|
|
432
|
-
with open(os.path.join(d, 'survey_not_run.json'), 'w', encoding='utf-8') as f:
|
|
433
|
-
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}', 'index_kind': stream.index_kind}, f, indent=1)
|
|
434
|
-
made.setdefault(name, []).append('survey_not_run')
|
|
435
|
-
continue
|
|
436
|
-
cat = TagCatalogue.from_stream(stream, gauge_spec=gauge_spec)
|
|
437
|
-
# instruments: swaps segment the fit; certificates print beside the bias (Band 2)
|
|
438
|
-
inst = None
|
|
439
|
-
rdir = fw.get('records_dir')
|
|
440
|
-
fit_stream = stream
|
|
441
|
-
if rdir and os.path.isdir(rdir):
|
|
442
|
-
from .sensor_swap import SwapRegister, segment_mask, detect_swaps
|
|
443
|
-
from .certificates import CertificateRegister
|
|
444
|
-
reg = SwapRegister(os.path.join(rdir, 'sensor_swaps.jsonl'))
|
|
445
|
-
fit_stream, notes = segment_mask(stream, reg)
|
|
446
|
-
latest = reg.latest_by_tag()
|
|
447
|
-
certs = CertificateRegister(os.path.join(rdir, 'certificates.jsonl'))
|
|
448
|
-
ptags = [c for c in stream.channels if c.lower().startswith('p_')]
|
|
449
|
-
cert_rows = certs.status(ptags, serial_by_tag={k: v['new_serial'] for k, v in latest.items()}) if certs.list() else []
|
|
481
|
+
ev = Reconciler(cfg).reconcile(fit_stream)
|
|
482
|
+
write(gauge_drift_report(ev, stream, catalogue=cat, well_name=name, program_version=_v, gauge_spec=gauge_spec, instruments=inst), d)
|
|
483
|
+
made.setdefault(name, []).append('drift')
|
|
484
|
+
except ValueError as e: # a stream with no downhole gauge stations (a drill-floor feed): drift does not apply
|
|
485
|
+
with open(os.path.join(d, 'drift_not_applicable.json'), 'w', encoding='utf-8') as f:
|
|
486
|
+
json.dump({'well': name, 'reason': str(e), 'channels': list(stream.channels), 'evaluated_at_utc': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}, f, indent=1)
|
|
487
|
+
made.setdefault(name, []).append('drift_not_applicable')
|
|
488
|
+
# shut-ins and the build-up analyses (Band 2): criteria and parameters from the records folder when present
|
|
450
489
|
try:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
write(transient_report(det, analyses, well_name=name, program_version=_v), d, basename='pressure_transient_report')
|
|
491
|
-
made.setdefault(name, []).append('transient')
|
|
492
|
-
else:
|
|
493
|
-
with open(os.path.join(d, 'shut_ins.json'), 'w', encoding='utf-8') as f:
|
|
494
|
-
json.dump(det, f, indent=1, default=str)
|
|
495
|
-
except Exception as ex: # the transient look is additional; it never blocks the alarms
|
|
496
|
-
with open(os.path.join(d, 'transient_not_run.json'), 'w', encoding='utf-8') as f:
|
|
497
|
-
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}'}, f, indent=1)
|
|
498
|
-
recs = records_from_stream(stream, cat)
|
|
499
|
-
eng = AlarmEngine(defaults_from_catalogue(cat), event_log_path=os.path.join(d, 'alarm_events.jsonl'))
|
|
500
|
-
eng.process(recs)
|
|
501
|
-
write(alarm_event_report(eng, well_name=name, program_version=_v), d, basename='alarm_event_report')
|
|
502
|
-
made[name].append('alarms')
|
|
503
|
-
if outages:
|
|
504
|
-
cad = next((t.cadence_s for t in cat.tags.values() if t.cadence_s), 60.0)
|
|
505
|
-
sim = simulate([r for r in recs if r.tag_id.startswith('P_raw')], parse_outages(outages), BufferConfig(cadence_s=cad))
|
|
506
|
-
write(data_resilience_report(sim, site_name=name, program_version=_v), d, basename='data_resilience_report')
|
|
507
|
-
made[name].append('resilience')
|
|
490
|
+
from .shut_in import detect_shut_ins, extract_buildup, load_criteria as _si_crit
|
|
491
|
+
from .transient import analyze_buildup
|
|
492
|
+
from .client_reports import transient_report
|
|
493
|
+
si_c = _si_crit(os.path.join(rdir, 'shut_in.json') if rdir else None)
|
|
494
|
+
det = detect_shut_ins(stream, criteria=si_c)
|
|
495
|
+
prm = None
|
|
496
|
+
if rdir and os.path.isfile(os.path.join(rdir, 'transient_params.json')):
|
|
497
|
+
with open(os.path.join(rdir, 'transient_params.json'), encoding='utf-8') as f:
|
|
498
|
+
prm = json.load(f)
|
|
499
|
+
analyses = []
|
|
500
|
+
for si in [x for x in det['shut_ins'] if x['qualified']][:6]:
|
|
501
|
+
b = extract_buildup(stream, si)
|
|
502
|
+
a = analyze_buildup(b['dt_h'], b['p_ws'], b['tp_h'], b['p_wf'], prm)
|
|
503
|
+
a['shut_in_id'] = si['shut_in_id']
|
|
504
|
+
analyses.append(a)
|
|
505
|
+
if det['shut_ins']:
|
|
506
|
+
write(transient_report(det, analyses, well_name=name, program_version=_v), d, basename='pressure_transient_report')
|
|
507
|
+
made.setdefault(name, []).append('transient')
|
|
508
|
+
else:
|
|
509
|
+
with open(os.path.join(d, 'shut_ins.json'), 'w', encoding='utf-8') as f:
|
|
510
|
+
json.dump(det, f, indent=1, default=str)
|
|
511
|
+
except Exception as ex: # the transient look is additional; it never blocks the alarms
|
|
512
|
+
with open(os.path.join(d, 'transient_not_run.json'), 'w', encoding='utf-8') as f:
|
|
513
|
+
json.dump({'well': name, 'reason': f'{type(ex).__name__}: {ex}'}, f, indent=1)
|
|
514
|
+
recs = records_from_stream(stream, cat)
|
|
515
|
+
eng = AlarmEngine(defaults_from_catalogue(cat), event_log_path=os.path.join(d, 'alarm_events.jsonl'))
|
|
516
|
+
eng.process(recs)
|
|
517
|
+
write(alarm_event_report(eng, well_name=name, program_version=_v), d, basename='alarm_event_report')
|
|
518
|
+
made[name].append('alarms')
|
|
519
|
+
if outages:
|
|
520
|
+
cad = next((t.cadence_s for t in cat.tags.values() if t.cadence_s), 60.0)
|
|
521
|
+
sim = simulate([r for r in recs if r.tag_id.startswith('P_raw')], parse_outages(outages), BufferConfig(cadence_s=cad))
|
|
522
|
+
write(data_resilience_report(sim, site_name=name, program_version=_v), d, basename='data_resilience_report')
|
|
523
|
+
made[name].append('resilience')
|
|
524
|
+
except Exception as ex: # one well that cannot be reported never blocks the other wells' reports
|
|
525
|
+
with open(os.path.join(d, 'refresh_failed.json'), 'w', encoding='utf-8') as f:
|
|
526
|
+
json.dump({'well': name, 'source': fw.get('path'), 'reason': f'{type(ex).__name__}: {ex}',
|
|
527
|
+
'evaluated_at_utc': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}, f, indent=1)
|
|
528
|
+
made.setdefault(name, []).append('refresh_failed')
|
|
508
529
|
write(accuracy_statement_report(library_backtest(), program_version=_v), out_dir, basename='accuracy_statement')
|
|
509
530
|
cards = build_cards()
|
|
510
531
|
for c in cards:
|
|
@@ -232,6 +232,8 @@ class Roots:
|
|
|
232
232
|
"""An absolute path inside the named root, or WorkspaceError when it would leave it."""
|
|
233
233
|
root = next((r for r in self.list(which) if r['name'] == name), None)
|
|
234
234
|
if root is None:
|
|
235
|
+
if not name:
|
|
236
|
+
raise WorkspaceError(f'no {which} root chosen' + ('' if self.list(which) else f': add an {which} root under Administration first'))
|
|
235
237
|
raise WorkspaceError(f'no {which} root named {name}')
|
|
236
238
|
base = os.path.realpath(root['path'])
|
|
237
239
|
full = os.path.realpath(os.path.join(base, rel or ''))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> The program ships 52 public datasets, each with a provenance sidecar saying where it came from, under what licence, and what it covers. A sidecar does not say what in this program reads the entry; the register does. `gea catalog --list` prints every entry with the bands, commands and reports that take it, or the plain statement that nothing does yet - CARRIED, NO BAND - and the gate holds that register to the code. The Catalogue tab on the dashboard is the world map of them: where each entry was drilled, taken from the entry's own file (a PANGAEA event line, a LAS header, a KTB Gauss-Krueger grid reference unprojected and shifted to WGS84) or from the sidecar's source record, or NO POSITION - nothing is looked up or guessed. 44 of the 52 are positioned, on 33 marks; the 8 that are not are listed with what their files do say. The entries with a use: every LAS log runs through the strata survey; seven measured-friction and strength datasets become the envelope a slip statement discloses its coefficient against; the Volve core checks the published sandstone range.
|
|
4
4
|
|
|
5
|
-
**The command**: `gea catalog --list` (the register), `--unused` (the carried entries, with the reason), `--check` (every entry read through its own reader, the shape or the error), `--entry volve_f12_f14_production_excerpt --info` (the provenance in full and how it reads), `--entry volve_f12_f14_production_excerpt --stream-csv out.csv` (the entry as a stream file any other command ingests), `--friction` (the measured friction and strength, each with its source, and the envelope against Byerlee's 0.6), `--core` (the Volve 15/9-19 A core plugs against the published sandstone range). `--positions` (where every entry was drilled, how that is known, or NO POSITION), `--map catalogue.svg` (the world map as a file: the Natural Earth 1:110m coastline, one numbered mark per position, the key, the NO POSITION list), `--check-positions` (the sidecars' recorded positions against a fresh read of the files - the gate's check; `tools/catalog_positions.py` rewrites them after a catalogue change). `gea survey --catalog collingwood_1_28_ks_complete` runs the strata survey on any LAS entry - Scorpio E1 (South Australia), University 6-17 (Texas), Kennetcook #2 (Nova Scotia), Collingwood 1-28 (Kansas), L07-01 (Dutch North Sea), 15/9-19 SR (Volve) - and says what it found or why it did not estimate.
|
|
5
|
+
**The command**: `gea catalog --list` (the register), `--unused` (the carried entries, with the reason), `--check` (every entry read through its own reader, the shape or the error), `--entry volve_f12_f14_production_excerpt --info` (the provenance in full and how it reads), `--entry volve_f12_f14_production_excerpt --stream-csv out.csv` (the entry as a stream file any other command ingests), `--friction` (the measured friction and strength, each with its source, and the envelope against Byerlee's 0.6), `--core` (the Volve 15/9-19 A core plugs against the published sandstone range). `--positions` (where every entry was drilled, how that is known, or NO POSITION), `--map catalogue.svg` (the world map as a file: the Natural Earth 1:110m coastline, one numbered mark per position, the key, the NO POSITION list), `--check-positions` (the sidecars' recorded positions against a fresh read of the files - the gate's check; `tools/catalog_positions.py` rewrites them after a catalogue change). `gea survey --catalog collingwood_1_28_ks_complete` runs the strata survey on any LAS entry - Scorpio E1 (South Australia), University 6-17 (Texas), Kennetcook #2 (Nova Scotia), Collingwood 1-28 (Kansas), L07-01 (Dutch North Sea), 15/9-19 SR (Volve) - and says what it found or why it did not estimate. A catalogue well (Wells -> Add a catalogue well, or `gea workspace --path C:\site --action add-catalog --entry volve_f12_f14_production_excerpt --well 15/9-F-12 --station-md 10000`) is added only from an entry that carries a downhole gauge series, naming a well inside it and the gauge depth from the completion records; the form lists only those entries.
|
|
6
6
|
|
|
7
7
|
**What it writes**: each sidecar's `position` block - status, latitude, longitude, datum, the source line it was read from, and for a Gauss-Krueger reference the grid values, the DHDN position and the shift applied; the map SVG; the register - entry, kind, tier, file size, USED or CARRIED, and each use in the user's terms; the check - 52 of 52 entries read; the friction envelope - friction coefficients 0.21 to 0.90 on 70 measurements from three datasets (ODP Costa Rica, IODP Hikurangi, IODP Nankai), cohesion 188-990 kPa (Sumatra), vane shear 19.5-290 kPa (Barbados), fault-zone peak shear strength 1.7-1.9 MPa at slow slip (JFAST), uniaxial compressive strength 3-265 MPa on crystalline cores (KTB pilot hole) - none from the Permian, and the page says so; the core check - bulk density per plug as grain density x (1 - porosity) + brine x porosity, the brine stated, 65 of 70 Volve plugs inside the published 2.05-2.55 g/cc sandstone range, written into the rock inventory model card's evaluation.
|
|
8
8
|
|