gea-program 0.47.0__tar.gz → 0.49.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.47.0 → gea_program-0.49.0}/PKG-INFO +30 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/README.md +29 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/__init__.py +1 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/__main__.py +63 -3
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/acceptance_tests.py +385 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/alerts.py +20 -2
- gea_program-0.49.0/gea/collision.py +344 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/declarations.py +136 -2
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/dossier.py +36 -2
- gea_program-0.49.0/gea/geomag.py +122 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/alerts.md +1 -1
- gea_program-0.49.0/gea/help/collision.md +19 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/declare.md +2 -2
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/dossier.md +2 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/invoice-check.md +1 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/pad-summary.md +1 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/rig-time.md +3 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/helplib.py +2 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/invoice_check.py +25 -7
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/notify.py +3 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/pad_summary.py +41 -4
- gea_program-0.49.0/gea/reference/WMM2025.COF +93 -0
- gea_program-0.49.0/gea/reference/iscwsa_rev4_example.json +1751 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/rig_state.py +144 -8
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/service.py +31 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/web/app.html +62 -8
- gea_program-0.49.0/gea/wellpos.py +286 -0
- gea_program-0.49.0/gea/xlsx_read.py +205 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/PKG-INFO +30 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/SOURCES.txt +7 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/pyproject.toml +1 -1
- {gea_program-0.47.0 → gea_program-0.49.0}/LICENSE +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/backup.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/bench.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/blind_harness.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog_map.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/catalog_register.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/certificates.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/cli.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/client_reports.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/config_versioning.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/conformance.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/control_room.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/correlation.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/dashboard.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/deviation.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/dlis.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/dnp3.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/doctor.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/dossier_verify.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/drill_string.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/earth_model.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/economics.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/ed25519.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/edr.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/elevation.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/etp.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/example_register_map.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fat_sat.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/files.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/follower.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/forward_model.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/friction_priors.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/gamma.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/geodesy.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/hartip.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/alarms.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/backup.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/catalog.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/charts.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/conformance.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/control-room.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/data-in.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/dlis.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/doctor.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/drift.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/economics.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/edr.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/etp.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/files.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/historian.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/instruments.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/interval.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/map.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/month-end.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/notifications.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/opseis.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/osdu.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/patches.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/quality.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/report-history.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/seismic.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/setup.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/simulation.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/site.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/sites.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/slip.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/soak.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/sra.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/start.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/structure.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/tools.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/transients.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/vibration.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/well-report.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/help/well3d.md +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/housekeeping.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/interval.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/jobs.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/live_ports.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/loadtest.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/modbus.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/model_card.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/month_end.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/op_seismicity.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/opcua_port.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/operator_app.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/osdu.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/patches.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/pdf_report.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/penetrator.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/permits.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/piwebapi.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/ports.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/ppdm.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/project.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/quakeml.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reconciler.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/report_history.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sample_record.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sbom.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seedlink.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/segy.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_array.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_film.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_response.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_track.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/seismic_watch.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/series_cache.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/service_life.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/shapefile.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/shell.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/shut_in.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/simulation.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/site_map.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sla_report.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/slip.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/soak.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/sra.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/store_forward.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/strata_join.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/structure.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/supervisor.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/survey_view.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/telemetry.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/tool_library.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/transient.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/vibration.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/well3d.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/well_assembler.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/well_report.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/wits0.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/witsml.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea/workspace.py +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.47.0 → gea_program-0.49.0}/setup.cfg +0 -0
- {gea_program-0.47.0 → gea_program-0.49.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.49.0
|
|
4
4
|
Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
|
|
5
5
|
Author: Daniel T. Murphy
|
|
6
6
|
License: MPL-2.0
|
|
@@ -648,6 +648,35 @@ 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
|
+
### Well collision checks
|
|
652
|
+
|
|
653
|
+
Every well scanned against every other: at each point of one well, the
|
|
654
|
+
closest point of the other, and the ISCWSA separation factor - the distance
|
|
655
|
+
between them less the holes and a surface margin, over k times the
|
|
656
|
+
uncertainty of that distance. Each well's position uncertainty comes from
|
|
657
|
+
the ISCWSA MWD Rev 4 error model (27 error sources, propagated as the model
|
|
658
|
+
says, the errors two wells share taken out of the distance between them),
|
|
659
|
+
checked against all 112 rows of the ISCWSA's own example; the Earth's field
|
|
660
|
+
at each well comes from the World Magnetic Model 2025. Each pair is levelled
|
|
661
|
+
as the ISCWSA's separation rule does - reported below 4.0, the approach
|
|
662
|
+
limit below 1.5, revise the plan below 1.25, below 1.0 only by the
|
|
663
|
+
operator's dispensation - with a company standard's k and margin kept with
|
|
664
|
+
its source. The Volve template's three wells are the worked example. The
|
|
665
|
+
**Collision** tab, `gea collision`, `gea help collision`.
|
|
666
|
+
|
|
667
|
+
### One record, tied together
|
|
668
|
+
|
|
669
|
+
The invoice check, rig time, the daily summary, the alerts, the signed
|
|
670
|
+
dossier and the declarations sheet now read each other: an invoice that
|
|
671
|
+
differs is an alert; the morning summary carries each well's invoice check
|
|
672
|
+
and yesterday's rig time; a signed dossier carries the contract, the
|
|
673
|
+
invoices and daily reports as attached, the rig-time targets and casing,
|
|
674
|
+
and both checks as they read at signing. The declarations sheet declares
|
|
675
|
+
the contract, the rig-time targets, block weights, casing shoes and the
|
|
676
|
+
watch and summary settings, and every file the program reads as a table -
|
|
677
|
+
the sheet, an invoice, a daily report - may be an Excel workbook. Casing
|
|
678
|
+
shoes split rig time by hole section.
|
|
679
|
+
|
|
651
680
|
### Rig time
|
|
652
681
|
|
|
653
682
|
What the rig did each second of the drilling record, read from the hook
|
|
@@ -609,6 +609,35 @@ 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
|
+
### Well collision checks
|
|
613
|
+
|
|
614
|
+
Every well scanned against every other: at each point of one well, the
|
|
615
|
+
closest point of the other, and the ISCWSA separation factor - the distance
|
|
616
|
+
between them less the holes and a surface margin, over k times the
|
|
617
|
+
uncertainty of that distance. Each well's position uncertainty comes from
|
|
618
|
+
the ISCWSA MWD Rev 4 error model (27 error sources, propagated as the model
|
|
619
|
+
says, the errors two wells share taken out of the distance between them),
|
|
620
|
+
checked against all 112 rows of the ISCWSA's own example; the Earth's field
|
|
621
|
+
at each well comes from the World Magnetic Model 2025. Each pair is levelled
|
|
622
|
+
as the ISCWSA's separation rule does - reported below 4.0, the approach
|
|
623
|
+
limit below 1.5, revise the plan below 1.25, below 1.0 only by the
|
|
624
|
+
operator's dispensation - with a company standard's k and margin kept with
|
|
625
|
+
its source. The Volve template's three wells are the worked example. The
|
|
626
|
+
**Collision** tab, `gea collision`, `gea help collision`.
|
|
627
|
+
|
|
628
|
+
### One record, tied together
|
|
629
|
+
|
|
630
|
+
The invoice check, rig time, the daily summary, the alerts, the signed
|
|
631
|
+
dossier and the declarations sheet now read each other: an invoice that
|
|
632
|
+
differs is an alert; the morning summary carries each well's invoice check
|
|
633
|
+
and yesterday's rig time; a signed dossier carries the contract, the
|
|
634
|
+
invoices and daily reports as attached, the rig-time targets and casing,
|
|
635
|
+
and both checks as they read at signing. The declarations sheet declares
|
|
636
|
+
the contract, the rig-time targets, block weights, casing shoes and the
|
|
637
|
+
watch and summary settings, and every file the program reads as a table -
|
|
638
|
+
the sheet, an invoice, a daily report - may be an Excel workbook. Casing
|
|
639
|
+
shoes split rig time by hole section.
|
|
640
|
+
|
|
612
641
|
### Rig time
|
|
613
642
|
|
|
614
643
|
What the rig did each second of the drilling record, read from the hook
|
|
@@ -590,6 +590,20 @@ def main(argv=None) -> int:
|
|
|
590
590
|
p_dz.add_argument("--actor", type=str, default="cli")
|
|
591
591
|
p_dz.add_argument("--json", action="store_true")
|
|
592
592
|
|
|
593
|
+
p_co = sub.add_parser("collision", help="every well scanned against every other: the closest approach and the ISCWSA separation factor, each well's position uncertainty by the ISCWSA MWD model and the field by WMM2025")
|
|
594
|
+
p_co.add_argument("--workspace", type=str, default=None)
|
|
595
|
+
p_co.add_argument("--scene", type=str, default=None, help="workspace (your wells) or volve (the worked example)")
|
|
596
|
+
p_co.add_argument("--write", action="store_true", help="write the client report")
|
|
597
|
+
p_co.add_argument("--k", type=str, default=None, help="declare the rule's scaling factor")
|
|
598
|
+
p_co.add_argument("--surface-margin", type=str, default=None, help="declare the surface margin, m")
|
|
599
|
+
p_co.add_argument("--sigma-pa", type=str, default=None, help="declare the project-ahead uncertainty, m")
|
|
600
|
+
p_co.add_argument("--source", type=str, default="", help="where the rule's values came from")
|
|
601
|
+
p_co.add_argument("--well", type=str, default=None)
|
|
602
|
+
p_co.add_argument("--hole", type=str, default=None, help="with --well: the hole size in inches ('' removes it)")
|
|
603
|
+
p_co.add_argument("--field", type=str, default=None, help="the magnetic field at LAT,LON[,YYYY-MM-DD] by WMM2025, and nothing else")
|
|
604
|
+
p_co.add_argument("--actor", type=str, default="cli")
|
|
605
|
+
p_co.add_argument("--json", action="store_true")
|
|
606
|
+
|
|
593
607
|
p_rt = sub.add_parser("rig-time", help="what the rig did each second of the drilling record - drilling, connections, trips, the slips - and the time lost against the pad's own best")
|
|
594
608
|
p_rt.add_argument("--workspace", type=str, required=True)
|
|
595
609
|
p_rt.add_argument("--well", type=str, default=None, help="with --write: one well's report (without it, the pad's)")
|
|
@@ -599,6 +613,9 @@ def main(argv=None) -> int:
|
|
|
599
613
|
p_rt.add_argument("--trip-out", type=str, default=None, help="declare the tripping-out target, ft/h")
|
|
600
614
|
p_rt.add_argument("--source", type=str, default="", help="where the targets came from")
|
|
601
615
|
p_rt.add_argument("--block-weight", type=str, default=None, help="with --well: the block weight in klbf where the estimate is wrong ('' removes it)")
|
|
616
|
+
p_rt.add_argument("--casing", type=str, default=None, help="with --well, --shoe-md and --source: declare a casing shoe, its size in inches as written (13-3/8)")
|
|
617
|
+
p_rt.add_argument("--shoe-md", type=str, default=None, help="the shoe's measured depth, ft")
|
|
618
|
+
p_rt.add_argument("--set-time", type=str, default=None, help="when the casing was set (the recorder's clock); otherwise when the hole is 50 ft past the shoe")
|
|
602
619
|
p_rt.add_argument("--actor", type=str, default="cli")
|
|
603
620
|
p_rt.add_argument("--json", action="store_true")
|
|
604
621
|
|
|
@@ -2205,9 +2222,10 @@ def main(argv=None) -> int:
|
|
|
2205
2222
|
if not a.file:
|
|
2206
2223
|
raise SystemExit("declare: give --file <sheet.csv> (or --template <out.csv> to start one)")
|
|
2207
2224
|
try:
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2225
|
+
from . import xlsx_read as _XR
|
|
2226
|
+
with open(a.file, "rb") as f:
|
|
2227
|
+
text_ = _XR.text_of(f.read())
|
|
2228
|
+
except (OSError, ValueError, KeyError) as e:
|
|
2211
2229
|
raise SystemExit(f"declare: {e}")
|
|
2212
2230
|
p = _DC.apply(ws, text_, a.actor, os.path.dirname(os.path.abspath(a.file)), write=a.apply)
|
|
2213
2231
|
print(_json.dumps(p, default=str) if a.json else _DC.text(p))
|
|
@@ -2368,6 +2386,42 @@ def main(argv=None) -> int:
|
|
|
2368
2386
|
print(f" {os.path.join(ws.reports_dir, e['path'])}")
|
|
2369
2387
|
print(f" key fingerprint {e['fingerprint']}")
|
|
2370
2388
|
return 0
|
|
2389
|
+
elif a.cmd == "collision":
|
|
2390
|
+
import json as _json
|
|
2391
|
+
from . import collision as _CO
|
|
2392
|
+
from .cli import _safe_stdout
|
|
2393
|
+
from .workspace import Workspace, WorkspaceError
|
|
2394
|
+
_safe_stdout()
|
|
2395
|
+
try:
|
|
2396
|
+
if a.field:
|
|
2397
|
+
from . import geomag as _GM
|
|
2398
|
+
parts = [x.strip() for x in a.field.split(",")]
|
|
2399
|
+
f_ = _GM.field(float(parts[0]), float(parts[1]), 0.0, parts[2] if len(parts) > 2 else None)
|
|
2400
|
+
print(f"{f_['model']} at {float(parts[0]):.4f}, {float(parts[1]):.4f} for {f_['decimal_year']:.2f}: declination {f_['declination_deg']:+.3f} deg, "
|
|
2401
|
+
f"dip {f_['dip_deg']:.3f} deg, total {f_['total_nT']:,.1f} nT" + (f" ({f_['note']})" if f_['note'] else ""))
|
|
2402
|
+
return 0
|
|
2403
|
+
ws = Workspace(a.workspace) if a.workspace else None
|
|
2404
|
+
if ws is not None and (a.k is not None or a.surface_margin is not None or a.sigma_pa is not None or a.hole is not None):
|
|
2405
|
+
if a.hole is not None and not a.well:
|
|
2406
|
+
raise ValueError("--hole needs --well")
|
|
2407
|
+
s_ = _CO.set_settings(ws, a.actor, a.k, a.surface_margin, a.sigma_pa, (a.well, a.hole) if a.hole is not None else None, a.source)
|
|
2408
|
+
print(f"rule: k {s_['k']:g}, surface margin {s_['surface_margin_m']:g} m, project-ahead {s_['sigma_pa_m']:g} m; holes: "
|
|
2409
|
+
+ (", ".join(f"{k} {v:g} in" for k, v in s_['holes_in'].items()) or "8.5 in by default"))
|
|
2410
|
+
return 0
|
|
2411
|
+
scene = a.scene or ("workspace" if ws is not None else "volve")
|
|
2412
|
+
if a.write:
|
|
2413
|
+
if ws is None:
|
|
2414
|
+
raise ValueError("--write needs --workspace")
|
|
2415
|
+
r_ = _CO.write_report(ws, __version__, a.actor, scene)
|
|
2416
|
+
print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
|
|
2417
|
+
if r_['paths'].get('pdf'):
|
|
2418
|
+
print(f" printable: {r_['paths']['pdf']}")
|
|
2419
|
+
return 0
|
|
2420
|
+
r = _CO.page(ws, scene)
|
|
2421
|
+
except (KeyError, ValueError, OSError, WorkspaceError) as e:
|
|
2422
|
+
raise SystemExit(f"collision: {e.args[0] if e.args else e}")
|
|
2423
|
+
print(_json.dumps({k: v for k, v in r.items()}, default=str) if a.json else _CO.text(r))
|
|
2424
|
+
return 0
|
|
2371
2425
|
elif a.cmd == "rig-time":
|
|
2372
2426
|
import json as _json
|
|
2373
2427
|
from . import rig_state as _RS
|
|
@@ -2376,6 +2430,12 @@ def main(argv=None) -> int:
|
|
|
2376
2430
|
_safe_stdout()
|
|
2377
2431
|
try:
|
|
2378
2432
|
ws = Workspace(a.workspace)
|
|
2433
|
+
if a.casing:
|
|
2434
|
+
if not a.well:
|
|
2435
|
+
raise ValueError("--casing needs --well")
|
|
2436
|
+
c_ = _RS.set_casing(ws, a.actor, a.well, a.casing, a.shoe_md, a.source, a.set_time)
|
|
2437
|
+
print(f"casing {c_['size']} in, shoe at {c_['shoe_md_ft']:,.0f} ft MD ({c_['source']})")
|
|
2438
|
+
return 0
|
|
2379
2439
|
if a.w2w_min is not None or a.trip_in is not None or a.trip_out is not None or a.block_weight is not None:
|
|
2380
2440
|
if a.block_weight is not None and not a.well:
|
|
2381
2441
|
raise ValueError("--block-weight needs --well")
|
|
@@ -10913,6 +10913,389 @@ def section_cp_rig_time(tmp: str) -> None:
|
|
|
10913
10913
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
10914
10914
|
|
|
10915
10915
|
|
|
10916
|
+
def section_cq_tied_together(tmp: str) -> None:
|
|
10917
|
+
"""Section CQ - the recent parts tied together: an Excel workbook read without Excel (shared and inline strings,
|
|
10918
|
+
numbers, booleans, dates and times from their cell formats, the 1904 epoch, a named sheet) and taken wherever a
|
|
10919
|
+
CSV is - the invoice check's files and the declarations sheet; the declarations sheet's new tables (the contract,
|
|
10920
|
+
the rig-time targets, block weights, casing, the watch and summary settings) planned, written, round-tripped and
|
|
10921
|
+
refused when wrong; rig time by hole section from declared casing; the invoice differing raised as an alert and
|
|
10922
|
+
cleared; the daily summary carrying each invoice check and yesterday's rig time; a signed dossier carrying the
|
|
10923
|
+
contract, the files, the targets, the casing and both checks as they read at signing; the API, the CLI, the tabs
|
|
10924
|
+
and the help pages."""
|
|
10925
|
+
import http.cookiejar
|
|
10926
|
+
import subprocess as _sp
|
|
10927
|
+
import urllib.request
|
|
10928
|
+
import urllib.error
|
|
10929
|
+
import base64 as _b64
|
|
10930
|
+
import zipfile as _zf
|
|
10931
|
+
from datetime import datetime as _dtm, timedelta as _td, timezone as _tz
|
|
10932
|
+
from . import __version__
|
|
10933
|
+
from . import alerts as AL
|
|
10934
|
+
from . import declarations as DC
|
|
10935
|
+
from . import dossier as DZ
|
|
10936
|
+
from . import helplib as H
|
|
10937
|
+
from . import invoice_check as IC
|
|
10938
|
+
from . import pad_summary as PS
|
|
10939
|
+
from . import rig_state as RS
|
|
10940
|
+
from . import seismic_watch as SW
|
|
10941
|
+
from . import xlsx_read as XR
|
|
10942
|
+
from .workspace import Workspace
|
|
10943
|
+
from .service import Service, Users
|
|
10944
|
+
pkg = Path(__file__).parent
|
|
10945
|
+
d = Path(tmp, "cq"); d.mkdir()
|
|
10946
|
+
|
|
10947
|
+
# -- CQ1 the workbook -------------------------------------------------------------------------------------------------
|
|
10948
|
+
XR.write_simple(str(d / "a.xlsx"), [["Date", "Rate Type", "Quantity", "Note"], [_dtm(2026, 7, 1), "Operating day rate", 1, 'with, a "comma"'],
|
|
10949
|
+
[_dtm(2026, 7, 2, 6, 30), "Standby", 12.5, None]])
|
|
10950
|
+
a = XR.rows((d / "a.xlsx").read_bytes())
|
|
10951
|
+
ns = 'xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"'
|
|
10952
|
+
with _zf.ZipFile(d / "b.xlsx", "w") as z: # a hand-made workbook: two sheets, inline strings, booleans, the 1904 epoch, a custom date format
|
|
10953
|
+
z.writestr("xl/workbook.xml", f'<workbook {ns}><workbookPr date1904="1"/><sheets><sheet name="Notes" sheetId="1" r:id="rId1"/><sheet name="Invoice" sheetId="2" r:id="rId2"/></sheets></workbook>')
|
|
10954
|
+
z.writestr("xl/_rels/workbook.xml.rels", '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="worksheets/sheet1.xml" Type="x"/><Relationship Id="rId2" Target="/xl/worksheets/sheet2.xml" Type="x"/></Relationships>')
|
|
10955
|
+
z.writestr("xl/styles.xml", f'<styleSheet {ns}><numFmts count="1"><numFmt numFmtId="164" formatCode="dd/mm/yyyy\\ hh:mm"/><numFmt numFmtId="165" formatCode="#,##0.00"/></numFmts>'
|
|
10956
|
+
'<cellXfs count="3"><xf numFmtId="0"/><xf numFmtId="164"/><xf numFmtId="165"/></cellXfs></styleSheet>')
|
|
10957
|
+
z.writestr("xl/worksheets/sheet1.xml", f'<worksheet {ns}><sheetData><row r="1"><c r="A1" t="inlineStr"><is><t>notes</t></is></c></row></sheetData></worksheet>')
|
|
10958
|
+
z.writestr("xl/worksheets/sheet2.xml", f'<worksheet {ns}><sheetData><row r="1"><c r="A1" t="inlineStr"><is><t>When</t></is></c><c r="C1" t="inlineStr"><is><t>Paid</t></is></c></row>'
|
|
10959
|
+
'<row r="3"><c r="A3" s="1"><v>44743.25</v></c><c r="B3" s="2"><v>1234.5</v></c><c r="C3" t="b"><v>1</v></c></row></sheetData></worksheet>')
|
|
10960
|
+
bx = (d / "b.xlsx").read_bytes()
|
|
10961
|
+
b2 = XR.rows(bx, "Invoice")
|
|
10962
|
+
c1 = {
|
|
10963
|
+
"simple": a == [["Date", "Rate Type", "Quantity", "Note"], ["2026-07-01", "Operating day rate", "1", 'with, a "comma"'], ["2026-07-02T06:30", "Standby", "12.5"]],
|
|
10964
|
+
"csv": XR.to_csv((d / "a.xlsx").read_bytes()).splitlines()[1] == '2026-07-01,Operating day rate,1,"with, a ""comma"""',
|
|
10965
|
+
"sheets": XR.sheets(bx) == ["Notes", "Invoice"] and XR.rows(bx) == [["notes"]],
|
|
10966
|
+
"hand_made": b2 == [["When", "", "Paid"], [], ["2026-07-02T06:00", "1234.5", "TRUE"]],
|
|
10967
|
+
"text_of": XR.text_of(b"a,b\n1,2\n") == "a,b\n1,2\n" and not XR.is_xlsx(b"PK not a zip"),
|
|
10968
|
+
}
|
|
10969
|
+
try:
|
|
10970
|
+
XR.rows(bx, "Nope"); c1["named"] = False
|
|
10971
|
+
except ValueError as e:
|
|
10972
|
+
c1["named"] = "Notes, Invoice" in str(e)
|
|
10973
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
10974
|
+
ok(not failed1,
|
|
10975
|
+
"CQ1 an Excel workbook read with the standard library: shared and inline strings, a comma and quotes kept, whole numbers without "
|
|
10976
|
+
"'.0', dates and date-times from built-in and custom formats, the 1904 epoch, booleans, a gap row kept as a gap, the first or a "
|
|
10977
|
+
"named sheet; CSV text passed through" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
10978
|
+
|
|
10979
|
+
# -- CQ2 the declarations sheet, casing, and the invoice as a workbook --------------------------------------------------
|
|
10980
|
+
F = RS.selftest_edr(str(d))
|
|
10981
|
+
G = IC.selftest_files(str(d))
|
|
10982
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad CQ", actor="tester")
|
|
10983
|
+
w1 = ws.add_well_file(F["path"], display="RIG-1", actor="tester"); w2 = ws.add_well_file(F["path"], display="RIG-2", actor="tester")
|
|
10984
|
+
wi = ws.add_well_file(G["edr"], display="INV-1", actor="tester")
|
|
10985
|
+
sheet = ("[contract],day_start,report_date,operating,standby,repair,repair_hours_per_month,source\n,06:00,start,30000,25000,0,2,daywork contract A\n"
|
|
10986
|
+
"[rig_time],w2w_min,trip_in_ft_h,source\n,,2800,program 2026\n[block_weights],well,block_weight_klbf\n,RIG-2,42\n"
|
|
10987
|
+
"[casing],well,size_in,shoe_md_ft,set,source\n,RIG-1,9-5/8,10150,,tally\n,RIG-2,9-5/8,10150,,tally\n,RIG-1,13-3/8,2000,,tally\n"
|
|
10988
|
+
"[settings],setting,value\n,watch_every_min,10\n,summary_at,10:45\n,summary_send,no\n")
|
|
10989
|
+
rows_ = [ln.split(",") for ln in sheet.strip().splitlines()]
|
|
10990
|
+
XR.write_simple(str(d / "decl.xlsx"), rows_)
|
|
10991
|
+
p_x = DC.plan(ws, XR.text_of((d / "decl.xlsx").read_bytes()))
|
|
10992
|
+
p_w = DC.apply(ws, sheet, "tester", write=True)
|
|
10993
|
+
p_rt = DC.plan(ws, DC.template(ws))
|
|
10994
|
+
p_bad = DC.plan(ws, "[contract],operating,source\n,30000,\n[settings],setting,value\n,watch_mode,sometimes\n,colour,blue\n[casing],well,size_in,shoe_md_ft,source\n,RIG-1,9 5/8,100,x\n,NOPE,7,100,x\n")
|
|
10995
|
+
pad = RS.pad(ws)
|
|
10996
|
+
W1 = next(w for w in pad["wells"] if w["display"] == "RIG-1")
|
|
10997
|
+
secs = [c["section"] for c in W1["connection_list"]]
|
|
10998
|
+
XR.write_simple(str(d / "inv.xlsx"), [ln.split(",") if i == 0 else [(_dtm.fromisoformat(x) if j == 0 else (float(x) if re.fullmatch(r"[\d.]+", x) else x)) for j, x in enumerate(ln.split(","))]
|
|
10999
|
+
for i, ln in enumerate(G["invoice"].strip().splitlines())])
|
|
11000
|
+
IC.attach(ws, "tester", wi["id"], "ddr", "ddr.csv", G["ddr"].encode(), "reports")
|
|
11001
|
+
IC.attach(ws, "tester", wi["id"], "invoice", "inv.xlsx", (d / "inv.xlsx").read_bytes(), "invoice as a workbook")
|
|
11002
|
+
ic = IC.check(ws, wi["id"])
|
|
11003
|
+
c2 = {
|
|
11004
|
+
"xlsx_sheet": p_x["counts"]["NEW"] == 6 and p_x["counts"]["CHANGE"] == 3 and not p_x["errors"],
|
|
11005
|
+
"written": p_w["applied"] and IC.terms(ws)["rates_usd_per_day"] == {"operating": 30000.0, "standby": 25000.0, "repair": 0.0}
|
|
11006
|
+
and RS.settings(ws)["targets"] == {"trip_in_ft_h": 2800.0} and RS.settings(ws)["block_weight_klbf"] == {w2["id"]: 42.0}
|
|
11007
|
+
and [x["size"] for x in RS.casing(ws)[w1["id"]]] == ["13-3/8", "9-5/8"] and SW.settings(ws)["every_min"] == 10
|
|
11008
|
+
and PS.settings(ws)["at"] == "10:45" and PS.settings(ws)["send"] is False,
|
|
11009
|
+
"round_trip": p_rt["counts"]["NEW"] == 0 and p_rt["counts"]["CHANGE"] == 0 and not p_rt["errors"],
|
|
11010
|
+
"refused": len(p_bad["errors"]) == 5 and any("watch_mode" in e for e in p_bad["errors"]) and any("no setting 'colour'" in e for e in p_bad["errors"])
|
|
11011
|
+
and any("13-3/8, 9-5/8 or 7" in e for e in p_bad["errors"]) and any("came from" in e for e in p_bad["errors"]),
|
|
11012
|
+
"sections": secs == ["below the 13-3/8 in shoe", "below the 9-5/8 in shoe", "below the 9-5/8 in shoe"]
|
|
11013
|
+
and all(x["hole"] == "CASED" for x in W1["trips"]) and set(pad["targets"]["by_section"]) == {"below the 9-5/8 in shoe"}
|
|
11014
|
+
and next(w for w in pad["wells"] if w["display"] == "RIG-2")["block_weight_klbf"] == 42.0,
|
|
11015
|
+
"invoice_xlsx": ic["status"] == "DIFFERS" and ic["totals"]["usd_in_question"] == 37666.67,
|
|
11016
|
+
}
|
|
11017
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
11018
|
+
ok(not failed2,
|
|
11019
|
+
"CQ2 the declarations sheet's new tables - the contract, the rig-time targets, a block weight, casing shoes, the watch and summary "
|
|
11020
|
+
"settings - read from a workbook, written, round-tripped to nothing to do, and refused when wrong (no source, an unknown setting, a "
|
|
11021
|
+
"size not in inches as written, a well not there); connections by hole section and trips inside casing; an invoice attached as a "
|
|
11022
|
+
"workbook checked to the same 37,666.67" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
11023
|
+
|
|
11024
|
+
# -- CQ3 the alert, the summary, the dossier ----------------------------------------------------------------------
|
|
11025
|
+
now = _dtm(2026, 7, 2, 12, 0, tzinfo=_tz.utc)
|
|
11026
|
+
k1 = AL.check_conditions(ws, None, now, force=True)
|
|
11027
|
+
st1 = dict(AL.state(ws)["conditions"])
|
|
11028
|
+
g = PS.gather(ws, _dtm(2026, 8, 2, 7, 0, tzinfo=_tz.utc), _dtm(2026, 8, 1, 0, 0, tzinfo=_tz.utc))
|
|
11029
|
+
RS.write_report(ws, __version__, "tester")
|
|
11030
|
+
e1 = DZ.build(ws, "tester", __version__, None)
|
|
11031
|
+
with _zf.ZipFile(os.path.join(ws.reports_dir, e1["path"])) as z:
|
|
11032
|
+
names = set(z.namelist())
|
|
11033
|
+
rt = json.loads(z.read("results/rig_time.json"))
|
|
11034
|
+
icj = json.loads(z.read(f"results/invoice_check/{wi['id']}.json"))
|
|
11035
|
+
v1 = DZ.verify(os.path.join(ws.reports_dir, e1["path"]), ws)
|
|
11036
|
+
for f_ in IC.files(ws, wi["id"]): # the corrected report and invoice replace them
|
|
11037
|
+
IC.remove(ws, "tester", f_["file"])
|
|
11038
|
+
ddr_ok = G["ddr"].replace("2026-07-02,14:00,16:00,2,6,Trip out", "2026-07-02,14:00,16:00,2,7,Service rig").replace("6200,7600", "6200,7400")
|
|
11039
|
+
inv_ok = ("Date,Rate Type,Quantity,Unit,Unit Price,Amount\n2026-07-01,Operating day rate,1,day,30000,30000\n"
|
|
11040
|
+
"2026-07-02,Operating day rate,22,hour,1250,27500\n2026-07-02,Repair rate,2,hour,0,0\n"
|
|
11041
|
+
"2026-07-03,Standby rate,20,hour,1041.6667,20833.33\n2026-07-03,Operating day rate,4,hour,1250,5000\n2026-07-04,Operating day rate,1,day,30000,30000\n")
|
|
11042
|
+
IC.attach(ws, "tester", wi["id"], "ddr", "ddr_ok.csv", ddr_ok.encode(), "corrected reports")
|
|
11043
|
+
IC.attach(ws, "tester", wi["id"], "invoice", "inv_ok.csv", inv_ok.encode(), "corrected invoice")
|
|
11044
|
+
k2 = AL.check_conditions(ws, None, now + _td(minutes=1))
|
|
11045
|
+
c3 = {
|
|
11046
|
+
"alert": [a_["event"] for a_ in k1 if a_["event"] == "invoice.differs"] == ["invoice.differs"] and f"invoice.differs:{wi['id']}" in st1
|
|
11047
|
+
and "37,666.67" in next(a_ for a_ in k1 if a_["event"] == "invoice.differs")["detail"],
|
|
11048
|
+
"cleared": [a_["event"] for a_ in k2] == ["condition.cleared"] and k2[0]["cleared"] == "invoice.differs",
|
|
11049
|
+
"summary": any(x["item"] == "the invoice check of INV-1" and x["status"] == "DIFFERS" for x in g["due"])
|
|
11050
|
+
and [x["well"] for x in g["rig_time"]] == ["RIG-1", "RIG-2"] and all(x["connections"] == 3 and x["date"] == "2026-08-01" for x in g["rig_time"])
|
|
11051
|
+
and all(x["lost_usd"] is not None for x in g["rig_time"]),
|
|
11052
|
+
"dossier": v1["status"] == "VALID" and {"declarations/economics/invoices/terms.json", "declarations/economics/invoices/files.json",
|
|
11053
|
+
"declarations/drillstring/rig_time.json", "declarations/drillstring/casing.json"} <= names
|
|
11054
|
+
and any(n.startswith(f"declarations/economics/invoices/{wi['id']}/") and n.endswith("_inv.xlsx") for n in names)
|
|
11055
|
+
and icj["status"] == "DIFFERS" and len(rt["wells"]) == 3 and rt["targets"]["trip_in_ft_h"]["value"] == 2800,
|
|
11056
|
+
}
|
|
11057
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
11058
|
+
ok(not failed3,
|
|
11059
|
+
"CQ3 an invoice that differs raised as an alert with its dollars, and cleared when a corrected invoice replaces it; the daily summary "
|
|
11060
|
+
"carrying the invoice check and yesterday's rig time well by well with its cost at the contract's rate; a signed pad dossier carrying "
|
|
11061
|
+
"the contract, the invoice files as attached, the targets and the casing, and both checks as they read at signing - VALID"
|
|
11062
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
11063
|
+
|
|
11064
|
+
# -- CQ4 the API, the CLI, the tabs, the help ------------------------------------------------------------------------
|
|
11065
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
11066
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
11067
|
+
base_url = svc.url.rstrip("/")
|
|
11068
|
+
|
|
11069
|
+
def opener(name, pw_):
|
|
11070
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
11071
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
11072
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
11073
|
+
return o
|
|
11074
|
+
|
|
11075
|
+
def call(o, method, path, body=None):
|
|
11076
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
11077
|
+
if method == "POST":
|
|
11078
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
11079
|
+
try:
|
|
11080
|
+
with o.open(rq, timeout=120) as rr_:
|
|
11081
|
+
return rr_.status, json.loads(rr_.read())
|
|
11082
|
+
except urllib.error.HTTPError as e:
|
|
11083
|
+
return e.code, json.loads(e.read())
|
|
11084
|
+
try:
|
|
11085
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
11086
|
+
s_vc, _ = call(V, "POST", "/api/rig-time/casing", {"well_id": w2["id"], "size": "7", "shoe_md_ft": 9000, "source": "x"})
|
|
11087
|
+
s_oc, ocj = call(O, "POST", "/api/rig-time/casing", {"well_id": w2["id"], "size": "7", "shoe_md_ft": 9000, "source": "tally"})
|
|
11088
|
+
s_ob, _ = call(O, "POST", "/api/rig-time/casing", {"well_id": w2["id"], "size": "seven", "shoe_md_ft": 9000, "source": "tally"})
|
|
11089
|
+
s_or, _ = call(O, "POST", "/api/rig-time/casing/remove", {"well_id": w2["id"], "size": "7"})
|
|
11090
|
+
s_dx, dxj = call(O, "POST", "/api/declare", {"content_b64": _b64.b64encode((d / "decl.xlsx").read_bytes()).decode()})
|
|
11091
|
+
s_ia, iaj = call(O, "POST", "/api/invoice-check/attach", {"well_id": wi["id"], "kind": "invoice", "filename": "inv2.xlsx", "source": "workbook",
|
|
11092
|
+
"content_b64": _b64.b64encode((d / "inv.xlsx").read_bytes()).decode()})
|
|
11093
|
+
s_al, alj = call(V, "GET", "/api/alerts")
|
|
11094
|
+
finally:
|
|
11095
|
+
svc.stop()
|
|
11096
|
+
cli_c = _sp.run([sys.executable, "-m", "gea", "rig-time", "--workspace", wsp, "--well", w2["id"], "--casing", "13-3/8", "--shoe-md", "2000", "--source", "tally"],
|
|
11097
|
+
capture_output=True, text=True, cwd=str(pkg.parent))
|
|
11098
|
+
cli_d = _sp.run([sys.executable, "-m", "gea", "declare", "--workspace", wsp, "--file", str(d / "decl.xlsx")], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
11099
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
11100
|
+
c4 = {
|
|
11101
|
+
"api": s_vc == 403 and s_oc == 200 and ocj["shoe_md_ft"] == 9000 and s_ob == 400 and s_or == 200 and s_dx == 200 and dxj["counts"]["SAME"] == 9
|
|
11102
|
+
and s_ia == 200 and iaj["lines"] == 7 and s_al == 200 and "invoice.differs" in alj["events"],
|
|
11103
|
+
"cli": cli_c.returncode == 0 and "13-3/8 in, shoe at 2,000 ft" in cli_c.stdout and cli_d.returncode == 0 and "already declared" in cli_d.stdout,
|
|
11104
|
+
"page": all(x in page_src for x in ("rtC", "/api/rig-time/casing", ".xlsx", "content_b64: DC.b64", "'invoice.differs': 'Invoice differs'")),
|
|
11105
|
+
"help": all(not H.check(t_) for t_ in ("declare", "rig-time", "invoice-check", "dossier", "alerts", "pad-summary"))
|
|
11106
|
+
and "[casing]" in (pkg / "help" / "declare.md").read_text(encoding="utf-8") and "Hole sections" in (pkg / "help" / "rig-time.md").read_text(encoding="utf-8")
|
|
11107
|
+
and "invoice.differs" in (pkg / "help" / "alerts.md").read_text(encoding="utf-8") and "results/" in (pkg / "help" / "dossier.md").read_text(encoding="utf-8"),
|
|
11108
|
+
}
|
|
11109
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
11110
|
+
ok(not failed4,
|
|
11111
|
+
"CQ4 on the API (only an operator declares or removes a casing shoe, a size that is not one refused; a declarations workbook and an "
|
|
11112
|
+
"invoice workbook sent as they are; the new alert listed), the CLI (a casing shoe, a declarations workbook), the tabs and the help pages"
|
|
11113
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
11114
|
+
|
|
11115
|
+
|
|
11116
|
+
def section_cr_collision(tmp: str) -> None:
|
|
11117
|
+
"""Section CR - well collision checks: each well's position uncertainty by the ISCWSA MWD Rev 4 error model,
|
|
11118
|
+
checked against all 112 rows of the ISCWSA's own example; the Earth's field from WMM2025, checked against a
|
|
11119
|
+
published example; the ISCWSA separation factor, its arithmetic recomputed by hand with the errors two wells share
|
|
11120
|
+
taken out; the Volve template as the worked example; a workspace's wells checked, the rule and the hole sizes kept
|
|
11121
|
+
by an operator only; the API, the CLI, the report, the tab and the help."""
|
|
11122
|
+
import hashlib as _hl
|
|
11123
|
+
import http.cookiejar
|
|
11124
|
+
import math as _m
|
|
11125
|
+
import subprocess as _sp
|
|
11126
|
+
import time as _t
|
|
11127
|
+
import urllib.request
|
|
11128
|
+
import urllib.error
|
|
11129
|
+
import numpy as _np
|
|
11130
|
+
from . import collision as CO
|
|
11131
|
+
from . import edr as E
|
|
11132
|
+
from . import geomag as GM
|
|
11133
|
+
from . import helplib as H
|
|
11134
|
+
from . import pdf_report as PR
|
|
11135
|
+
from . import well3d as W
|
|
11136
|
+
from . import wellpos as WP
|
|
11137
|
+
from .workspace import Workspace
|
|
11138
|
+
from .service import Service, Users
|
|
11139
|
+
pkg = Path(__file__).parent
|
|
11140
|
+
d = Path(tmp, "collision"); d.mkdir()
|
|
11141
|
+
|
|
11142
|
+
# -- CR1 the error model and the field against their published examples --------------------------------------------
|
|
11143
|
+
ex = WP.check_example()
|
|
11144
|
+
tot8 = ex["totals"]["8000"]
|
|
11145
|
+
f1 = GM.field(42.283, -71.35, 1000.0, "2028-07-04")
|
|
11146
|
+
f2 = GM.field(31.42, -104.10, 0.0, 2031.5)
|
|
11147
|
+
c1 = {
|
|
11148
|
+
"example": ex["rows"] == 112 and ex["sources"] == 27 and ex["depths_m"] == [1200, 2100, 5100, 8000] and not ex["disagree"] and ex["worst_relative"] < 1e-3,
|
|
11149
|
+
"totals": abs(tot8[0] - 8547.78) < 0.05 and abs(tot8[1] - 712.69) < 0.05 and abs(tot8[2] - 468.13) < 0.05 and abs(tot8[3] + 2252.31) < 0.05,
|
|
11150
|
+
"attribution": "ISCWSA" in ex["source"] and "welleng" in ex["source"] and "Apache" in ex["license"],
|
|
11151
|
+
"wmm": abs(f1["declination_deg"] + 13.6891) < 5e-4 and abs(f1["dip_deg"] - 65.9899) < 5e-4 and abs(f1["total_nT"] - 50940) < 1.0
|
|
11152
|
+
and abs(f1["north_nT"] - 20139) < 1.0 and abs(f1["east_nT"] + 4905) < 1.0 and abs(f1["down_nT"] - 46532) < 1.0 and f1["in_range"],
|
|
11153
|
+
"wmm_range": not f2["in_range"] and "extrapolated" in f2["note"] and abs(GM.decimal_year("2026-07-02") - (2026 + 182 / 365)) < 1e-9,
|
|
11154
|
+
"coefficients": _hl.md5((pkg / "reference" / "WMM2025.COF").read_bytes()).hexdigest() == "a1b6402dd23658d15affb0f31babccc7",
|
|
11155
|
+
}
|
|
11156
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
11157
|
+
ok(not failed1,
|
|
11158
|
+
"CR1 each well's uncertainty by the ISCWSA MWD Rev 4 error model: all 112 rows of the ISCWSA's own example (27 error sources "
|
|
11159
|
+
"and the total, at 1,200, 2,100, 5,100 and 8,000 m MD) recomputed and agreeing to the printed digits, its source and licence "
|
|
11160
|
+
"carried; the field by WMM2025 agreeing with a published example (declination -13.6891, dip 65.9899 deg, 50,940 nT), a date "
|
|
11161
|
+
"past the model's years answered and called extrapolated, the coefficients as NOAA published them"
|
|
11162
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
11163
|
+
|
|
11164
|
+
# -- CR2 the separation factor's arithmetic ----------------------------------------------------------------------------
|
|
11165
|
+
fld = {"declination_deg": 6.0, "dip_deg": 60.0, "total_nT": 48000.0}
|
|
11166
|
+
md = [0.0, 500.0, 1000.0, 2000.0]
|
|
11167
|
+
mk = lambda i, x, inc=(0, 0, 0, 0), az=(0, 0, 0, 0), y=0.0: {"id": i, "display": i, "md": md, "inc": list(inc), "az": list(az), "x0": x, "y0": y,
|
|
11168
|
+
"ref_elev_m": 0.0, "field": fld, "hole_in": 8.5}
|
|
11169
|
+
rule = {**CO.DEFAULTS}
|
|
11170
|
+
A, B = CO.prepare(mk("A", 0.0)), CO.prepare(mk("B", 10.0))
|
|
11171
|
+
sAB, sBA = CO.scan(A, B, rule), CO.scan(B, A, rule)
|
|
11172
|
+
u = WP.well_uncertainty(_np.array(md), _np.zeros(4), _np.zeros(4), _np.array(md), 6.0, 60.0, 48000.0)
|
|
11173
|
+
Cee = float(u["cov"][-1][1, 1]); Gee = sum(float(g[-1][1]) ** 2 for g in u["global"].values())
|
|
11174
|
+
sig_hand = _m.sqrt(2.0 * (Cee - Gee))
|
|
11175
|
+
sf_hand = (10.0 - 2 * 8.5 * 0.0254 / 2 - 0.3) / (3.5 * _m.sqrt(sig_hand ** 2 + 0.25))
|
|
11176
|
+
i_td = len(sAB["profile"]["md_m"]) - 1
|
|
11177
|
+
E1, E2 = CO.prepare(mk("E1", 0.0, (0, 0, 30, 30), (90, 90, 90, 90))), CO.prepare(mk("E2", 0.0, (0, 0, 30, 30), (90, 90, 90, 90), 10.0))
|
|
11178
|
+
s_sh = CO.scan(E1, E2, rule) # parallel, inclined east, 10 m apart north-south: the declination moves both
|
|
11179
|
+
s_ng = CO.scan({**E1, "glob": {k: v * 0 for k, v in E1["glob"].items()}}, {**E2, "glob": {k: v * 0 for k, v in E2["glob"].items()}}, rule)
|
|
11180
|
+
j_td = len(s_sh["profile"]["md_m"]) - 1
|
|
11181
|
+
ue = WP.well_uncertainty(_np.array(md), _np.array([0.0, 0.0, 30.0, 30.0]), _np.full(4, 90.0), _np.array(W.Path(md, [0, 0, 30, 30], [90] * 4, 0.0, 0.0, 0.0, 0.0).tvd), 6.0, 60.0, 48000.0)
|
|
11182
|
+
Cnn = float(ue["cov"][-1][0, 0]); Gnn = sum(float(g[-1][0]) ** 2 for g in ue["global"].values())
|
|
11183
|
+
C_, D_ = CO.prepare(mk("C", 0.0, (0, 0, 30, 30), (0, 0, 90, 90))), CO.prepare(mk("D", 30.0, (0, 0, 30, 30), (0, 0, 270, 270)))
|
|
11184
|
+
sCD = CO.scan(C_, D_, rule)
|
|
11185
|
+
c2 = {
|
|
11186
|
+
"hand": abs(sAB["profile"]["sigma_m"][i_td] - sig_hand) < 2e-3 and abs(sAB["profile"]["sf"][i_td] - sf_hand) < 2e-3 and abs(sAB["profile"]["distance_m"][i_td] - 10.0) < 1e-6,
|
|
11187
|
+
"shared_out": set(u["global"]) == {"DECG", "DBHG", "DSTG"} and Gnn > 0.01 and abs(s_sh["profile"]["sigma_m"][j_td] - _m.sqrt(2.0 * (Cnn - Gnn))) < 2e-3
|
|
11188
|
+
and abs(s_ng["profile"]["sigma_m"][j_td] - _m.sqrt(2.0 * Cnn)) < 2e-3 and abs(s_sh["profile"]["distance_m"][j_td] - 10.0) < 1e-3,
|
|
11189
|
+
"symmetric": abs(sAB["min_sf"] - sBA["min_sf"]) < 1e-6 and sAB["at_md_m"] == 2000.0,
|
|
11190
|
+
"crossing": sCD["min_sf"] < 1.0 and sCD["level"] == "BELOW 1.0" and sCD["closest_m"] < 1.0 and sCD["sections"] and sCD["sections"][0]["from_md_m"] < sCD["at_md_m"],
|
|
11191
|
+
"levels": [CO.level(x)[0] for x in (0.99, 1.0, 1.2, 1.25, 1.4, 1.5, 3.99, 4.0)]
|
|
11192
|
+
== ["BELOW 1.0", "REVISE THE PLAN", "REVISE THE PLAN", "APPROACH LIMIT", "APPROACH LIMIT", "REPORTED", "REPORTED", "CLEAR"],
|
|
11193
|
+
}
|
|
11194
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
11195
|
+
ok(not failed2,
|
|
11196
|
+
"CR2 the separation factor's arithmetic: two vertical wells 10 m apart recomputed by hand at TD - sigma along the line between "
|
|
11197
|
+
"them from each well's covariance less the errors both share (declination, the global magnetic and depth terms), the holes' "
|
|
11198
|
+
"radii, the surface margin, project-ahead and k; leaving the shared errors in widens sigma; the same from either well; two wells "
|
|
11199
|
+
"steered at each other found below 1.0 where they cross; the four levels at their edges"
|
|
11200
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
11201
|
+
|
|
11202
|
+
# -- CR3 the worked example: the Volve template ------------------------------------------------------------------------
|
|
11203
|
+
t0 = _t.time()
|
|
11204
|
+
v = CO.page(None, "volve")
|
|
11205
|
+
dt = _t.time() - t0
|
|
11206
|
+
w_ = v["worst"]; wl = {x["display"]: x for x in v["wells"]}
|
|
11207
|
+
c3 = {
|
|
11208
|
+
"wells": len(v["wells"]) == 3 and len(v["pairs"]) == 6 and abs(wl["15/9-F-10"]["td_md_m"] - 5300.1) < 0.1
|
|
11209
|
+
and all(abs(x["field"]["declination_deg"] - 1.12) < 0.05 for x in v["wells"]) and all("grid convergence" in x["azimuth_note"] for x in v["wells"]),
|
|
11210
|
+
"worst": w_["level"] == "BELOW 1.0" and abs(w_["min_sf"] - 0.29) < 0.01 and {w_["reference_name"], w_["offset_name"]} == {"15/9-F-5", "15/9-F-10"}
|
|
11211
|
+
and abs(w_["at_md_m"] - 250) < 15 and abs(w_["distance_m"] - 1.20) < 0.05,
|
|
11212
|
+
"order": [p["min_sf"] for p in v["pairs"]] == sorted(p["min_sf"] for p in v["pairs"]) and all(len(p["profile"]["md_m"]) == len(p["profile"]["sf"]) for p in v["pairs"]),
|
|
11213
|
+
"ellipses": wl["15/9-F-10"]["td_ellipse_1sigma"]["major_m"] > wl["15/9-F-5"]["td_ellipse_1sigma"]["major_m"] > wl["NO 15/9-F-4"]["td_ellipse_1sigma"]["major_m"] > 1.0,
|
|
11214
|
+
"fast": dt < 20.0,
|
|
11215
|
+
}
|
|
11216
|
+
failed3 = [k for k, v_ in c3.items() if not v_]
|
|
11217
|
+
ok(not failed3,
|
|
11218
|
+
"CR3 the worked example, the Volve template's three wells (Equinor's released data): every pair scanned both ways, the field from "
|
|
11219
|
+
"WMM2025 at the template and the grid convergence applied; the closest, 15/9-F-5 and 15/9-F-10 near the surface where the slots "
|
|
11220
|
+
"sit 1.2 m apart, below 1.0; the longest well the widest ellipse at TD"
|
|
11221
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
11222
|
+
|
|
11223
|
+
# -- CR4 a workspace's wells, the API, the CLI, the report, the tab and the help ----------------------------------------
|
|
11224
|
+
ev = E.selftest_events(str(d))
|
|
11225
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
11226
|
+
ids = []
|
|
11227
|
+
for i, n in enumerate(("Well A", "Well B", "Well C")):
|
|
11228
|
+
w = ws.add_well_file(ev["path"], display=n, actor="tester"); ids.append(w["id"])
|
|
11229
|
+
ws.set_well(w["id"], surface={"lat": 31.42, "lon": -104.10 + 0.0001 * i, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
|
|
11230
|
+
W.attach_trajectory(ws, ids[0], "a.csv", b"MD (ft),Inc (deg),Azi (deg)\n0,0,0\n1000,0,0\n2000,10,90\n3000,30,90\n4000,45,95\n5750,60,100\n", "tester", "true", "ft", None, None, None, "test survey")
|
|
11231
|
+
W.attach_trajectory(ws, ids[1], "b.csv", b"MD (ft),Inc (deg),Azi (deg)\n0,0,0\n1200,0,0\n2200,12,270\n3500,35,265\n5500,55,260\n", "tester", "true", "ft", None, None, None, "test survey")
|
|
11232
|
+
p0 = CO.page(ws)
|
|
11233
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
11234
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
11235
|
+
base_url = svc.url.rstrip("/")
|
|
11236
|
+
|
|
11237
|
+
def opener(name, pw_):
|
|
11238
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
11239
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
11240
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
11241
|
+
return o
|
|
11242
|
+
|
|
11243
|
+
def call(o, method, path, body=None):
|
|
11244
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
11245
|
+
if method == "POST":
|
|
11246
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
11247
|
+
try:
|
|
11248
|
+
with o.open(rq, timeout=120) as rr_:
|
|
11249
|
+
return rr_.status, json.loads(rr_.read())
|
|
11250
|
+
except urllib.error.HTTPError as e:
|
|
11251
|
+
return e.code, json.loads(e.read())
|
|
11252
|
+
try:
|
|
11253
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
11254
|
+
s_vg, vg = call(V, "GET", "/api/collision")
|
|
11255
|
+
s_vv, vv = call(V, "GET", "/api/collision?scene=volve")
|
|
11256
|
+
s_bad, _ = call(V, "GET", "/api/collision?scene=nowhere")
|
|
11257
|
+
s_vs, _ = call(V, "POST", "/api/collision/settings", {"k": 3.0, "source": "x"})
|
|
11258
|
+
s_ns, _ = call(O, "POST", "/api/collision/settings", {"k": 3.0})
|
|
11259
|
+
s_lk, _ = call(O, "POST", "/api/collision/settings", {"k": 0.1, "source": "standard"})
|
|
11260
|
+
s_ok, okj = call(O, "POST", "/api/collision/settings", {"k": 3.0, "surface_margin_m": 0.5, "source": "company anti-collision standard, rev 3"})
|
|
11261
|
+
s_hb, _ = call(O, "POST", "/api/collision/settings", {"well_id": ids[0], "hole_in": 99})
|
|
11262
|
+
s_ho, hoj = call(O, "POST", "/api/collision/settings", {"well_id": ids[0], "hole_in": 12.25})
|
|
11263
|
+
s_ag, ag = call(V, "GET", "/api/collision")
|
|
11264
|
+
s_vw, _ = call(V, "POST", "/api/collision/write", {})
|
|
11265
|
+
s_ow, owj = call(O, "POST", "/api/collision/write", {})
|
|
11266
|
+
finally:
|
|
11267
|
+
svc.stop()
|
|
11268
|
+
cli_v = _sp.run([sys.executable, "-m", "gea", "collision", "--scene", "volve"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
11269
|
+
cli_f = _sp.run([sys.executable, "-m", "gea", "collision", "--field", "42.283,-71.35,2028-07-04"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
11270
|
+
cli_w = _sp.run([sys.executable, "-m", "gea", "collision", "--workspace", wsp, "--write"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
11271
|
+
pdf = Path(wsp, "reports", "collision.pdf")
|
|
11272
|
+
rd = PR.read(pdf.read_bytes()) if pdf.exists() else {"ok": False, "text": []}
|
|
11273
|
+
audit = Path(ws.audit_path).read_text(encoding="utf-8")
|
|
11274
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
11275
|
+
wa = next((p for p in ag.get("pairs", []) if p["reference"] == ids[0]), {}) if s_ag == 200 else {}
|
|
11276
|
+
c4 = {
|
|
11277
|
+
"workspace": len(p0["wells"]) == 2 and len(p0["pairs"]) == 2 and [x["id"] for x in p0["left_out"]] == [ids[2]] and "survey" in p0["left_out"][0]["why"]
|
|
11278
|
+
and all("WMM2025 at the well" in x["field_note"] for x in p0["wells"]) and p0["worst"]["level"] == "BELOW 1.0",
|
|
11279
|
+
"api": s_vg == 200 and vg["scene"] == "workspace" and len(vg["pairs"]) == 2 and s_vv == 200 and len(vv["pairs"]) == 6 and s_bad in (400, 404)
|
|
11280
|
+
and s_vs == 403 and s_ns == 400 and s_lk == 400 and s_ok == 200 and okj["k"] == 3.0 and s_hb == 400 and s_ho == 200 and hoj["holes_in"][ids[0]] == 12.25
|
|
11281
|
+
and s_ag == 200 and ag["rule"]["k"] == 3.0 and ag["rule_source"].startswith("company") and wa.get("min_sf", 0) < p0["pairs"][0]["min_sf"] + 1
|
|
11282
|
+
and next(x for x in ag["wells"] if x["id"] == ids[0])["hole_in"] == 12.25 and s_vw == 403 and s_ow == 200 and owj["report_id"].startswith("COL-"),
|
|
11283
|
+
"cli": cli_v.returncode == 0 and "15/9-F-5" in cli_v.stdout and "BELOW 1.0" in cli_v.stdout and cli_f.returncode == 0 and "declination -13.69" in cli_f.stdout
|
|
11284
|
+
and cli_w.returncode == 0 and "collision" in cli_w.stdout,
|
|
11285
|
+
"report": rd["ok"] and any("separation factor" in t_ for t_ in rd["text"]) and any("Well A" in t_ for t_ in rd["text"])
|
|
11286
|
+
and "collision.settings" in audit and "report.collision" in audit,
|
|
11287
|
+
"page": all(x in page_src for x in ("VIEWS.collision", "['#/collision', 'Collision']", "/api/collision/settings", "/api/collision/write", "coChart")),
|
|
11288
|
+
"help": not H.check("collision") and H.VIEW_TOPIC.get("collision") == "collision" and "WMM2025" in (pkg / "help" / "collision.md").read_text(encoding="utf-8"),
|
|
11289
|
+
}
|
|
11290
|
+
failed4 = [k for k, v_ in c4.items() if not v_]
|
|
11291
|
+
ok(not failed4,
|
|
11292
|
+
"CR4 a workspace's wells: those with a position, an elevation and a survey checked, the one without listed with why, the field "
|
|
11293
|
+
"at each well; on the API anyone reads it and only an operator keeps a rule (with its source; a k below 0.5 refused) or a hole size "
|
|
11294
|
+
"(inches, 2 to 40) or writes the report; the CLI (the worked example, the field at a place and date, the report); the report as "
|
|
11295
|
+
"PDF; the tab and the help"
|
|
11296
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
11297
|
+
|
|
11298
|
+
|
|
10916
11299
|
def main() -> int:
|
|
10917
11300
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
10918
11301
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -11010,6 +11393,8 @@ def main() -> int:
|
|
|
11010
11393
|
section_cn_four(tmp)
|
|
11011
11394
|
section_co_invoice_check(tmp)
|
|
11012
11395
|
section_cp_rig_time(tmp)
|
|
11396
|
+
section_cq_tied_together(tmp)
|
|
11397
|
+
section_cr_collision(tmp)
|
|
11013
11398
|
if _FAILS:
|
|
11014
11399
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
11015
11400
|
for f in _FAILS:
|