gea-program 0.37.0__tar.gz → 0.38.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.37.0/gea_program.egg-info → gea_program-0.38.0}/PKG-INFO +12 -1
- {gea_program-0.37.0 → gea_program-0.38.0}/README.md +11 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/__init__.py +1 -1
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/__main__.py +26 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/acceptance_tests.py +169 -1
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/client_reports.py +27 -1
- gea_program-0.38.0/gea/help/well-report.md +11 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/helplib.py +1 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/month_end.py +14 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/service.py +12 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/web/app.html +8 -0
- gea_program-0.38.0/gea/well_report.py +469 -0
- {gea_program-0.37.0 → gea_program-0.38.0/gea_program.egg-info}/PKG-INFO +12 -1
- {gea_program-0.37.0 → gea_program-0.38.0}/gea_program.egg-info/SOURCES.txt +2 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/pyproject.toml +1 -1
- {gea_program-0.37.0 → gea_program-0.38.0}/LICENSE +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/backup.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/bench.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/blind_harness.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog_map.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/catalog_register.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/certificates.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/cli.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/config_versioning.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/conformance.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/control_room.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/correlation.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/dashboard.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/deviation.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/dlis.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/dnp3.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/doctor.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/earth_model.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/economics.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/edr.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/elevation.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/etp.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/example_register_map.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/fat_sat.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/files.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/follower.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/forward_model.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/friction_priors.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/gamma.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/geodesy.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/hartip.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/alarms.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/backup.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/catalog.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/charts.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/conformance.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/control-room.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/data-in.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/dlis.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/doctor.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/drift.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/economics.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/edr.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/etp.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/files.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/historian.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/instruments.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/interval.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/map.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/month-end.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/notifications.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/osdu.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/patches.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/quality.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/seismic.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/setup.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/site.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/sites.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/slip.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/soak.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/sra.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/start.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/structure.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/tools.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/transients.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/vibration.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/help/well3d.md +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/housekeeping.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/interval.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/jobs.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/live_ports.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/loadtest.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/modbus.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/model_card.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/notify.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/opcua_port.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/operator_app.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/osdu.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/patches.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/permits.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/piwebapi.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/ports.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/ppdm.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/project.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/quakeml.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reconciler.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sample_record.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sbom.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seedlink.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/segy.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_array.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_film.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_response.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_track.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/series_cache.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/service_life.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/shapefile.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/shell.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/shut_in.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/site_map.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sla_report.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/slip.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/soak.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/sra.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/store_forward.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/strata_join.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/structure.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/supervisor.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/survey_view.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/telemetry.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/tool_library.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/transient.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/vibration.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/well3d.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/well_assembler.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/wits0.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/witsml.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea/workspace.py +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.37.0 → gea_program-0.38.0}/setup.cfg +0 -0
- {gea_program-0.37.0 → gea_program-0.38.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.38.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,17 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### The client well report
|
|
652
|
+
|
|
653
|
+
`gea well-report` writes one printable document per well and one for the
|
|
654
|
+
pad: the drilling record and its depth events with who checked them, a chart
|
|
655
|
+
of depth against days, the declared location and what the mapped structure
|
|
656
|
+
says there, the well path from its survey in plan and section, and the
|
|
657
|
+
declared cost against the market while it was drilled - each number with
|
|
658
|
+
where it came from, and what the report cannot say yet with what would
|
|
659
|
+
supply it. Buttons on the well page and the Reports page; included in the
|
|
660
|
+
month-end packet. `gea help well-report`.
|
|
661
|
+
|
|
651
662
|
### Economics
|
|
652
663
|
|
|
653
664
|
The Economics tab puts each well's days on its drilling record, feet
|
|
@@ -609,6 +609,17 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### The client well report
|
|
613
|
+
|
|
614
|
+
`gea well-report` writes one printable document per well and one for the
|
|
615
|
+
pad: the drilling record and its depth events with who checked them, a chart
|
|
616
|
+
of depth against days, the declared location and what the mapped structure
|
|
617
|
+
says there, the well path from its survey in plan and section, and the
|
|
618
|
+
declared cost against the market while it was drilled - each number with
|
|
619
|
+
where it came from, and what the report cannot say yet with what would
|
|
620
|
+
supply it. Buttons on the well page and the Reports page; included in the
|
|
621
|
+
month-end packet. `gea help well-report`.
|
|
622
|
+
|
|
612
623
|
### Economics
|
|
613
624
|
|
|
614
625
|
The Economics tab puts each well's days on its drilling record, feet
|
|
@@ -475,6 +475,12 @@ def main(argv=None) -> int:
|
|
|
475
475
|
p_ck.add_argument("--note", type=str, default="", help="--confirm: what it was checked against (required)")
|
|
476
476
|
p_ck.add_argument("--actor", type=str, default="cli")
|
|
477
477
|
|
|
478
|
+
p_wr = sub.add_parser("well-report", help="the client well report: one printable document per well (drilling record, depth events, progress chart, location and structure, well path, cost and the market) and one for the pad")
|
|
479
|
+
p_wr.add_argument("--workspace", type=str, required=True)
|
|
480
|
+
p_wr.add_argument("--well", type=str, default=None, help="one well's report (the default is every well and the pad)")
|
|
481
|
+
p_wr.add_argument("--pad", action="store_true", help="the pad report only")
|
|
482
|
+
p_wr.add_argument("--actor", type=str, default="cli")
|
|
483
|
+
|
|
478
484
|
p_ec = sub.add_parser("economics", help="each well's time, footage and declared cost against the market while it was drilled; declare rates, items and an AFE; refresh or import price series")
|
|
479
485
|
p_ec.add_argument("--workspace", type=str, required=True)
|
|
480
486
|
p_ec.add_argument("--json", action="store_true")
|
|
@@ -2009,6 +2015,26 @@ def main(argv=None) -> int:
|
|
|
2009
2015
|
e = _EC.economics(ws)
|
|
2010
2016
|
print(_json.dumps(e, indent=1) if a.json else _EC.text(e))
|
|
2011
2017
|
return 0
|
|
2018
|
+
elif a.cmd == "well-report":
|
|
2019
|
+
from . import well_report as _WR
|
|
2020
|
+
from .cli import _safe_stdout
|
|
2021
|
+
from .workspace import Workspace, WorkspaceError
|
|
2022
|
+
_safe_stdout()
|
|
2023
|
+
try:
|
|
2024
|
+
ws = Workspace(a.workspace)
|
|
2025
|
+
except WorkspaceError as e:
|
|
2026
|
+
raise SystemExit(f"well-report: {e}")
|
|
2027
|
+
ids = [w["id"] for w in ws.wells()]
|
|
2028
|
+
if a.well and a.well not in ids:
|
|
2029
|
+
raise SystemExit(f"well-report: no well {a.well} (the wells are: {', '.join(ids) or 'none'})")
|
|
2030
|
+
todo = [] if a.pad else ([a.well] if a.well else ids)
|
|
2031
|
+
for wid in todo:
|
|
2032
|
+
r = _WR.write_well(ws, wid, __version__, actor=a.actor)
|
|
2033
|
+
print(f"{wid}: {r['paths']['html']}" + (f"\n not in it yet: {'; '.join(r['missing'])}" if r["missing"] else ""))
|
|
2034
|
+
if a.pad or not a.well:
|
|
2035
|
+
r = _WR.write_pad(ws, __version__, actor=a.actor)
|
|
2036
|
+
print(f"pad: {r['paths']['html']}")
|
|
2037
|
+
return 0
|
|
2012
2038
|
elif a.cmd in ("trajectory", "well3d"):
|
|
2013
2039
|
import json as _json
|
|
2014
2040
|
from . import well3d as _W3
|
|
@@ -2917,7 +2917,7 @@ def section_am_report_samples(tmp: str) -> None:
|
|
|
2917
2917
|
"model_card_strata_property_estimator.html", "gauge_drift_report_monitored_SYNTHETIC.html", "alarm_event_report_SYNTHETIC.html",
|
|
2918
2918
|
"data_resilience_report_SYNTHETIC.html", "sla_report_SYNTHETIC.html", "sat_protocol.html", "seismic_station_report_SYNTHETIC.html", "seismic_track_report_SYNTHETIC.html",
|
|
2919
2919
|
"seismic_dataset_report_SYNTHETIC.html", "seismic_field_report_SYNTHETIC.html", "site_report_SYNTHETIC.html",
|
|
2920
|
-
"sra_packet_SYNTHETIC.html"}
|
|
2920
|
+
"sra_packet_SYNTHETIC.html", "well_report_SYNTHETIC.html", "pad_report_SYNTHETIC.html"}
|
|
2921
2921
|
have = {p.name for p in d.glob("*.html")}
|
|
2922
2922
|
md = (d / "SAMPLES.md").read_text(encoding="utf-8") if (d / "SAMPLES.md").exists() else ""
|
|
2923
2923
|
texts = {n: (d / n).read_text(encoding="utf-8", errors="replace") for n in sorted(have & want)}
|
|
@@ -8970,6 +8970,173 @@ def section_cf_economics(tmp: str) -> None:
|
|
|
8970
8970
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8971
8971
|
|
|
8972
8972
|
|
|
8973
|
+
def section_cg_well_report(tmp: str) -> None:
|
|
8974
|
+
"""Section CG - the client well report: one document per well and one for the pad, assembled from the drilling record,
|
|
8975
|
+
the depth events and their check, the declared location and the mapped structure there, the survey's path, and the
|
|
8976
|
+
declared cost against the market; drawings made from the report's own numbers; each missing part named with what
|
|
8977
|
+
would supply it, never drawn as zero; the vocabulary guard; the month-end packet, the API, the CLI, the pages."""
|
|
8978
|
+
import http.cookiejar
|
|
8979
|
+
import math as _m
|
|
8980
|
+
import subprocess as _sp
|
|
8981
|
+
import urllib.request
|
|
8982
|
+
import urllib.error
|
|
8983
|
+
import xml.etree.ElementTree as _ET
|
|
8984
|
+
from . import client_reports as CR
|
|
8985
|
+
from . import economics as EC
|
|
8986
|
+
from . import edr as E
|
|
8987
|
+
from . import helplib as H
|
|
8988
|
+
from . import month_end as ME
|
|
8989
|
+
from . import setup_checklist as SC
|
|
8990
|
+
from . import shapefile as SF
|
|
8991
|
+
from . import well3d as W
|
|
8992
|
+
from . import well_report as WR
|
|
8993
|
+
from .workspace import Workspace
|
|
8994
|
+
from .service import Service, Users
|
|
8995
|
+
pkg = Path(__file__).parent
|
|
8996
|
+
d = Path(tmp, "cg"); d.mkdir()
|
|
8997
|
+
ev = E.selftest_events(str(d))
|
|
8998
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
8999
|
+
wb = ws.add_well_file(ev["path"], display="Well B", actor="tester"); wc = ws.add_well_file(ev["path"], display="Well C", actor="tester")
|
|
9000
|
+
ws.set_well(wb["id"], surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}},
|
|
9001
|
+
identity={"api_number": "42-389-12345"}, actor="tester")
|
|
9002
|
+
lat1, lon1 = 31.42, -104.10
|
|
9003
|
+
kmlon = 1.0 / (111.195 * _m.cos(_m.radians(lat1)))
|
|
9004
|
+
SF.write(str(d / "Top_A"), "PolyLine", [([[(lon1 + (i - 1.5) * kmlon + 0.0005 * k, lat1 - 0.05 + 0.001 * k) for k in range(100)]], {"ElevTop_ft": -1500.0 - 100.0 * i}) for i in range(4)],
|
|
9005
|
+
[("ElevTop_ft", "F", 10, 1)], SF.NAD83_GEOGCS)
|
|
9006
|
+
ws.add_layer(str(d / "Top_A.shp"), "Top A", actor="tester", formation="A", role="top", citation="test")
|
|
9007
|
+
csv_ok = "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"
|
|
9008
|
+
W.attach_trajectory(ws, wb["id"], "s.csv", csv_ok.encode(), "tester", "true", "ft", None, None, None, "test survey")
|
|
9009
|
+
ws.add_site("Pad 3", wells=[wb["id"], wc["id"]], client="Test Client", actor="tester")
|
|
9010
|
+
ws.refresh_dashboard(actor="tester")
|
|
9011
|
+
rec = json.loads(Path(ws.reports_dir, "wells", wb["id"], "drilling_record.json").read_text(encoding="utf-8"))
|
|
9012
|
+
EC.set_rate(ws, wb["id"], "tester", 30000, "rig spread", "contract")
|
|
9013
|
+
EC.add_item(ws, wb["id"], "tester", 18000, "bit", "invoice", 1)
|
|
9014
|
+
EC.set_afe(ws, wb["id"], "tester", 150000, "AFE 1")
|
|
9015
|
+
SC.confirm(ws, f"events:{wb['id']}", "tester", "daily drilling reports 1-5")
|
|
9016
|
+
r = WR.write_well(ws, wb["id"], "0.0-test", actor="tester")
|
|
9017
|
+
wdir = Path(ws.reports_dir, "wells", wb["id"])
|
|
9018
|
+
md = (wdir / "well_report.md").read_text(encoding="utf-8"); ht = (wdir / "well_report.html").read_text(encoding="utf-8")
|
|
9019
|
+
js = json.loads((wdir / "well_report.json").read_text(encoding="utf-8"))
|
|
9020
|
+
figs = sorted(Path(p).name for p in r["paths"].get("figures", []))
|
|
9021
|
+
parsed = 0
|
|
9022
|
+
for f in figs:
|
|
9023
|
+
try:
|
|
9024
|
+
_ET.fromstring((wdir / f).read_text(encoding="utf-8")); parsed += 1
|
|
9025
|
+
except _ET.ParseError:
|
|
9026
|
+
pass
|
|
9027
|
+
st = W.trajectory_of(ws, wb["id"])["stations"]
|
|
9028
|
+
p = W.Path([x["md"] for x in st], [x["incl"] for x in st], [x["azi"] for x in st], 0.0, 0.0, 0.0, 0.0)
|
|
9029
|
+
tvd_ft = float(p.tvd[-1]) / 0.3048
|
|
9030
|
+
fb = next(w for w in EC.economics(ws)["wells"] if w["well_id"] == wb["id"])
|
|
9031
|
+
c1 = {
|
|
9032
|
+
"complete": r["missing"] == [] and js["missing"] == [] and js["report_id"] == r["report_id"] and "42-389-12345" in md and "Test Client" in md
|
|
9033
|
+
and "KB 3,000.0 ft (test plat)" in md,
|
|
9034
|
+
"record": f"{rec['total_depth_md_ft']:,.1f} ft" in md and f"{rec['feet_drilled']:,.1f} ft" in md and rec["source"] in md
|
|
9035
|
+
and md.count("\n| STALE OPENING |") + md.count("\n| RE-REFERENCE |") + md.count("\n| SIDETRACK |") + md.count("\n| CORRECTION |") == len(rec["depth_events"])
|
|
9036
|
+
and "Checked by tester" in md and "daily drilling reports 1-5" in md,
|
|
9037
|
+
"figures": figs == sorted(["well_report_progress.svg", "well_report_plan.svg", "well_report_section.svg", "well_report_wti.svg", "well_report_henry_hub.svg"])
|
|
9038
|
+
and parsed == 5 and ht.count("<figure") == 5 and ht.count("<figcaption") == 5 and md.count("![") == 5 and "@media print" in ht and "break-inside:avoid" in ht,
|
|
9039
|
+
"path": f"{tvd_ft:,.1f}" in md and f"reaches {tvd_ft:,.0f} ft true vertical depth" in md and "6 stations, azimuths true" in md,
|
|
9040
|
+
"tops": "| A | 4,500 to 4,600 | -1,600 and -1,500 | Top A |" in md and "Between the -1,600 ft and -1,500 ft contours" in md,
|
|
9041
|
+
"cost": f"${fb['cost']['usd']:,.0f}" in md and f"${fb['cost']['per_ft']:,.2f}" in md and "over the whole record" in md and "$18,000.00 bit (section 1)" in md
|
|
9042
|
+
and f"{fb['market']['wti']['mean']:,.2f}" in md,
|
|
9043
|
+
"guarded": not CR.forbidden_terms(md) and not CR.forbidden_terms(ht),
|
|
9044
|
+
}
|
|
9045
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
9046
|
+
ok(not failed1,
|
|
9047
|
+
"CG1 a well's report: the identity, client and declared reference with their sources; the drilling record's own total depth, footage "
|
|
9048
|
+
"and every depth event with the person's check; depth against days, the plan and the vertical section from the survey (TVD to the "
|
|
9049
|
+
"minimum-curvature value), the mapped top at the location as a depth band from the two contours it lies between below the declared "
|
|
9050
|
+
"reference, the declared cost to the dollar of the Economics tab and the market while drilling - five drawings, each valid SVG with a "
|
|
9051
|
+
"caption and words for a reader who cannot see it, none split across a printed page"
|
|
9052
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
9053
|
+
|
|
9054
|
+
rc = WR.write_well(ws, wc["id"], "0.0-test", actor="tester")
|
|
9055
|
+
cdir = Path(ws.reports_dir, "wells", wc["id"])
|
|
9056
|
+
mdc = (cdir / "well_report.md").read_text(encoding="utf-8")
|
|
9057
|
+
EC.add_item(ws, wb["id"], "tester", 1, "lattice rental", "test") # a declared word the client register does not carry
|
|
9058
|
+
guarded = False
|
|
9059
|
+
try:
|
|
9060
|
+
WR.write_well(ws, wb["id"], "0.0-test", actor="tester")
|
|
9061
|
+
except ValueError as e:
|
|
9062
|
+
guarded = "lattice" in str(e)
|
|
9063
|
+
EC.remove(ws, wb["id"], "tester", "item", 1)
|
|
9064
|
+
rp = WR.write_pad(ws, "0.0-test", actor="tester")
|
|
9065
|
+
pmd = Path(ws.reports_dir, "pad_report.md").read_text(encoding="utf-8")
|
|
9066
|
+
psvg = Path(ws.reports_dir, "pad_report_progress.svg").read_text(encoding="utf-8")
|
|
9067
|
+
pk = ME.assemble(ws, "2026-09", actor="tester", refresh=False)
|
|
9068
|
+
idx = json.loads((Path(pk["folder"]) / "INDEX.json").read_text(encoding="utf-8"))
|
|
9069
|
+
by_name = {i["name"]: i for i in idx["items"]}
|
|
9070
|
+
c2 = {
|
|
9071
|
+
"missing_named": rc["missing"] == ["a person's check of the depth events", "the surface location", "the directional survey", "the cost basis (day rates)"]
|
|
9072
|
+
and "not declared" in mdc and "No directional survey is attached" in mdc and "the well is not placed" in mdc
|
|
9073
|
+
and "Cost (declared)" not in mdc and "Not yet checked by a person" in mdc and "what would supply it" in mdc
|
|
9074
|
+
and sorted(Path(x).name for x in rc["paths"]["figures"]) == ["well_report_henry_hub.svg", "well_report_progress.svg", "well_report_wti.svg"],
|
|
9075
|
+
"guard": guarded and not Path(ws.reports_dir, "wells", wb["id"], "well_report.html").read_text(encoding="utf-8").lower().count("lattice"),
|
|
9076
|
+
"pad": "| Well B | 2026-07-01 |" in pmd and "| Well C | 2026-07-01 |" in pmd and psvg.count("<path ") == 2 and "| Well B | nothing |" in pmd
|
|
9077
|
+
and "| Well C | a person's check of the depth events; the surface location; the directional survey; the cost basis (day rates) |" in pmd,
|
|
9078
|
+
"month_end": by_name[f"Well B: client well report"]["status"] == "INCLUDED" and "well_report_section.svg" in [f["file"] for f in by_name["Well B: client well report"]["files"]]
|
|
9079
|
+
and by_name["Pad report"]["status"] == "INCLUDED" and pk["errors"] == [],
|
|
9080
|
+
"audit": [a["action"] for a in ws.audit_log(500)].count("report.well") >= 3 and "report.pad" in [a["action"] for a in ws.audit_log(500)],
|
|
9081
|
+
}
|
|
9082
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
9083
|
+
ok(not failed2,
|
|
9084
|
+
"CG2 what a report cannot say: a well with no check of its depth events, no surface location, no survey and no cost basis gets its "
|
|
9085
|
+
"record, its progress and the market, and each missing part named with what would supply it - no path, no cost, nothing drawn as "
|
|
9086
|
+
"zero; a declared word the client register does not carry stops the report before it is written; the pad report puts every well side "
|
|
9087
|
+
"by side on one chart with what each still lacks; the month-end packet writes and includes them with their drawings; every run audited"
|
|
9088
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
9089
|
+
|
|
9090
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
9091
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
9092
|
+
base_url = svc.url.rstrip("/")
|
|
9093
|
+
|
|
9094
|
+
def opener(name, pw):
|
|
9095
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
9096
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw}).encode())
|
|
9097
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
9098
|
+
return o
|
|
9099
|
+
|
|
9100
|
+
def call(o, method, path, body=None):
|
|
9101
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
9102
|
+
if method == "POST":
|
|
9103
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
9104
|
+
try:
|
|
9105
|
+
with o.open(rq, timeout=120) as rr_:
|
|
9106
|
+
ct = rr_.headers.get("Content-Type", "")
|
|
9107
|
+
raw = rr_.read()
|
|
9108
|
+
return rr_.status, (json.loads(raw) if "json" in ct else raw.decode("utf-8", "replace"))
|
|
9109
|
+
except urllib.error.HTTPError as e:
|
|
9110
|
+
return e.code, json.loads(e.read())
|
|
9111
|
+
try:
|
|
9112
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
9113
|
+
st_v, _ = call(V, "POST", "/api/reports/well-report", {"well_id": wb["id"]})
|
|
9114
|
+
st_o, jo = call(O, "POST", "/api/reports/well-report", {"well_id": wb["id"]})
|
|
9115
|
+
st_p, jp = call(O, "POST", "/api/reports/well-report", {"pad": True})
|
|
9116
|
+
st_n, _ = call(O, "POST", "/api/reports/well-report", {"well_id": "nope"})
|
|
9117
|
+
st_h, page = call(V, "GET", jo.get("url", "/x") if isinstance(jo, dict) else "/x")
|
|
9118
|
+
st_r, ri = call(V, "GET", "/api/reports")
|
|
9119
|
+
finally:
|
|
9120
|
+
svc.stop()
|
|
9121
|
+
cli = _sp.run([sys.executable, "-m", "gea", "well-report", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
9122
|
+
cli_bad = _sp.run([sys.executable, "-m", "gea", "well-report", "--workspace", wsp, "--well", "nope"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
9123
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
9124
|
+
c3 = {
|
|
9125
|
+
"api": st_v == 403 and st_o == 200 and jo["missing"] == [] and jo["url"] == f"/reports/wells/{wb['id']}/well_report.html" and st_p == 200
|
|
9126
|
+
and jp["url"] == "/reports/pad_report.html" and st_n == 404 and st_h == 200 and "<figure" in page and st_r == 200 and "pad_report.html" in ri["site"]
|
|
9127
|
+
and "well_report_plan.svg" in ri["wells"][wb["id"]],
|
|
9128
|
+
"cli": cli.returncode == 0 and f"{wb['id']}: " in cli.stdout and "pad: " in cli.stdout and "not in it yet" in cli.stdout.split(f"{wc['id']}: ")[1]
|
|
9129
|
+
and cli_bad.returncode != 0 and "no well nope" in cli_bad.stderr,
|
|
9130
|
+
"page": all(x in page_src for x in ("/api/reports/well-report", "wrRun", "data-wr", "Client well report", "Write the pad report", "Not in it yet")),
|
|
9131
|
+
"help": any(t[0] == "well-report" for t in H.INDEX) and not H.check("well-report"),
|
|
9132
|
+
}
|
|
9133
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
9134
|
+
ok(not failed3,
|
|
9135
|
+
"CG3 the API (a viewer reads the report, only an operator writes it; an unknown well named), the CLI (every well and the pad, or one, "
|
|
9136
|
+
"or the pad alone), the buttons on the well page and the Reports page, and the help page"
|
|
9137
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
9138
|
+
|
|
9139
|
+
|
|
8973
9140
|
def main() -> int:
|
|
8974
9141
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
8975
9142
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -9057,6 +9224,7 @@ def main() -> int:
|
|
|
9057
9224
|
section_cd_control_room(tmp)
|
|
9058
9225
|
section_ce_well3d(tmp)
|
|
9059
9226
|
section_cf_economics(tmp)
|
|
9227
|
+
section_cg_well_report(tmp)
|
|
9060
9228
|
if _FAILS:
|
|
9061
9229
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
9062
9230
|
for f in _FAILS:
|
|
@@ -90,12 +90,23 @@ class Table:
|
|
|
90
90
|
caption: str = ''
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
@dataclass
|
|
94
|
+
class Figure:
|
|
95
|
+
"""A drawing the report carries: SVG made by the program from the numbers in the report's own tables, with a caption
|
|
96
|
+
and the words a reader who cannot see it gets instead."""
|
|
97
|
+
name: str # the file stem it is written under, beside the report
|
|
98
|
+
svg: str
|
|
99
|
+
caption: str
|
|
100
|
+
alt: str
|
|
101
|
+
|
|
102
|
+
|
|
93
103
|
@dataclass
|
|
94
104
|
class Section:
|
|
95
105
|
number: str
|
|
96
106
|
title: str
|
|
97
107
|
paragraphs: List[str] = field(default_factory=list)
|
|
98
108
|
tables: List[Table] = field(default_factory=list)
|
|
109
|
+
figures: List[Figure] = field(default_factory=list)
|
|
99
110
|
|
|
100
111
|
|
|
101
112
|
@dataclass
|
|
@@ -910,6 +921,11 @@ def render_markdown(doc: Document) -> str:
|
|
|
910
921
|
for p in s.paragraphs:
|
|
911
922
|
L.append(p)
|
|
912
923
|
L.append('')
|
|
924
|
+
for fg in s.figures:
|
|
925
|
+
L.append(f'')
|
|
926
|
+
L.append('')
|
|
927
|
+
L.append(f'*{fg.caption}*')
|
|
928
|
+
L.append('')
|
|
913
929
|
for t in s.tables:
|
|
914
930
|
if t.caption:
|
|
915
931
|
L.append(f'*{t.caption}*')
|
|
@@ -932,7 +948,9 @@ _CSS = ('body{font-family:Segoe UI,Arial,sans-serif;margin:2rem auto;max-width:1
|
|
|
932
948
|
'font-size:.85rem;width:100%}th,td{border:1px solid #d0d0d0;padding:.3rem .5rem;text-align:left;'
|
|
933
949
|
'vertical-align:top}th{background:#f2f2f2}caption{text-align:left;font-style:italic;padding:.2rem 0}'
|
|
934
950
|
'.front td:first-child{font-weight:600;width:14rem}.result-bad{color:#a40000;font-weight:700}'
|
|
935
|
-
'.result-ok{color:#0a6b1f;font-weight:700}footer{margin-top:2rem;font-size:.8rem;color:#555}'
|
|
951
|
+
'.result-ok{color:#0a6b1f;font-weight:700}footer{margin-top:2rem;font-size:.8rem;color:#555}'
|
|
952
|
+
'figure{margin:1rem 0}figure svg{max-width:100%;height:auto;display:block}figcaption{font-size:.8rem;font-style:italic;color:#444}'
|
|
953
|
+
'@media print{body{margin:0;max-width:none}h2{break-after:avoid}table,figure{break-inside:avoid}}')
|
|
936
954
|
|
|
937
955
|
|
|
938
956
|
def render_html(doc: Document) -> str:
|
|
@@ -950,6 +968,8 @@ def render_html(doc: Document) -> str:
|
|
|
950
968
|
H.append(f'<h2>{e(s.number)}. {e(s.title)}</h2>')
|
|
951
969
|
for p in s.paragraphs:
|
|
952
970
|
H.append(f'<p>{e(p)}</p>')
|
|
971
|
+
for fg in s.figures:
|
|
972
|
+
H.append(f'<figure role="img" aria-label="{e(fg.alt)}">{fg.svg}<figcaption>{e(fg.caption)}</figcaption></figure>')
|
|
953
973
|
for t in s.tables:
|
|
954
974
|
H.append('<table>')
|
|
955
975
|
if t.caption:
|
|
@@ -1888,6 +1908,12 @@ def write(doc: Document, out_dir: str, basename: str = 'gauge_drift_report') ->
|
|
|
1888
1908
|
f.write(md)
|
|
1889
1909
|
with open(paths['html'], 'w', encoding='utf-8', newline='') as f:
|
|
1890
1910
|
f.write(ht)
|
|
1911
|
+
for s in doc.sections:
|
|
1912
|
+
for fg in s.figures:
|
|
1913
|
+
fp = os.path.join(out_dir, fg.name + '.svg')
|
|
1914
|
+
with open(fp, 'w', encoding='utf-8', newline='') as f:
|
|
1915
|
+
f.write(fg.svg)
|
|
1916
|
+
paths.setdefault('figures', []).append(fp)
|
|
1891
1917
|
machine = {k: v for k, v in doc.data.items() if not k.startswith('_')}
|
|
1892
1918
|
with open(paths['json'], 'w', encoding='utf-8') as f:
|
|
1893
1919
|
json.dump(machine, f, indent=1, default=str)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The client well report
|
|
2
|
+
|
|
3
|
+
> One printable document per well, and one for the pad, assembled from what the workspace already holds: the drilling record and its depth events with who checked them, a chart of depth against days, the declared location and what the mapped structure says there, the well path from its directional survey in plan and section with the mapped tops, and the declared cost against the market while it was drilled. Each number says where it came from, and the report lists what it cannot say yet and what would supply it.
|
|
4
|
+
|
|
5
|
+
**The command**: on a well's page, **Write the well report** (the operator role); on the Reports page, **Write the pad report** or one button per well. From a terminal: `gea well-report --workspace C:\site` writes every well's report and the pad report; `--well Well-30636033` writes one; `--pad` the pad alone. The month-end packet writes and includes them for every well with a drilling record.
|
|
6
|
+
|
|
7
|
+
**What it writes**: `reports/wells/<well>/well_report.html`, `.md` and `.json`, with its drawings beside it as SVG (`well_report_progress.svg`, `well_report_plan.svg`, `well_report_section.svg`, one per price series) and inline in the HTML, which prints with no drawing split across two pages; `reports/pad_report.*` with `pad_report_progress.svg`. Each run is a line in the audit log with the report's ID and what it still lacks. The rendered text passes the same vocabulary check as every client report before it is written.
|
|
8
|
+
|
|
9
|
+
**The number to check**: the summary against the drilling record it came from (total depth, feet drilled, days, hours on bottom - the same numbers, read as delivered); the well-path table against the survey stations (minimum curvature, the dogleg in degrees per 30 m); a mapped top's depth below the reference against the two contours the location lies between, subtracted from the declared reference elevation; the cost against the Economics tab, which is where it is declared.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: a declared number - the surface location, the reference elevation, the US Well Number, a day rate, an item, the AFE - is printed as declared, with its source, and never as measured; a mapped top at the well - a contour map is read as the two contours the surface location lies between, and a well that deviates meets each top somewhere else; a well path drawn without a survey - with no survey attached, the section says so; a missing part as zero - a well with no surface location, no survey or no declared cost has the part left out and named under "What this report will not say", with what would supply it; a price as the cause of a cost.
|
|
@@ -44,6 +44,7 @@ INDEX: List[tuple] = [
|
|
|
44
44
|
('dlis', 'The DLIS log', 'a well log as it is delivered (API RP66 V1), read as the standard lays it out: the frames, the channels, what was skipped; into the strata survey'),
|
|
45
45
|
('historian', 'The historian pull', 'an operator\'s gauge history out of the PI System through PI Web API, read-only, every value with its archive time and flags'),
|
|
46
46
|
('map', 'The site map', 'everything with a position in one frame: the placed wells, the structure layers, the stress directions, the stations and response areas over the state and county outline; what every layer says about any spot'),
|
|
47
|
+
('well-report', 'The client well report', 'one printable document per well and one for the pad: the drilling record, depth events, progress chart, location and structure, well path and cost, each number with its source'),
|
|
47
48
|
('economics', 'Economics', 'each well\'s days, footage and cost by the operator\'s own declared rates, against the EIA oil and gas spot prices while it was drilled, and any series the site imports'),
|
|
48
49
|
('well3d', 'The well in 3D', 'each well\'s path from its directional survey, set among the mapped formation surfaces and contours with its picks, coloured by what was measured along it, and the drill string replayed from the record; the Volve field shipped as the worked example'),
|
|
49
50
|
('charts', 'The charts', 'a well\'s channels stacked on one axis with a linked cursor, zoom and pan from every sample, time or depth for a drilling recorder, the depth events marked, and the drilling progress of every well'),
|
|
@@ -122,6 +122,13 @@ def assemble(ws, period: str, actor: str = 'system', refresh: bool = True, catal
|
|
|
122
122
|
'the SLA month is written by the refresh with --month; none for this period')
|
|
123
123
|
item('site', '-', 'FAT/SAT protocol', _copy_family(rep, ['fat_protocol', 'sat_protocol'], os.path.join(out, 'site')), 'no protocol written (gea fat-sat, or the refresh with --sat)')
|
|
124
124
|
item('site', '-', 'Model cards and SBOM', _copy_family(rep, ['model_cards_index', 'sbom'], os.path.join(out, 'site')), 'never refreshed')
|
|
125
|
+
if any(os.path.isfile(os.path.join(rep, 'wells', w['id'], 'drilling_record.json')) for w in ws.wells()):
|
|
126
|
+
try:
|
|
127
|
+
from .well_report import write_pad
|
|
128
|
+
write_pad(ws, __version__, actor=actor)
|
|
129
|
+
except Exception as e:
|
|
130
|
+
errors.append(f'pad report: {e}')
|
|
131
|
+
item('site', '-', 'Pad report', _copy_family(rep, ['pad_report'], os.path.join(out, 'site')), 'the pad report failed to write (see errors)')
|
|
125
132
|
# -- every well -------------------------------------------------------------------------------------------------
|
|
126
133
|
for w in ws.wells():
|
|
127
134
|
wd = os.path.join(rep, 'wells', w['id'])
|
|
@@ -133,6 +140,13 @@ def assemble(ws, period: str, actor: str = 'system', refresh: bool = True, catal
|
|
|
133
140
|
item('well', w['id'], f"{w['display']}: data resilience", _copy_family(wd, ['data_resilience_report'], dst), 'no store-and-forward outage was given to the refresh')
|
|
134
141
|
item('well', w['id'], f"{w['display']}: machine vibration", _copy_family(wd, ['vibration_report'], dst),
|
|
135
142
|
'no machine record for this well (gea workspace --action vibration-report)')
|
|
143
|
+
if os.path.isfile(os.path.join(wd, 'drilling_record.json')):
|
|
144
|
+
try:
|
|
145
|
+
from .well_report import write_well
|
|
146
|
+
write_well(ws, w['id'], __version__, actor=actor)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
errors.append(f"well report {w['id']}: {e}")
|
|
149
|
+
item('well', w['id'], f"{w['display']}: client well report", _copy_family(wd, ['well_report'], dst), 'the well report failed to write (see errors)')
|
|
136
150
|
# certificate conformity: the register's standing, written here from the register as it stands
|
|
137
151
|
reg = os.path.join(ws.path, 'wells', w['id'], 'records', 'certificates.jsonl')
|
|
138
152
|
if os.path.isfile(reg):
|
|
@@ -2189,6 +2189,18 @@ def make_handler(app: App):
|
|
|
2189
2189
|
raise ApiError(400, str(e))
|
|
2190
2190
|
finally:
|
|
2191
2191
|
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
2192
|
+
if path == '/api/reports/well-report':
|
|
2193
|
+
from . import well_report as WR
|
|
2194
|
+
from . import __version__ as _v
|
|
2195
|
+
App.require(user, 'operator')
|
|
2196
|
+
if body.get('pad'):
|
|
2197
|
+
r = WR.write_pad(app.ws, _v, actor=user['name'])
|
|
2198
|
+
return self._json(200, {'report_id': r['report_id'], 'url': '/reports/pad_report.html'})
|
|
2199
|
+
wid = str(body.get('well_id', ''))
|
|
2200
|
+
if not any(w['id'] == wid for w in app.ws.wells()):
|
|
2201
|
+
raise ApiError(404, f'no well {wid}')
|
|
2202
|
+
r = WR.write_well(app.ws, wid, _v, actor=user['name'])
|
|
2203
|
+
return self._json(200, {'report_id': r['report_id'], 'missing': r['missing'], 'url': f'/reports/wells/{wid}/well_report.html'})
|
|
2192
2204
|
if path.startswith('/api/economics/'):
|
|
2193
2205
|
from . import economics as EC
|
|
2194
2206
|
import base64 as _b64
|
|
@@ -308,6 +308,9 @@ VIEWS.well = async (id) => {
|
|
|
308
308
|
else if (ga.status) h += `<p class="small">Gamma log: ${esc(ga.status)} — ${esc(ga.how || '')}.</p>`;
|
|
309
309
|
if ((dr.observations || []).length) h += `<p class="small"><b>Observations</b>: ${dr.observations.map(o => esc(o.channel + ' ' + o.what + ' in ' + o.samples.toLocaleString() + ' samples — ' + o.note)).join('; ')}.</p>`;
|
|
310
310
|
h += `<p class="small muted">${esc(dr.lines[dr.lines.length - 1])}</p></div>`; }
|
|
311
|
+
// the client well report
|
|
312
|
+
if (R.drilling_record || R.well_report) { const wr = R.well_report;
|
|
313
|
+
h += `<div class="card"><h3>Client well report <span class="small muted">${wr ? esc(wr.report_id) + ' · ' + reportLinks(dir, 'well_report') : 'not written yet'}</span></h3><p class="small muted">One printable document for the client: the drilling record, the depth events and who checked them, the progress chart, the location and the mapped structure, the well path from its survey, and the declared cost against the market — each number with where it came from, and what the report cannot say yet.</p>${wr && (wr.missing || []).length ? `<p class="small">Not in it yet: ${wr.missing.map(esc).join('; ')}.</p>` : ''}${can('operator') ? `<button id="wrRun" class="secondary" type="button">${wr ? 'Write it again' : 'Write the well report'}</button>` : ''}</div>`; }
|
|
311
314
|
// drift
|
|
312
315
|
if (R.gauge_drift_report) { const ev = R.gauge_drift_report.evaluation, dq = R.gauge_drift_report.data_quality || {};
|
|
313
316
|
h += `<div class="card"><h3>Gauge drift and reconciliation <span class="small muted">${esc(R.gauge_drift_report.report_id)} · ${reportLinks(dir, 'gauge_drift_report')}</span></h3>`;
|
|
@@ -361,6 +364,7 @@ VIEWS.well = async (id) => {
|
|
|
361
364
|
$('#main').innerHTML = h;
|
|
362
365
|
const run = async (args, label) => { try { const j = await post('/api/jobs', {args, label}); toast(label + ': job ' + j.id, 'ok'); watch(j.id, () => route()); } catch (e) { toast(e.message, 'err'); } };
|
|
363
366
|
const wsPath = (await get('/api/overview')).workspace.path;
|
|
367
|
+
const wrb = $('#wrRun'); if (wrb) wrb.onclick = async () => { wrb.disabled = true; try { const j = await post('/api/reports/well-report', {well_id: id}); toast('Well report written: ' + j.report_id, 'ok'); route(); } catch (e) { toast(e.message, 'err'); wrb.disabled = false; } };
|
|
364
368
|
const b1 = $('#runDrift'); if (b1) b1.onclick = () => run(['workspace', '--path', wsPath, '--action', 'refresh', '--actor', me.name], 'drift report (all wells)');
|
|
365
369
|
intervalCardBind(id); slipCardBind(id);
|
|
366
370
|
const b2 = $('#runAlarms'); if (b2) b2.onclick = () => run(['workspace', '--path', wsPath, '--action', 'refresh', '--actor', me.name], 'alarms (all wells)');
|
|
@@ -1577,11 +1581,15 @@ VIEWS.reports = async () => {
|
|
|
1577
1581
|
h += `<div class="card"><h3>Month-end packet <span class="small muted">the month's deliverable, assembled once</span></h3><p class="small muted">The refresh for the month, then every report - each well's drift, alarm month, well tests, shut-ins, vibration and certificate conformity; each site's SRA packet and site report; every station and track; the accuracy statement and the SLA month - collected into one dated folder and one archive with an index (INCLUDED with the files, or NOT AVAILABLE with the reason) and a manifest of every file's hash. Nothing is recomputed in the index.</p>
|
|
1578
1582
|
${ME.packets.length ? `<table><thead><tr><th>Period</th><th>Closed</th><th>Generated</th><th>Included</th><th>Not available</th><th>Failed</th><th>Files</th><th>Index</th><th>Archive</th></tr></thead><tbody>${ME.packets.slice().reverse().map(p => `<tr><td>${esc(p.period)}</td><td>${p.closed ? 'yes' : statusPill('warn', 'OPEN')}</td><td class="small">${tz(p.generated_utc)}</td><td>${p.included}</td><td>${p.not_available}</td><td>${p.errors.length}</td><td>${p.n_files}</td><td><a href="/reports/month_end/${esc(p.period)}/INDEX.html" target="_blank" rel="noopener">INDEX.html</a></td><td class="mono small">${p.present ? `<a href="/reports/month_end/${esc(p.archive.split(/[\\/]/).pop())}" target="_blank" rel="noopener">${esc(p.archive.split(/[\\/]/).pop())}</a>` : 'archive missing'}</td></tr>`).join('')}</tbody></table>` : `<p class="muted small">No packet yet.</p>`}
|
|
1579
1583
|
${can('operator') ? `<div class="row"><label>Period (YYYY-MM)<input id="mePeriod" value="${esc(ME.previous)}" style="width:8em"></label><button id="meRun" type="button">Assemble the packet</button>${can('admin') ? `<label>Monthly on day<input id="meDay" type="number" min="1" max="28" value="${esc(ME.schedule ? ME.schedule.monthly_day : 1)}" style="width:4em"></label><label>at (UTC)<input id="meAt" value="${esc(ME.schedule ? ME.schedule.daily_at : '03:00')}" style="width:6em"></label><button class="secondary" id="meSched" type="button">Schedule monthly</button><span class="small muted">${ME.schedule ? `scheduled: day ${esc(ME.schedule.monthly_day)} at ${esc(ME.schedule.daily_at)} UTC, the previous month` : 'not scheduled'}</span>` : ''}</div>` : need('operator')}</div>`;
|
|
1584
|
+
h += `<div class="card"><h3>Client well reports <span class="small muted">one printable document per well, and one for the pad</span></h3><p class="small muted">Assembled from what the workspace holds — drilling record, depth events, location and structure, well path, declared cost and the market — with drawings made from the report's own tables. Each says what it cannot say yet and what would supply it.</p>
|
|
1585
|
+
<p class="small">${r.site.includes('pad_report.html') ? `Pad report · ${['html', 'md', 'json'].map(x => `<a href="/reports/pad_report.${x}" target="_blank" rel="noopener">${x}</a>`).join(' · ')}` : '<span class="muted">No pad report yet.</span>'}</p>
|
|
1586
|
+
${can('operator') ? `<div class="row"><button data-wr="__pad__" type="button">Write the pad report</button>${Object.keys(r.wells).map(w => `<button class="secondary" data-wr="${esc(w)}" type="button">${esc(w)}</button>`).join('')}</div>` : ''}</div>`;
|
|
1580
1587
|
h += `<div class="card"><h3>Site</h3><p class="mono small">${r.site.map(f => `<a href="/reports/${esc(f)}" target="_blank" rel="noopener">${esc(f)}</a>`).join(' · ') || 'none yet'}</p></div>`;
|
|
1581
1588
|
for (const [w, files] of Object.entries(r.wells)) h += `<div class="card"><h3><a href="#/wells/${esc(w)}">${esc(w)}</a></h3><p class="mono small">${files.map(f => `<a href="/reports/wells/${esc(w)}/${esc(f)}" target="_blank" rel="noopener">${esc(f)}</a>`).join(' · ')}</p></div>`;
|
|
1582
1589
|
$('#main').innerHTML = h;
|
|
1583
1590
|
const meRun = $('#meRun'); if (meRun) meRun.onclick = async () => { meRun.disabled = true; try { const j = await post('/api/month-end/run', {period: $('#mePeriod').value.trim()}); toast('Month-end packet started: job ' + j.id, 'ok'); watch(j.id, () => route()); } catch (e) { toast(e.message, 'err'); meRun.disabled = false; } };
|
|
1584
1591
|
const meSched = $('#meSched'); if (meSched) meSched.onclick = async () => { try { const e = await post('/api/month-end/schedule', {day: +$('#meDay').value, at: $('#meAt').value}); toast(`Scheduled monthly: day ${e.monthly_day} at ${e.daily_at} UTC`, 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
|
|
1592
|
+
document.querySelectorAll('[data-wr]').forEach(b => b.onclick = async () => { b.disabled = true; try { const k = b.dataset.wr; const j = await post('/api/reports/well-report', k === '__pad__' ? {pad: true} : {well_id: k}); toast('Written: ' + j.report_id, 'ok'); route(); } catch (e) { toast(e.message, 'err'); b.disabled = false; } });
|
|
1585
1593
|
document.querySelectorAll('[data-run]').forEach(b => b.onclick = async () => { const k = b.dataset.run; try { let j; if (k === 'refresh') j = await post('/api/refresh', {}); else if (k === 'sla') { const m = $('#slaMonth').value; if (!m) return toast('give the month', 'err'); j = await post('/api/refresh', {month: m}); } else if (k === 'model-cards') j = await post('/api/jobs', {args: ['model-cards', '--out', o.workspace.path + '/reports'], label: 'model cards'}); else j = await post('/api/verify', {what: k}); toast('Started: job ' + j.id, 'ok'); watch(j.id, () => route()); } catch (e) { toast(e.message, 'err'); } });
|
|
1586
1594
|
};
|
|
1587
1595
|
|