gea-program 0.14.0__tar.gz → 0.15.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.14.0 → gea_program-0.15.0}/PKG-INFO +23 -2
- {gea_program-0.14.0 → gea_program-0.15.0}/README.md +22 -1
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/__init__.py +1 -1
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/__main__.py +48 -2
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/acceptance_tests.py +136 -1
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/certificates.py +31 -2
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/client_reports.py +22 -4
- gea_program-0.15.0/gea/conformance.py +230 -0
- gea_program-0.15.0/gea/help/conformance.md +11 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/helplib.py +1 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/service.py +8 -2
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/web/app.html +3 -3
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/PKG-INFO +23 -2
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/SOURCES.txt +2 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/pyproject.toml +1 -1
- {gea_program-0.14.0 → gea_program-0.15.0}/LICENSE +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/bench.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/blind_harness.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/cli.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/config_versioning.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/correlation.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/dashboard.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/deviation.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/doctor.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/earth_model.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/example_register_map.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/fat_sat.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/files.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/follower.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/forward_model.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/gamma.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/geodesy.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/alarms.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/data-in.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/doctor.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/drift.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/files.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/instruments.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/notifications.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/osdu.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/patches.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/quality.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/seismic.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/site.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/sites.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/sra.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/start.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/transients.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/vibration.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/housekeeping.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/jobs.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/live_ports.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/loadtest.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/modbus.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/model_card.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/notify.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/opcua_port.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/operator_app.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/osdu.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/patches.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/permits.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/ports.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/ppdm.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/project.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/quakeml.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reconciler.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sample_record.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sbom.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/segy.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_array.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_film.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_response.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_track.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/service_life.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/shell.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/shut_in.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sla_report.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/sra.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/store_forward.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/strata_join.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/supervisor.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/survey_view.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/telemetry.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/tool_library.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/transient.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/vibration.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/well_assembler.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/wits0.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/witsml.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea/workspace.py +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.14.0 → gea_program-0.15.0}/setup.cfg +0 -0
- {gea_program-0.14.0 → gea_program-0.15.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.15.0
|
|
4
4
|
Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
|
|
5
5
|
Author: Daniel T. Murphy
|
|
6
6
|
License: MPL-2.0
|
|
@@ -125,7 +125,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
125
125
|
gea client-report --report accuracy --out client_report
|
|
126
126
|
gea model-cards --out model_cards
|
|
127
127
|
gea sbom --out sbom
|
|
128
|
-
gea accept # the product gate (
|
|
128
|
+
gea accept # the product gate (333 checks)
|
|
129
129
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
130
130
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
131
131
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -619,6 +619,27 @@ promoted; the group and the support are declarations; a matched frequency
|
|
|
619
619
|
is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
|
|
620
620
|
pump; `gea help vibration`.
|
|
621
621
|
|
|
622
|
+
### Conformity, in the standards' words
|
|
623
|
+
|
|
624
|
+
The drift report's "bias inside the instrument's class" is a statement of
|
|
625
|
+
conformity, and ISO/IEC 17025:2017 7.8.6 says what one carries: the result it
|
|
626
|
+
applies to, the specification, and the decision rule - which has to take
|
|
627
|
+
the uncertainty into account. The report now states it that way: the tag's
|
|
628
|
+
bias over the window with its sample count, the certificate's accuracy
|
|
629
|
+
class as the tolerance limit, the bias's expanded uncertainty (k = 2) as the
|
|
630
|
+
guard band, and the ILAC-G8:09/2019 non-binary rule - PASS, CONDITIONAL
|
|
631
|
+
PASS, CONDITIONAL FAIL or FAIL, with the acceptance limit printed; without
|
|
632
|
+
an uncertainty it falls back to simple acceptance and says so. The
|
|
633
|
+
certificate register takes what 7.8.4 asks the paper to carry (the expanded
|
|
634
|
+
uncertainty with k and probability, the traceability statement as worded,
|
|
635
|
+
the conditions, the calibration date, the method, who authorised it) and
|
|
636
|
+
`gea certificates --action conformance` prints each certificate against
|
|
637
|
+
the fourteen items of 7.8.2.1 and 7.8.4.1 - CARRIED, NOT CARRIED, NOT
|
|
638
|
+
RECORDED - with COMPLETE or INCOMPLETE and the missing clauses named. The
|
|
639
|
+
well page's filing form takes the items and its table shows U (k) and the
|
|
640
|
+
standing. `gea conformance --action decide` states one decision. `gea help
|
|
641
|
+
conformance`.
|
|
642
|
+
|
|
622
643
|
### Audit / Update
|
|
623
644
|
|
|
624
645
|
The Audit / Update tab holds three things. The whole audit log
|
|
@@ -86,7 +86,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
86
86
|
gea client-report --report accuracy --out client_report
|
|
87
87
|
gea model-cards --out model_cards
|
|
88
88
|
gea sbom --out sbom
|
|
89
|
-
gea accept # the product gate (
|
|
89
|
+
gea accept # the product gate (333 checks)
|
|
90
90
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
91
91
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
92
92
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -580,6 +580,27 @@ promoted; the group and the support are declarations; a matched frequency
|
|
|
580
580
|
is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
|
|
581
581
|
pump; `gea help vibration`.
|
|
582
582
|
|
|
583
|
+
### Conformity, in the standards' words
|
|
584
|
+
|
|
585
|
+
The drift report's "bias inside the instrument's class" is a statement of
|
|
586
|
+
conformity, and ISO/IEC 17025:2017 7.8.6 says what one carries: the result it
|
|
587
|
+
applies to, the specification, and the decision rule - which has to take
|
|
588
|
+
the uncertainty into account. The report now states it that way: the tag's
|
|
589
|
+
bias over the window with its sample count, the certificate's accuracy
|
|
590
|
+
class as the tolerance limit, the bias's expanded uncertainty (k = 2) as the
|
|
591
|
+
guard band, and the ILAC-G8:09/2019 non-binary rule - PASS, CONDITIONAL
|
|
592
|
+
PASS, CONDITIONAL FAIL or FAIL, with the acceptance limit printed; without
|
|
593
|
+
an uncertainty it falls back to simple acceptance and says so. The
|
|
594
|
+
certificate register takes what 7.8.4 asks the paper to carry (the expanded
|
|
595
|
+
uncertainty with k and probability, the traceability statement as worded,
|
|
596
|
+
the conditions, the calibration date, the method, who authorised it) and
|
|
597
|
+
`gea certificates --action conformance` prints each certificate against
|
|
598
|
+
the fourteen items of 7.8.2.1 and 7.8.4.1 - CARRIED, NOT CARRIED, NOT
|
|
599
|
+
RECORDED - with COMPLETE or INCOMPLETE and the missing clauses named. The
|
|
600
|
+
well page's filing form takes the items and its table shows U (k) and the
|
|
601
|
+
standing. `gea conformance --action decide` states one decision. `gea help
|
|
602
|
+
conformance`.
|
|
603
|
+
|
|
583
604
|
### Audit / Update
|
|
584
605
|
|
|
585
606
|
The Audit / Update tab holds three things. The whole audit log
|
|
@@ -409,6 +409,14 @@ def main(argv=None) -> int:
|
|
|
409
409
|
|
|
410
410
|
p_osdu = sub.add_parser("osdu", help="the OSDU-shaped export: the self-test on a labelled site")
|
|
411
411
|
p_osdu.add_argument("--action", choices=["selftest"], default="selftest")
|
|
412
|
+
p_cf = sub.add_parser("conformance", help="ISO/IEC 17025 7.8 certificate items and the ILAC-G8 decision rules: the self-test, or one decision")
|
|
413
|
+
p_cf.add_argument("--action", choices=["selftest", "decide"], default="selftest")
|
|
414
|
+
p_cf.add_argument("--value", type=float, default=None, help="decide: the measured value (a deviation; the tolerance is symmetric)")
|
|
415
|
+
p_cf.add_argument("--tolerance", type=float, default=None, help="decide: the tolerance limit TL")
|
|
416
|
+
p_cf.add_argument("--uncertainty", type=float, default=None, help="decide: the expanded uncertainty U of the value (omit: simple acceptance)")
|
|
417
|
+
p_cf.add_argument("--k", type=float, default=2.0)
|
|
418
|
+
p_cf.add_argument("--rule", choices=["simple-acceptance", "binary-guard-band", "non-binary-guard-band"], default="non-binary-guard-band")
|
|
419
|
+
p_cf.add_argument("--unit", type=str, default="")
|
|
412
420
|
p_vib = sub.add_parser("vibration", help="machine vibration: ISO 20816-3 zone and bearing envelope analysis on a record, or the self-test")
|
|
413
421
|
p_vib.add_argument("--action", choices=["selftest", "assess"], default="selftest")
|
|
414
422
|
p_vib.add_argument("--record", type=str, default=None, help="assess: the record (CSV with a time column, or miniSEED/SAC)")
|
|
@@ -522,7 +530,7 @@ def main(argv=None) -> int:
|
|
|
522
530
|
|
|
523
531
|
p_ce = sub.add_parser("certificates", help="calibration certificates per instrument: list, file one, status against today")
|
|
524
532
|
p_ce.add_argument("--register", type=str, required=True, help="the well's certificates.jsonl")
|
|
525
|
-
p_ce.add_argument("--action", choices=["list", "add", "status"], default="list")
|
|
533
|
+
p_ce.add_argument("--action", choices=["list", "add", "status", "conformance"], default="list")
|
|
526
534
|
p_ce.add_argument("--tag", type=str, default=None)
|
|
527
535
|
p_ce.add_argument("--tags", type=str, default=None, help="comma-separated tags for --action status")
|
|
528
536
|
p_ce.add_argument("--serial", type=str, default="")
|
|
@@ -536,6 +544,17 @@ def main(argv=None) -> int:
|
|
|
536
544
|
p_ce.add_argument("--doc", type=str, default=None, help="the certificate file (hashed, not copied)")
|
|
537
545
|
p_ce.add_argument("--note", type=str, default="")
|
|
538
546
|
p_ce.add_argument("--actor", type=str, default="cli")
|
|
547
|
+
p_ce.add_argument("--calibrated", type=str, default="", help="ISO/IEC 17025 7.8.2.1 i: the date the calibration was performed")
|
|
548
|
+
p_ce.add_argument("--method", type=str, default="", help="7.8.2.1 f: the method used")
|
|
549
|
+
p_ce.add_argument("--authorised-by", type=str, default="", help="7.8.2.1 o: who authorised the certificate")
|
|
550
|
+
p_ce.add_argument("--uncertainty", type=float, default=None, help="7.8.4.1 a: the expanded uncertainty U, in --uncertainty-unit (default the result's unit)")
|
|
551
|
+
p_ce.add_argument("--uncertainty-unit", type=str, default="")
|
|
552
|
+
p_ce.add_argument("--k", type=float, default=None, help="7.8.4.1 a: the coverage factor U is stated at")
|
|
553
|
+
p_ce.add_argument("--probability", type=float, default=None, help="7.8.4.1 a: the coverage probability as a fraction (0.95)")
|
|
554
|
+
p_ce.add_argument("--conditions", type=str, default="", help="7.8.4.1 b: the conditions the calibration was made under")
|
|
555
|
+
p_ce.add_argument("--traceability", type=str, default="", help="7.8.4.1 c: the statement of metrological traceability, as the certificate words it")
|
|
556
|
+
p_ce.add_argument("--adjustment", type=str, default="", help="7.8.4.1 d: results before and after adjustment, if the certificate carries them")
|
|
557
|
+
p_ce.add_argument("--decision-rule", type=str, default="", help="7.8.6: the decision rule the certificate's conformity statement names, if any")
|
|
539
558
|
|
|
540
559
|
p_tr = sub.add_parser("transient", help="shut-in detection and build-up analysis (Horner + Bourdet derivative with a bootstrap band)")
|
|
541
560
|
p_tr.add_argument("--file", type=str, required=True, help="historian file")
|
|
@@ -773,7 +792,10 @@ def main(argv=None) -> int:
|
|
|
773
792
|
if a.action == "add":
|
|
774
793
|
try:
|
|
775
794
|
print(_json.dumps(reg.add(a.tag or "", a.serial, a.certificate, a.issued or "", a.valid_until or "", a.actor, a.lab,
|
|
776
|
-
a.accuracy_pct_fs, a.full_scale, a.unit, a.doc, a.note
|
|
795
|
+
a.accuracy_pct_fs, a.full_scale, a.unit, a.doc, a.note,
|
|
796
|
+
calibrated_utc=a.calibrated, method=a.method, authorised_by=a.authorised_by, uncertainty=a.uncertainty,
|
|
797
|
+
uncertainty_unit=a.uncertainty_unit, uncertainty_k=a.k, uncertainty_probability=a.probability,
|
|
798
|
+
conditions=a.conditions, traceability=a.traceability, adjustment=a.adjustment, decision_rule=a.decision_rule), indent=1))
|
|
777
799
|
except (ValueError, TypeError) as e:
|
|
778
800
|
raise SystemExit(f"certificates: {e}")
|
|
779
801
|
return 0
|
|
@@ -784,6 +806,13 @@ def main(argv=None) -> int:
|
|
|
784
806
|
print(f"{r['tag_id']:24s} {r['status']:9s} {r.get('certificate_id') or '-':16s} until {r.get('valid_until_utc') or '-'} {('' if r['days_left'] is None else str(r['days_left']) + ' d')}")
|
|
785
807
|
print(_json.dumps(CertificateRegister.summary(rows)))
|
|
786
808
|
return 0 if CertificateRegister.summary(rows)['all_valid'] else 1
|
|
809
|
+
if a.action == "conformance":
|
|
810
|
+
from .conformance import report_lines
|
|
811
|
+
c = reg.conformance()
|
|
812
|
+
for r in c["rows"]:
|
|
813
|
+
print("\n".join(report_lines(r)))
|
|
814
|
+
print(f"certificates: {c['n']} filed, {c['complete']} COMPLETE against ISO/IEC 17025:2017 7.8" + (f"; incomplete: {', '.join(c['incomplete'])}" if c['incomplete'] else ''))
|
|
815
|
+
return 0 if not c["incomplete"] else 1
|
|
787
816
|
for e in reg.list():
|
|
788
817
|
print(f"{e['tag_id']:24s} {e['serial']:12s} {e['certificate_id']:16s} {e['issued_utc'][:10]} to {e['valid_until_utc'][:10]} ±{e.get('accuracy_pct_fs') or '-'} % FS {e.get('lab', '')}")
|
|
789
818
|
print(f"certificates: {len(reg.list())} filed")
|
|
@@ -1524,6 +1553,23 @@ def main(argv=None) -> int:
|
|
|
1524
1553
|
return 0
|
|
1525
1554
|
print(f"notifications: {'configured' if n.cfg else 'not configured'}" + (f" - {len(n.cfg['channels'])} channel(s), {len(n.cfg['rules'])} rule(s), quiet {n.cfg['quiet_s']} s" if n.cfg else " (gea notify --example)"))
|
|
1526
1555
|
return 0
|
|
1556
|
+
elif a.cmd == "conformance":
|
|
1557
|
+
from . import conformance as _C
|
|
1558
|
+
if a.action == "decide":
|
|
1559
|
+
if a.value is None or a.tolerance is None:
|
|
1560
|
+
raise SystemExit("conformance --action decide needs --value and --tolerance")
|
|
1561
|
+
d = _C.decide(a.value, a.tolerance, a.uncertainty, rule=a.rule, k=a.k if a.uncertainty is not None else None,
|
|
1562
|
+
probability=(0.95 if abs(a.k - 2.0) < 1e-9 else None) if a.uncertainty is not None else None, unit=a.unit)
|
|
1563
|
+
print(d["statement"]); print(f" {d['rule_text']}")
|
|
1564
|
+
return 0 if d["outcome"] in ("PASS", "CONDITIONAL PASS") else 1
|
|
1565
|
+
r = _C.selftest()
|
|
1566
|
+
print(f"conformance selftest [{r['label']}]: {r['status']}")
|
|
1567
|
+
for k, v in r["checks"].items():
|
|
1568
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
1569
|
+
print("\n".join(_C.report_lines(r["full"])))
|
|
1570
|
+
for d in r["decisions"]:
|
|
1571
|
+
print(" " + d["statement"])
|
|
1572
|
+
return 0 if r["status"] == "OK" else 1
|
|
1527
1573
|
elif a.cmd == "vibration":
|
|
1528
1574
|
from . import vibration as _V
|
|
1529
1575
|
if a.action == "assess":
|
|
@@ -2420,7 +2420,7 @@ def section_ah_band2(tmp: str) -> None:
|
|
|
2420
2420
|
and Path(wsp, "wells", w1["id"], "records", "certificates", "CERT-1_cert1.txt").exists()
|
|
2421
2421
|
and st2["P_raw_psi_S2"] == 48 and st2["P_raw_psi_S1"] == 240 and gd["instruments"]["swap_notes"][0]["samples_excluded"] == 192
|
|
2422
2422
|
and [c["status"] for c in inst["certificate_status"]] == ["VALID", "VALID"] and not [c for c in inst["candidates"] if c["tag_id"] == "P_raw_psi_S2"]
|
|
2423
|
-
and "Instruments: sensor swaps" in drift_html and "CERT-77" in drift_html and "
|
|
2423
|
+
and "Instruments: sensor swaps" in drift_html and "CERT-77" in drift_html and "Conformity" in drift_html and "ISO/IEC 17025 7.8" in drift_html
|
|
2424
2424
|
and ptr and ptr["analyses"][0]["status"] == "OK" and abs(ptr["analyses"][0]["derived"]["k_md"] - k) / k < 0.05
|
|
2425
2425
|
and ov["wells"][0]["instruments"]["swaps"] == 1 and ov["wells"][0]["transient"]["analysed"] == 1 and ov["site"]["certificates"]["counts"]["VALID"] == 2
|
|
2426
2426
|
and "swap.add" in audit and "certificate.add" in audit and "transient.params" in audit,
|
|
@@ -5319,6 +5319,140 @@ def section_bh_vibration(tmp: str) -> None:
|
|
|
5319
5319
|
"a channel that is not there; `gea vibration --action assess` reads a miniSEED record in counts with a declared sensitivity and says "
|
|
5320
5320
|
"NOT ASSESSED without one; the self-test runs from the command line; the help page carries the boundaries and names the fault size it will not claim")
|
|
5321
5321
|
|
|
5322
|
+
|
|
5323
|
+
def section_bi_conformance(tmp: str) -> None:
|
|
5324
|
+
"""Section BI - conformity in the standards' words: each certificate against the items ISO/IEC 17025:2017
|
|
5325
|
+
7.8.2.1 and 7.8.4.1 ask it to carry, and the drift report's bias-against-class line as a statement of
|
|
5326
|
+
conformity with its ILAC-G8 decision rule named, the uncertainty as the guard band, nothing assumed."""
|
|
5327
|
+
import csv
|
|
5328
|
+
import datetime as D
|
|
5329
|
+
import http.cookiejar
|
|
5330
|
+
import subprocess as _sp
|
|
5331
|
+
import urllib.request
|
|
5332
|
+
import urllib.error
|
|
5333
|
+
import numpy as _np
|
|
5334
|
+
from . import conformance as C
|
|
5335
|
+
from . import helplib as H
|
|
5336
|
+
from .certificates import CertificateRegister
|
|
5337
|
+
from .workspace import Workspace
|
|
5338
|
+
from .service import Service, Users
|
|
5339
|
+
pkg = Path(__file__).parent
|
|
5340
|
+
d = Path(tmp, "bi"); d.mkdir()
|
|
5341
|
+
|
|
5342
|
+
# -- the self-test and the rules --------------------------------------------------------------------
|
|
5343
|
+
r = C.selftest()
|
|
5344
|
+
ok(r["status"] == "OK" and all(r["checks"].values())
|
|
5345
|
+
and [x["outcome"] for x in r["decisions"]] == ["PASS", "CONDITIONAL PASS", "CONDITIONAL FAIL", "FAIL", "PASS"]
|
|
5346
|
+
and r["decisions"][4]["rule"] == "simple-acceptance"
|
|
5347
|
+
and C.decide(8.0, 10.0, 2.0)["outcome"] == "PASS" and C.decide(8.0001, 10.0, 2.0)["outcome"] == "CONDITIONAL PASS"
|
|
5348
|
+
and C.decide(10.0, 10.0, 2.0)["outcome"] == "CONDITIONAL PASS" and C.decide(10.0001, 10.0, 2.0)["outcome"] == "CONDITIONAL FAIL"
|
|
5349
|
+
and C.decide(12.0, 10.0, 2.0)["outcome"] == "CONDITIONAL FAIL" and C.decide(12.0001, 10.0, 2.0)["outcome"] == "FAIL"
|
|
5350
|
+
and C.decide(10.0, 10.0, 2.0, rule="simple-acceptance")["outcome"] == "PASS" and C.decide(8.5, 10.0, 2.0, rule="binary-guard-band")["outcome"] == "FAIL"
|
|
5351
|
+
and len(C.CERTIFICATE_CLAUSES) == 14 and {c[0].split(" ")[0] for c in C.CERTIFICATE_CLAUSES} == {"7.8.2.1", "7.8.4.1"}
|
|
5352
|
+
and all(k in " ".join(x[1] for x in C.CERTIFICATE_CLAUSES) for k in ("uncertainty", "traceability", "conditions", "adjustment", "decision_rule", "authorised_by", "method", "calibrated_utc")),
|
|
5353
|
+
"BI1 the ILAC-G8:09/2019 rules give the four outcomes at their boundaries (PASS to TL - U, CONDITIONAL PASS to TL, CONDITIONAL FAIL to TL + U, "
|
|
5354
|
+
"FAIL above), simple acceptance ignores U and says so, the binary rule fails inside the guard band; the certificate items are the fourteen of "
|
|
5355
|
+
"ISO/IEC 17025:2017 7.8.2.1 and 7.8.4.1 - identification, laboratory, item, dates, result, method, authorisation, uncertainty with its "
|
|
5356
|
+
"coverage factor, conditions, traceability, adjustment, decision rule - and a full certificate is COMPLETE, a thin one INCOMPLETE with the "
|
|
5357
|
+
"missing clauses named, an entry filed before the fields existed NOT RECORDED rather than NOT CARRIED")
|
|
5358
|
+
|
|
5359
|
+
# -- the register: the fields recorded from the paper, nothing invented ------------------------------
|
|
5360
|
+
reg = CertificateRegister(str(d / "certs.jsonl"))
|
|
5361
|
+
e1 = reg.add("P1", "S1", "C-1", "2026-01-10", "2027-01-10", "tester", "Acme Cal", 0.1, 10000.0, "psi",
|
|
5362
|
+
uncertainty=2.5, uncertainty_k=2.0, uncertainty_probability=0.95, traceability="NIST via DWT 1234", conditions="23 ± 1 °C",
|
|
5363
|
+
calibrated_utc="2026-01-09", method="comparison", authorised_by="J. Doe", adjustment="as found / as left", decision_rule="simple acceptance")
|
|
5364
|
+
e2 = reg.add("P2", "S2", "C-2", "2026-01-10", "2027-01-10", "tester", "Acme Cal", 0.1, 10000.0, "psi")
|
|
5365
|
+
bad = []
|
|
5366
|
+
for kw in ({"uncertainty_k": 0}, {"uncertainty_probability": 1.5}, {"uncertainty": -1}, {"nonsense": "x"}):
|
|
5367
|
+
try:
|
|
5368
|
+
reg.add("P3", "S3", "C-3", "2026-01-10", "2027-01-10", "tester", **kw); bad.append(False)
|
|
5369
|
+
except ValueError:
|
|
5370
|
+
bad.append(True)
|
|
5371
|
+
st = {x["tag_id"]: x for x in reg.status(["P1", "P2"], now=D.datetime(2026, 6, 1, tzinfo=D.timezone.utc))}
|
|
5372
|
+
conf = reg.conformance()
|
|
5373
|
+
r1 = _sp.run([sys.executable, "-m", "gea", "certificates", "--register", str(d / "certs.jsonl"), "--action", "conformance"], capture_output=True, text=True)
|
|
5374
|
+
r2 = _sp.run([sys.executable, "-m", "gea", "certificates", "--register", str(d / "cli.jsonl"), "--action", "add", "--tag", "P9", "--serial", "S9", "--certificate", "C-9",
|
|
5375
|
+
"--issued", "2026-01-10", "--valid-until", "2027-01-10", "--lab", "Cal Lab GmbH", "--accuracy-pct-fs", "0.05", "--full-scale", "5000", "--unit", "psi", "--uncertainty", "1.2",
|
|
5376
|
+
"--k", "2", "--probability", "0.95", "--traceability", "PTB via transfer standard 7", "--conditions", "21 °C", "--calibrated", "2026-01-08",
|
|
5377
|
+
"--method", "comparison", "--authorised-by", "A. B."], capture_output=True, text=True)
|
|
5378
|
+
r3 = _sp.run([sys.executable, "-m", "gea", "certificates", "--register", str(d / "cli.jsonl"), "--action", "conformance"], capture_output=True, text=True)
|
|
5379
|
+
r4 = _sp.run([sys.executable, "-m", "gea", "conformance", "--action", "decide", "--value", "11", "--tolerance", "10", "--uncertainty", "2", "--unit", "psi"], capture_output=True, text=True)
|
|
5380
|
+
r5 = _sp.run([sys.executable, "-m", "gea", "conformance"], capture_output=True, text=True)
|
|
5381
|
+
ok(e1["uncertainty"] == 2.5 and e1["uncertainty_k"] == 2.0 and e1["traceability"] == "NIST via DWT 1234" and e2["uncertainty"] is None and e2["traceability"] == ""
|
|
5382
|
+
and all(bad) and st["P1"]["iso17025"]["status"] == "COMPLETE" and st["P1"]["iso17025"]["carried"] == 14
|
|
5383
|
+
and st["P2"]["iso17025"]["status"] == "INCOMPLETE" and any("7.8.4.1 c" in m for m in st["P2"]["iso17025"]["missing"]) and st["P2"]["uncertainty"] is None
|
|
5384
|
+
and conf["n"] == 2 and conf["complete"] == 1 and conf["incomplete"] == ["C-2"]
|
|
5385
|
+
and r1.returncode == 1 and "C-1 (serial S1, tag P1): COMPLETE - 14 of 14" in r1.stdout and "NOT CARRIED a statement identifying how the measurements are metrologically traceable" in r1.stdout
|
|
5386
|
+
and r2.returncode == 0 and r3.returncode == 0 and "1 COMPLETE" in r3.stdout
|
|
5387
|
+
and r4.returncode == 1 and "CONDITIONAL FAIL" in r4.stdout and "acceptance limit 8 psi" in r4.stdout
|
|
5388
|
+
and r5.returncode == 0 and "conformance selftest [SELF_TEST]: OK" in r5.stdout,
|
|
5389
|
+
"BI2 the register takes what 7.8.4 asks the paper to carry - the expanded uncertainty with its coverage factor and probability, the "
|
|
5390
|
+
"traceability statement as worded, the conditions, the calibration date, the method, who authorised it, the adjustment results, the "
|
|
5391
|
+
"decision rule - rejects a k of zero, a probability above one, a negative uncertainty and a field it does not know, leaves empty what was "
|
|
5392
|
+
"not filed, and every status row carries the certificate's 7.8 standing; `gea certificates --action conformance` prints it item by item "
|
|
5393
|
+
"and exits 1 while any certificate is incomplete; `gea conformance --action decide` states one decision from the command line")
|
|
5394
|
+
|
|
5395
|
+
# -- the drift report: the statement of conformity with its rule named ----------------------------------
|
|
5396
|
+
rng = _np.random.default_rng(5)
|
|
5397
|
+
n = 240
|
|
5398
|
+
t0 = D.datetime(2026, 3, 1, tzinfo=D.timezone.utc)
|
|
5399
|
+
hist = d / "hist.csv"
|
|
5400
|
+
base = 3000.0 + 0.2 * _np.sin(_np.arange(n) / 24.0)
|
|
5401
|
+
with open(hist, "w", newline="") as f:
|
|
5402
|
+
w = csv.writer(f); w.writerow(["timestamp_utc", "P_raw_psi_S1", "P_raw_psi_S2", "T_raw_F_S1"])
|
|
5403
|
+
for i in range(n):
|
|
5404
|
+
w.writerow([(t0 + D.timedelta(hours=i)).strftime("%Y-%m-%dT%H:%M:%SZ"), round(base[i] + rng.normal(0, 0.8), 2), round(base[i] + 1.5 + rng.normal(0, 0.8), 2), round(80 + 0.1 * rng.normal(), 2)])
|
|
5405
|
+
wsp = str(d / "ws")
|
|
5406
|
+
ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
5407
|
+
w1 = ws.add_well_file(str(hist), display="Well BI", actor="tester")
|
|
5408
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op1", "operator-pass-1", "operator")
|
|
5409
|
+
svc = Service(wsp, host="127.0.0.1", port=0, scheduler=False).start()
|
|
5410
|
+
base_url = svc.url.rstrip("/")
|
|
5411
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
5412
|
+
|
|
5413
|
+
def call(method, path, body=None):
|
|
5414
|
+
req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
5415
|
+
req.add_header("Content-Type", "application/json")
|
|
5416
|
+
if method == "POST":
|
|
5417
|
+
req.add_header("X-GEA-Action", "1")
|
|
5418
|
+
try:
|
|
5419
|
+
with opener.open(req, timeout=600) as rr:
|
|
5420
|
+
return rr.status, json.loads(rr.read())
|
|
5421
|
+
except urllib.error.HTTPError as ex:
|
|
5422
|
+
return ex.code, json.loads(ex.read())
|
|
5423
|
+
try:
|
|
5424
|
+
call("POST", "/api/login", {"name": "op1", "password": "operator-pass-1"})
|
|
5425
|
+
s_c1, _ = call("POST", f"/api/wells/{w1['id']}/certificates", {"tag_id": "P_raw_psi_S1", "serial": "SN-1", "certificate_id": "CERT-A", "issued_utc": "2026-01-01",
|
|
5426
|
+
"valid_until_utc": "2027-01-01", "lab": "LabX", "accuracy_pct_fs": 0.02, "full_scale": 10000, "unit": "psi",
|
|
5427
|
+
"uncertainty": "0.5", "uncertainty_k": "2", "uncertainty_probability": "0.95", "traceability": "NIST via DWT",
|
|
5428
|
+
"conditions": "23 °C", "calibrated_utc": "2025-12-30", "method": "comparison", "authorised_by": "Q. A."})
|
|
5429
|
+
s_c2, _ = call("POST", f"/api/wells/{w1['id']}/certificates", {"tag_id": "P_raw_psi_S2", "serial": "SN-2", "certificate_id": "CERT-B", "issued_utc": "2026-01-01",
|
|
5430
|
+
"valid_until_utc": "2027-01-01", "lab": "LabX", "accuracy_pct_fs": 0.02, "full_scale": 10000, "unit": "psi"})
|
|
5431
|
+
call("POST", "/api/refresh", {})
|
|
5432
|
+
svc.app.runner.wait(svc.app.runner.list(1)[0]["id"], 600)
|
|
5433
|
+
inst = call("GET", f"/api/wells/{w1['id']}/instruments")[1]
|
|
5434
|
+
drift_md = Path(wsp, "reports", "wells", w1["id"], "gauge_drift_report.md").read_text(encoding="utf-8")
|
|
5435
|
+
drift_html = Path(wsp, "reports", "wells", w1["id"], "gauge_drift_report.html").read_text(encoding="utf-8")
|
|
5436
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
5437
|
+
by = inst["iso17025"]["by_certificate"]
|
|
5438
|
+
ok(s_c1 == 200 and s_c2 == 200 and inst["iso17025"]["n"] == 2 and inst["iso17025"]["complete"] == 1 and inst["iso17025"]["incomplete"] == ["CERT-B"]
|
|
5439
|
+
and by["CERT-A"]["status"] == "COMPLETE" and by["CERT-B"]["status"] == "INCOMPLETE"
|
|
5440
|
+
and "Conformity" in drift_md and "ISO/IEC 17025 7.8" in drift_md and "decision rule: non-binary-guard-band (ILAC-G8:09/2019)" in drift_md
|
|
5441
|
+
and "bias of P_raw_psi_S1 over the evaluation window" in drift_md and "with expanded uncertainty U = " in drift_md and "(k = 2, 95%)" in drift_md
|
|
5442
|
+
and "tolerance limit 2 psi" in drift_md and "acceptance limit" in drift_md and "COMPLETE (12/14)" in drift_md and "INCOMPLETE (5/14)" in drift_md and by["CERT-A"]["carried"] == 12
|
|
5443
|
+
and ("CONDITIONAL" in drift_md or "PASS" in drift_md or "FAIL" in drift_md) and "ISO/IEC 17025:2017 7.8.6" in drift_html
|
|
5444
|
+
and "ctUnc" in page_src and "ctTrace" in page_src and "uncertainty_k" in page_src and "ISO/IEC 17025 7.8" in page_src
|
|
5445
|
+
and any(t["topic"] == "conformance" for t in H.topics()) and not H.check("conformance")
|
|
5446
|
+
and "simple acceptance" in (pkg / "help" / "conformance.md").read_text(encoding="utf-8").split("will not call a measurement")[1],
|
|
5447
|
+
"BI3 through the API: a certificate filed with its uncertainty, coverage factor and traceability is COMPLETE and one filed without them is "
|
|
5448
|
+
"INCOMPLETE; the instruments answer carries every certificate's 7.8 standing; the drift report's bias line is a statement of conformity "
|
|
5449
|
+
"in 7.8.6's words - the result it applies to with its sample count, the certificate's class as the tolerance limit, the bias's expanded "
|
|
5450
|
+
"uncertainty at k = 2, the ILAC-G8 non-binary guard-band rule and the acceptance limit - beside the certificate's own 7.8 standing; the "
|
|
5451
|
+
"page's filing form takes the 7.8.4 items and its table shows U (k) and the standing; the help page names simple acceptance as the "
|
|
5452
|
+
"fallback it will not dress up")
|
|
5453
|
+
finally:
|
|
5454
|
+
svc.stop()
|
|
5455
|
+
|
|
5322
5456
|
def main() -> int:
|
|
5323
5457
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
5324
5458
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -5382,6 +5516,7 @@ def main() -> int:
|
|
|
5382
5516
|
section_bf_quakeml(tmp)
|
|
5383
5517
|
section_bg_ppdm(tmp)
|
|
5384
5518
|
section_bh_vibration(tmp)
|
|
5519
|
+
section_bi_conformance(tmp)
|
|
5385
5520
|
if _FAILS:
|
|
5386
5521
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
5387
5522
|
for f in _FAILS:
|
|
@@ -64,9 +64,14 @@ class CertificateRegister:
|
|
|
64
64
|
continue
|
|
65
65
|
return out
|
|
66
66
|
|
|
67
|
+
# what ISO/IEC 17025:2017 7.8 asks a calibration certificate to carry beyond the fields above (v0.15.0):
|
|
68
|
+
# recorded from the paper by the operator, empty when the paper does not carry it. None of it is invented.
|
|
69
|
+
CLAUSE_FIELDS = ('calibrated_utc', 'method', 'authorised_by', 'uncertainty', 'uncertainty_unit', 'uncertainty_k', 'uncertainty_probability',
|
|
70
|
+
'conditions', 'traceability', 'adjustment', 'decision_rule')
|
|
71
|
+
|
|
67
72
|
def add(self, tag_id: str, serial: str, certificate_id: str, issued_utc: str, valid_until_utc: str, actor: str,
|
|
68
73
|
lab: str = '', accuracy_pct_fs: Optional[float] = None, full_scale: Optional[float] = None, unit: str = '',
|
|
69
|
-
file_path: Optional[str] = None, note: str = '') -> dict:
|
|
74
|
+
file_path: Optional[str] = None, note: str = '', **clauses) -> dict:
|
|
70
75
|
if not (tag_id and serial and certificate_id):
|
|
71
76
|
raise ValueError('a certificate needs the tag id, the instrument serial and the certificate id')
|
|
72
77
|
issued, until = _parse(issued_utc), _parse(valid_until_utc)
|
|
@@ -79,6 +84,20 @@ class CertificateRegister:
|
|
|
79
84
|
'full_scale': (float(full_scale) if full_scale is not None else None), 'unit': unit,
|
|
80
85
|
'file': os.path.basename(file_path) if file_path else '', 'file_sha256': _sha(file_path) if file_path and os.path.isfile(file_path) else '',
|
|
81
86
|
'filed_by': actor, 'filed_utc': _iso(datetime.now(timezone.utc)), 'note': note}
|
|
87
|
+
unknown = sorted(set(clauses) - set(self.CLAUSE_FIELDS))
|
|
88
|
+
if unknown:
|
|
89
|
+
raise ValueError(f'unknown certificate field(s): {", ".join(unknown)}')
|
|
90
|
+
for k in self.CLAUSE_FIELDS:
|
|
91
|
+
v = clauses.get(k)
|
|
92
|
+
if k in ('uncertainty', 'uncertainty_k', 'uncertainty_probability') and v not in (None, ''):
|
|
93
|
+
v = float(v)
|
|
94
|
+
if k == 'uncertainty_k' and not (0 < v <= 10):
|
|
95
|
+
raise ValueError('the coverage factor k is between 0 and 10')
|
|
96
|
+
if k == 'uncertainty_probability' and not (0 < v < 1):
|
|
97
|
+
raise ValueError('the coverage probability is a fraction between 0 and 1')
|
|
98
|
+
if k == 'uncertainty' and v <= 0:
|
|
99
|
+
raise ValueError('the expanded uncertainty is a positive value')
|
|
100
|
+
e[k] = (v if v not in (None,) else None) if k in ('uncertainty', 'uncertainty_k', 'uncertainty_probability') else (str(v) if v is not None else '')
|
|
82
101
|
if any(x['certificate_id'] == certificate_id and x['serial'] == serial for x in self.list()):
|
|
83
102
|
raise ValueError(f'certificate already filed: {certificate_id} for serial {serial}')
|
|
84
103
|
os.makedirs(os.path.dirname(self.path) or '.', exist_ok=True)
|
|
@@ -110,11 +129,21 @@ class CertificateRegister:
|
|
|
110
129
|
continue
|
|
111
130
|
days = (_parse(e['valid_until_utc']) - now).total_seconds() / 86400.0
|
|
112
131
|
st = 'EXPIRED' if days < 0 else ('EXPIRING' if days <= self.warn_days else 'VALID')
|
|
132
|
+
from .conformance import certificate_conformance
|
|
133
|
+
cf = certificate_conformance(e)
|
|
113
134
|
rows.append({'tag_id': tag, 'status': st, 'days_left': int(days), 'certificate_id': e['certificate_id'], 'serial': e['serial'],
|
|
114
135
|
'accuracy_pct_fs': e.get('accuracy_pct_fs'), 'full_scale': e.get('full_scale'), 'unit': e.get('unit'),
|
|
115
|
-
'valid_until_utc': e['valid_until_utc'], 'lab': e.get('lab', '')
|
|
136
|
+
'valid_until_utc': e['valid_until_utc'], 'lab': e.get('lab', ''),
|
|
137
|
+
'uncertainty': e.get('uncertainty'), 'uncertainty_k': e.get('uncertainty_k'), 'uncertainty_probability': e.get('uncertainty_probability'),
|
|
138
|
+
'traceability': e.get('traceability', ''),
|
|
139
|
+
'iso17025': {'status': cf['status'], 'carried': cf['carried'], 'n_items': cf['n_items'], 'missing': cf['missing']}})
|
|
116
140
|
return rows
|
|
117
141
|
|
|
142
|
+
def conformance(self) -> dict:
|
|
143
|
+
"""Every filed certificate against ISO/IEC 17025:2017 7.8, item by item."""
|
|
144
|
+
from .conformance import register_conformance
|
|
145
|
+
return register_conformance(self.list())
|
|
146
|
+
|
|
118
147
|
@staticmethod
|
|
119
148
|
def summary(rows: List[dict]) -> dict:
|
|
120
149
|
c = {s: 0 for s in STATUSES}
|
|
@@ -349,15 +349,28 @@ def gauge_drift_report(evaluation: dict, stream, catalogue: Optional[TagCatalogu
|
|
|
349
349
|
if inst.get('swaps') or inst.get('certificates') or inst.get('swap_notes'):
|
|
350
350
|
by_station = {st['channel']: st for st in stations}
|
|
351
351
|
cert_rows = []
|
|
352
|
+
statements = []
|
|
353
|
+
from .conformance import decide as _decide, expanded_uncertainty as _eu
|
|
352
354
|
for c in inst.get('certificates', []):
|
|
353
355
|
st = by_station.get(c['tag_id'])
|
|
354
356
|
band = None
|
|
355
357
|
if c.get('accuracy_pct_fs') is not None and c.get('full_scale'):
|
|
356
358
|
band = float(c['accuracy_pct_fs']) / 100.0 * float(c['full_scale'])
|
|
357
|
-
|
|
359
|
+
# the statement of conformity, with its decision rule named (ISO/IEC 17025:2017 7.8.6; ILAC-G8:09/2019):
|
|
360
|
+
# the bias over the window against the certificate's accuracy class, the bias's expanded uncertainty as the guard band
|
|
361
|
+
outcome = '-'
|
|
362
|
+
if st and band is not None and st.get('bias_psi') is not None:
|
|
363
|
+
eu = _eu(st.get('noise_sigma_psi'), st.get('n'))
|
|
364
|
+
dec = _decide(float(st['bias_psi']), band, eu['U'] if eu else None, k=(eu['k'] if eu else None), probability=(eu['probability'] if eu else None),
|
|
365
|
+
unit='psi', what=f"bias of {c['tag_id']} over the evaluation window ({st.get('n') or '?'} samples)",
|
|
366
|
+
spec=f"certificate {c.get('certificate_id') or '-'}: ±{_fmt(c['accuracy_pct_fs'])} % FS = ±{_fmt(band)} psi")
|
|
367
|
+
outcome = dec['outcome']
|
|
368
|
+
statements.append(dec['statement'] + '.')
|
|
369
|
+
iso = c.get('iso17025') or {}
|
|
358
370
|
cert_rows.append([c['tag_id'], c.get('serial') or '-', c.get('certificate_id') or '-', c['status'],
|
|
359
371
|
c.get('valid_until_utc') or '-', (f"±{_fmt(c['accuracy_pct_fs'])} % FS" + (f" = ±{_fmt(band)}" if band is not None else '')) if c.get('accuracy_pct_fs') is not None else '-',
|
|
360
|
-
_fmt(st.get('bias_psi')) if st else '-',
|
|
372
|
+
_fmt(st.get('bias_psi')) if st else '-', outcome,
|
|
373
|
+
(f"{iso.get('status')} ({iso.get('carried')}/{iso.get('n_items')})" if iso else '-')])
|
|
361
374
|
swap_rows = [[e['swap_utc'], e['tag_id'], e.get('old_serial') or '-', e['new_serial'], e.get('certificate_id') or '-', e.get('recorded_by'), e.get('note', '')]
|
|
362
375
|
for e in inst.get('swaps', [])]
|
|
363
376
|
note_rows = [[n['tag_id'], n.get('swap_utc') or '-', _fmt(n.get('samples_excluded')), _fmt(n.get('samples_kept')), 'applied' if n.get('applied') else n.get('reason', '')]
|
|
@@ -368,10 +381,15 @@ def gauge_drift_report(evaluation: dict, stream, catalogue: Optional[TagCatalogu
|
|
|
368
381
|
if note_rows:
|
|
369
382
|
tables.append(Table(['Tag', 'Fit starts at', 'Samples before (excluded)', 'Samples used', 'Applied'], note_rows, caption='Effect on this evaluation'))
|
|
370
383
|
if cert_rows:
|
|
371
|
-
tables.append(Table(['Tag', 'Serial', 'Certificate', 'Status', 'Valid until', 'Stated accuracy', 'Measured bias (psi)', '
|
|
384
|
+
tables.append(Table(['Tag', 'Serial', 'Certificate', 'Status', 'Valid until', 'Stated accuracy', 'Measured bias (psi)', 'Conformity', 'ISO/IEC 17025 7.8'], cert_rows, caption='Calibration certificates'))
|
|
372
385
|
cand = inst.get('candidates', [])
|
|
373
386
|
par = ['A swapped gauge is a new instrument: the evaluation above fits only the samples after each tag\'s latest recorded swap. '
|
|
374
|
-
'A certificate\'s stated accuracy is printed beside the measured bias
|
|
387
|
+
'A certificate\'s stated accuracy is printed beside the measured bias, and the conformity column is a statement of conformity in the '
|
|
388
|
+
'words of ISO/IEC 17025:2017 7.8.6: the result it applies to, the specification, and the decision rule, which is ILAC-G8:09/2019 '
|
|
389
|
+
'non-binary with a guard band equal to the bias\'s expanded uncertainty (k = 2, from the noise and the sample count) - PASS, '
|
|
390
|
+
'CONDITIONAL PASS, CONDITIONAL FAIL or FAIL. Without an uncertainty the rule falls back to simple acceptance and the statement says so. '
|
|
391
|
+
'The last column is the certificate itself against the items 7.8.2.1 and 7.8.4.1 ask it to carry, as the operator recorded them.']
|
|
392
|
+
par += statements
|
|
375
393
|
if cand:
|
|
376
394
|
par.append(f"{len(cand)} unconfirmed swap candidate(s) proposed by the step detector (not applied): "
|
|
377
395
|
+ '; '.join(f"{c['tag_id']} at {c.get('swap_utc') or c['elapsed_s']} ({c['step']:+g} {c['unit']}, {c['confidence']})" for c in cand[:6]) + '.')
|