gea-program 0.30.0__tar.gz → 0.31.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.
Files changed (280) hide show
  1. {gea_program-0.30.0/gea_program.egg-info → gea_program-0.31.0}/PKG-INFO +30 -6
  2. {gea_program-0.30.0 → gea_program-0.31.0}/README.md +29 -5
  3. {gea_program-0.30.0 → gea_program-0.31.0}/gea/__init__.py +1 -1
  4. {gea_program-0.30.0 → gea_program-0.31.0}/gea/__main__.py +76 -0
  5. {gea_program-0.30.0 → gea_program-0.31.0}/gea/acceptance_tests.py +188 -3
  6. {gea_program-0.30.0 → gea_program-0.31.0}/gea/edr.py +370 -82
  7. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/edr.md +4 -2
  8. gea_program-0.31.0/gea/help/tools.md +11 -0
  9. {gea_program-0.30.0 → gea_program-0.31.0}/gea/helplib.py +1 -1
  10. gea_program-0.31.0/gea/reference/tool_sources.json +3046 -0
  11. {gea_program-0.30.0 → gea_program-0.31.0}/gea/service.py +31 -0
  12. gea_program-0.31.0/gea/tool_taxonomy.py +905 -0
  13. {gea_program-0.30.0 → gea_program-0.31.0}/gea/web/app.html +105 -2
  14. {gea_program-0.30.0 → gea_program-0.31.0/gea_program.egg-info}/PKG-INFO +30 -6
  15. {gea_program-0.30.0 → gea_program-0.31.0}/gea_program.egg-info/SOURCES.txt +2 -0
  16. {gea_program-0.30.0 → gea_program-0.31.0}/pyproject.toml +1 -1
  17. gea_program-0.30.0/gea/help/tools.md +0 -11
  18. {gea_program-0.30.0 → gea_program-0.31.0}/LICENSE +0 -0
  19. {gea_program-0.30.0 → gea_program-0.31.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  20. {gea_program-0.30.0 → gea_program-0.31.0}/gea/accuracy_statement.py +0 -0
  21. {gea_program-0.30.0 → gea_program-0.31.0}/gea/alarm_engine.py +0 -0
  22. {gea_program-0.30.0 → gea_program-0.31.0}/gea/backup.py +0 -0
  23. {gea_program-0.30.0 → gea_program-0.31.0}/gea/bench.py +0 -0
  24. {gea_program-0.30.0 → gea_program-0.31.0}/gea/blind_harness.py +0 -0
  25. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  26. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  27. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  28. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  29. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  30. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  31. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  32. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  33. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  34. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  35. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  36. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  37. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  38. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  39. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  40. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  41. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  42. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  43. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  44. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  45. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  46. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  47. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  48. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  49. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  50. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  51. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  52. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  53. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  54. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  55. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  56. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  57. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  58. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  59. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  60. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  61. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  62. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  63. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  64. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  65. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  66. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  67. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  68. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  69. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  70. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  71. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  72. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  73. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  74. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  75. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  76. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  77. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  78. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  79. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  80. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  81. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  82. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  83. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  84. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  85. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  86. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  87. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  88. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  89. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  90. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  91. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  92. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/med_160_sapropels.txt +0 -0
  93. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  94. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  95. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  96. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  97. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  98. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  99. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  100. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  101. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  102. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  103. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  104. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  105. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  106. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  107. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  108. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  109. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  110. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  111. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  112. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  113. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  114. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  115. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  116. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  117. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  118. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  119. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  120. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  121. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  122. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  123. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  124. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  125. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  126. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  127. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  128. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  129. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog_map.py +0 -0
  130. {gea_program-0.30.0 → gea_program-0.31.0}/gea/catalog_register.py +0 -0
  131. {gea_program-0.30.0 → gea_program-0.31.0}/gea/certificates.py +0 -0
  132. {gea_program-0.30.0 → gea_program-0.31.0}/gea/cli.py +0 -0
  133. {gea_program-0.30.0 → gea_program-0.31.0}/gea/client_reports.py +0 -0
  134. {gea_program-0.30.0 → gea_program-0.31.0}/gea/config_versioning.py +0 -0
  135. {gea_program-0.30.0 → gea_program-0.31.0}/gea/conformance.py +0 -0
  136. {gea_program-0.30.0 → gea_program-0.31.0}/gea/correlation.py +0 -0
  137. {gea_program-0.30.0 → gea_program-0.31.0}/gea/dashboard.py +0 -0
  138. {gea_program-0.30.0 → gea_program-0.31.0}/gea/deviation.py +0 -0
  139. {gea_program-0.30.0 → gea_program-0.31.0}/gea/dlis.py +0 -0
  140. {gea_program-0.30.0 → gea_program-0.31.0}/gea/dnp3.py +0 -0
  141. {gea_program-0.30.0 → gea_program-0.31.0}/gea/doctor.py +0 -0
  142. {gea_program-0.30.0 → gea_program-0.31.0}/gea/downhole_engine.py +0 -0
  143. {gea_program-0.30.0 → gea_program-0.31.0}/gea/drift_monitor.py +0 -0
  144. {gea_program-0.30.0 → gea_program-0.31.0}/gea/earth_model.py +0 -0
  145. {gea_program-0.30.0 → gea_program-0.31.0}/gea/elevation.py +0 -0
  146. {gea_program-0.30.0 → gea_program-0.31.0}/gea/etp.py +0 -0
  147. {gea_program-0.30.0 → gea_program-0.31.0}/gea/etp12_schemas.json +0 -0
  148. {gea_program-0.30.0 → gea_program-0.31.0}/gea/example_register_map.json +0 -0
  149. {gea_program-0.30.0 → gea_program-0.31.0}/gea/fat_sat.py +0 -0
  150. {gea_program-0.30.0 → gea_program-0.31.0}/gea/files.py +0 -0
  151. {gea_program-0.30.0 → gea_program-0.31.0}/gea/fixtures/README.md +0 -0
  152. {gea_program-0.30.0 → gea_program-0.31.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  153. {gea_program-0.30.0 → gea_program-0.31.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  154. {gea_program-0.30.0 → gea_program-0.31.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  155. {gea_program-0.30.0 → gea_program-0.31.0}/gea/follower.py +0 -0
  156. {gea_program-0.30.0 → gea_program-0.31.0}/gea/forward_model.py +0 -0
  157. {gea_program-0.30.0 → gea_program-0.31.0}/gea/friction_priors.py +0 -0
  158. {gea_program-0.30.0 → gea_program-0.31.0}/gea/gamma.py +0 -0
  159. {gea_program-0.30.0 → gea_program-0.31.0}/gea/gauge_aging.py +0 -0
  160. {gea_program-0.30.0 → gea_program-0.31.0}/gea/gauge_specs.py +0 -0
  161. {gea_program-0.30.0 → gea_program-0.31.0}/gea/geodesy.py +0 -0
  162. {gea_program-0.30.0 → gea_program-0.31.0}/gea/gravity_reference.py +0 -0
  163. {gea_program-0.30.0 → gea_program-0.31.0}/gea/hartip.py +0 -0
  164. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/TESTER_GUIDE.md +0 -0
  165. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/alarms.md +0 -0
  166. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/audit-update.md +0 -0
  167. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/backup.md +0 -0
  168. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/catalog.md +0 -0
  169. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/conformance.md +0 -0
  170. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/data-in.md +0 -0
  171. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/dlis.md +0 -0
  172. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/doctor.md +0 -0
  173. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/drift.md +0 -0
  174. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/etp.md +0 -0
  175. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/field-ports.md +0 -0
  176. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/files.md +0 -0
  177. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/historian.md +0 -0
  178. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/instruments.md +0 -0
  179. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/interval.md +0 -0
  180. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/month-end.md +0 -0
  181. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/notifications.md +0 -0
  182. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/osdu.md +0 -0
  183. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/patches.md +0 -0
  184. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/ppdm.md +0 -0
  185. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/quakeml.md +0 -0
  186. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/quality.md +0 -0
  187. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/seedlink.md +0 -0
  188. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/seismic.md +0 -0
  189. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/site.md +0 -0
  190. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/sites.md +0 -0
  191. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/slip.md +0 -0
  192. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/soak.md +0 -0
  193. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/sra.md +0 -0
  194. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/start.md +0 -0
  195. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/structure.md +0 -0
  196. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/transients.md +0 -0
  197. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/upkeep.md +0 -0
  198. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/vibration.md +0 -0
  199. {gea_program-0.30.0 → gea_program-0.31.0}/gea/help/well-tests.md +0 -0
  200. {gea_program-0.30.0 → gea_program-0.31.0}/gea/housekeeping.py +0 -0
  201. {gea_program-0.30.0 → gea_program-0.31.0}/gea/interval.py +0 -0
  202. {gea_program-0.30.0 → gea_program-0.31.0}/gea/inverse_engine.py +0 -0
  203. {gea_program-0.30.0 → gea_program-0.31.0}/gea/jobs.py +0 -0
  204. {gea_program-0.30.0 → gea_program-0.31.0}/gea/live_ports.py +0 -0
  205. {gea_program-0.30.0 → gea_program-0.31.0}/gea/loadtest.py +0 -0
  206. {gea_program-0.30.0 → gea_program-0.31.0}/gea/matplotlib_demo.py +0 -0
  207. {gea_program-0.30.0 → gea_program-0.31.0}/gea/modbus.py +0 -0
  208. {gea_program-0.30.0 → gea_program-0.31.0}/gea/modbus_rtu.py +0 -0
  209. {gea_program-0.30.0 → gea_program-0.31.0}/gea/model_card.py +0 -0
  210. {gea_program-0.30.0 → gea_program-0.31.0}/gea/month_end.py +0 -0
  211. {gea_program-0.30.0 → gea_program-0.31.0}/gea/mqtt_port.py +0 -0
  212. {gea_program-0.30.0 → gea_program-0.31.0}/gea/notify.py +0 -0
  213. {gea_program-0.30.0 → gea_program-0.31.0}/gea/opcua_port.py +0 -0
  214. {gea_program-0.30.0 → gea_program-0.31.0}/gea/operator_app.py +0 -0
  215. {gea_program-0.30.0 → gea_program-0.31.0}/gea/osdu.py +0 -0
  216. {gea_program-0.30.0 → gea_program-0.31.0}/gea/patches.py +0 -0
  217. {gea_program-0.30.0 → gea_program-0.31.0}/gea/permits.py +0 -0
  218. {gea_program-0.30.0 → gea_program-0.31.0}/gea/piwebapi.py +0 -0
  219. {gea_program-0.30.0 → gea_program-0.31.0}/gea/ports.py +0 -0
  220. {gea_program-0.30.0 → gea_program-0.31.0}/gea/ppdm.py +0 -0
  221. {gea_program-0.30.0 → gea_program-0.31.0}/gea/profile_catalog.py +0 -0
  222. {gea_program-0.30.0 → gea_program-0.31.0}/gea/project.py +0 -0
  223. {gea_program-0.30.0 → gea_program-0.31.0}/gea/qt6_downhole_app.py +0 -0
  224. {gea_program-0.30.0 → gea_program-0.31.0}/gea/quakeml.py +0 -0
  225. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reconciler.py +0 -0
  226. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  227. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  228. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  229. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  230. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  231. {gea_program-0.30.0 → gea_program-0.31.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  232. {gea_program-0.30.0 → gea_program-0.31.0}/gea/rock_inventory.py +0 -0
  233. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sample_record.py +0 -0
  234. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sample_well_profile.csv +0 -0
  235. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sbom.py +0 -0
  236. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seedlink.py +0 -0
  237. {gea_program-0.30.0 → gea_program-0.31.0}/gea/segy.py +0 -0
  238. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic.py +0 -0
  239. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_array.py +0 -0
  240. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_assoc.py +0 -0
  241. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_detect.py +0 -0
  242. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_film.py +0 -0
  243. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_harmonic.py +0 -0
  244. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_qc.py +0 -0
  245. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_response.py +0 -0
  246. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_signature.py +0 -0
  247. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_track.py +0 -0
  248. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_uncertainty.py +0 -0
  249. {gea_program-0.30.0 → gea_program-0.31.0}/gea/seismic_unlisted.py +0 -0
  250. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sensor_swap.py +0 -0
  251. {gea_program-0.30.0 → gea_program-0.31.0}/gea/service_life.py +0 -0
  252. {gea_program-0.30.0 → gea_program-0.31.0}/gea/shapefile.py +0 -0
  253. {gea_program-0.30.0 → gea_program-0.31.0}/gea/shell.py +0 -0
  254. {gea_program-0.30.0 → gea_program-0.31.0}/gea/shut_in.py +0 -0
  255. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sla_report.py +0 -0
  256. {gea_program-0.30.0 → gea_program-0.31.0}/gea/slip.py +0 -0
  257. {gea_program-0.30.0 → gea_program-0.31.0}/gea/soak.py +0 -0
  258. {gea_program-0.30.0 → gea_program-0.31.0}/gea/sra.py +0 -0
  259. {gea_program-0.30.0 → gea_program-0.31.0}/gea/store_forward.py +0 -0
  260. {gea_program-0.30.0 → gea_program-0.31.0}/gea/strata_join.py +0 -0
  261. {gea_program-0.30.0 → gea_program-0.31.0}/gea/structure.py +0 -0
  262. {gea_program-0.30.0 → gea_program-0.31.0}/gea/supervisor.py +0 -0
  263. {gea_program-0.30.0 → gea_program-0.31.0}/gea/survey_cmd.py +0 -0
  264. {gea_program-0.30.0 → gea_program-0.31.0}/gea/survey_view.py +0 -0
  265. {gea_program-0.30.0 → gea_program-0.31.0}/gea/telemetry.py +0 -0
  266. {gea_program-0.30.0 → gea_program-0.31.0}/gea/tool_catalog.py +0 -0
  267. {gea_program-0.30.0 → gea_program-0.31.0}/gea/tool_library.py +0 -0
  268. {gea_program-0.30.0 → gea_program-0.31.0}/gea/transient.py +0 -0
  269. {gea_program-0.30.0 → gea_program-0.31.0}/gea/vibration.py +0 -0
  270. {gea_program-0.30.0 → gea_program-0.31.0}/gea/well_assembler.py +0 -0
  271. {gea_program-0.30.0 → gea_program-0.31.0}/gea/well_test_validation.py +0 -0
  272. {gea_program-0.30.0 → gea_program-0.31.0}/gea/wits0.py +0 -0
  273. {gea_program-0.30.0 → gea_program-0.31.0}/gea/witsml.py +0 -0
  274. {gea_program-0.30.0 → gea_program-0.31.0}/gea/workspace.py +0 -0
  275. {gea_program-0.30.0 → gea_program-0.31.0}/gea_program.egg-info/dependency_links.txt +0 -0
  276. {gea_program-0.30.0 → gea_program-0.31.0}/gea_program.egg-info/entry_points.txt +0 -0
  277. {gea_program-0.30.0 → gea_program-0.31.0}/gea_program.egg-info/requires.txt +0 -0
  278. {gea_program-0.30.0 → gea_program-0.31.0}/gea_program.egg-info/top_level.txt +0 -0
  279. {gea_program-0.30.0 → gea_program-0.31.0}/setup.cfg +0 -0
  280. {gea_program-0.30.0 → gea_program-0.31.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.30.0
3
+ Version: 0.31.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 (376 checks)
128
+ gea accept # the product gate (380 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]")
@@ -650,8 +650,26 @@ itself; `gea help seedlink`. A TexNet station is served by EarthScope at
650
650
 
651
651
  ### Tools and ports
652
652
 
653
- The Tools & ports tab (`#/tools`, `gea tools`) is the program's two
654
- registers in one place: the tool library - every downhole and surface tool
653
+ The Tools & ports tab (`#/tools`, `gea tools`) opens on the **downhole tool
654
+ library**: every class of downhole tool in use across the industry - bits
655
+ and hole openers, motors and rotary steerable systems, MWD and LWD, the drill
656
+ string and its subs, casing and cementing equipment, completion and
657
+ artificial lift, wireline and cased-hole tools, fishing and milling, test
658
+ tools - ninety-six classes in nine categories. Point at a tool, or Tab to
659
+ it, for a one-line description; click, tap or press Enter for its tutorial:
660
+ what it is, where it runs, how it works, what it shows in the data, what to
661
+ watch, and what this program does with it, with the published figures
662
+ quoted from the pages they cite and the pages themselves. The tutorials
663
+ carry no numbers of their own; a figure on a tool's card is the cited page's
664
+ words, most often one maker's product, never a limit for the class. Your
665
+ **fleet** - the tools you actually run - goes in beside the classes, each
666
+ with its maker, model, serial and the datasheet that rates it
667
+ (`gea tools --action fleet-add --workspace C:\site --class pdm_motor
668
+ --maker ... --model ... --datasheet ...`, or from the tutorial), kept in the
669
+ workspace and audited. `gea tools --action classes`, `--action tutorial
670
+ --class drilling_jar`.
671
+
672
+ Below it are the program's two registers: the gauge library - every gauge
655
673
  with its source citation, ratings, what it measures, the telemetry interface
656
674
  it declares and its datasheet aging rate where the vendor published one -
657
675
  and the ports, the file formats read and the eleven live protocols spoken,
@@ -730,8 +748,14 @@ on bottom and rotating, the rate of penetration on bottom, the connections,
730
748
  the peaks; for the well, total depth and when the bit was first on bottom
731
749
  there, the recorder's TVD at it; per channel what was missing and how
732
750
  often; and the **MD-TVD table**, hole depth against the recorder's
733
- interpolated TVD, labelled an interpolation and not a survey. A recorder
734
- that resets its hole depth at a restart is named, not corrected. `gea edr
751
+ interpolated TVD, labelled an interpolation and not a survey. Before any
752
+ footage is counted the hole depth is read for what it can be trusted for:
753
+ a depth carried over from the last well, a depth set back and counted up
754
+ again by running pipe, a sidetrack, an overstated or understated depth
755
+ corrected - each named as a depth event in the record and on the well
756
+ page, and handled so that total depth is the final borehole's and the
757
+ footage adds up. The recorder's gamma is written as a LAS log by bit
758
+ depth. `gea edr
735
759
  --file C:\site\_incoming\30636033.csv`; `gea workspace --action add-file`
736
760
  then `refresh` for the record beside the well's other reports. `gea help
737
761
  edr`.
@@ -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 (376 checks)
89
+ gea accept # the product gate (380 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]")
@@ -611,8 +611,26 @@ itself; `gea help seedlink`. A TexNet station is served by EarthScope at
611
611
 
612
612
  ### Tools and ports
613
613
 
614
- The Tools & ports tab (`#/tools`, `gea tools`) is the program's two
615
- registers in one place: the tool library - every downhole and surface tool
614
+ The Tools & ports tab (`#/tools`, `gea tools`) opens on the **downhole tool
615
+ library**: every class of downhole tool in use across the industry - bits
616
+ and hole openers, motors and rotary steerable systems, MWD and LWD, the drill
617
+ string and its subs, casing and cementing equipment, completion and
618
+ artificial lift, wireline and cased-hole tools, fishing and milling, test
619
+ tools - ninety-six classes in nine categories. Point at a tool, or Tab to
620
+ it, for a one-line description; click, tap or press Enter for its tutorial:
621
+ what it is, where it runs, how it works, what it shows in the data, what to
622
+ watch, and what this program does with it, with the published figures
623
+ quoted from the pages they cite and the pages themselves. The tutorials
624
+ carry no numbers of their own; a figure on a tool's card is the cited page's
625
+ words, most often one maker's product, never a limit for the class. Your
626
+ **fleet** - the tools you actually run - goes in beside the classes, each
627
+ with its maker, model, serial and the datasheet that rates it
628
+ (`gea tools --action fleet-add --workspace C:\site --class pdm_motor
629
+ --maker ... --model ... --datasheet ...`, or from the tutorial), kept in the
630
+ workspace and audited. `gea tools --action classes`, `--action tutorial
631
+ --class drilling_jar`.
632
+
633
+ Below it are the program's two registers: the gauge library - every gauge
616
634
  with its source citation, ratings, what it measures, the telemetry interface
617
635
  it declares and its datasheet aging rate where the vendor published one -
618
636
  and the ports, the file formats read and the eleven live protocols spoken,
@@ -691,8 +709,14 @@ on bottom and rotating, the rate of penetration on bottom, the connections,
691
709
  the peaks; for the well, total depth and when the bit was first on bottom
692
710
  there, the recorder's TVD at it; per channel what was missing and how
693
711
  often; and the **MD-TVD table**, hole depth against the recorder's
694
- interpolated TVD, labelled an interpolation and not a survey. A recorder
695
- that resets its hole depth at a restart is named, not corrected. `gea edr
712
+ interpolated TVD, labelled an interpolation and not a survey. Before any
713
+ footage is counted the hole depth is read for what it can be trusted for:
714
+ a depth carried over from the last well, a depth set back and counted up
715
+ again by running pipe, a sidetrack, an overstated or understated depth
716
+ corrected - each named as a depth event in the record and on the well
717
+ page, and handled so that total depth is the final borehole's and the
718
+ footage adds up. The recorder's gamma is written as a LAS log by bit
719
+ depth. `gea edr
696
720
  --file C:\site\_incoming\30636033.csv`; `gea workspace --action add-file`
697
721
  then `refresh` for the record beside the well's other reports. `gea help
698
722
  edr`.
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.30.0"
126
+ __version__ = "0.31.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -285,6 +285,17 @@ def main(argv=None) -> int:
285
285
  p_tl.add_argument("--station", type=str, action="append", default=None, help="a station as DEPTH_FT:TOOL, e.g. 9850:quartz_pt_geoq177_16k")
286
286
  p_tl.add_argument("--surface-temp-f", type=float, default=75.0); p_tl.add_argument("--temp-gradient-f-ft", type=float, default=0.018)
287
287
  p_tl.add_argument("--surface-pressure-psi", type=float, default=14.7); p_tl.add_argument("--pressure-gradient-psi-ft", type=float, default=0.465)
288
+ p_tl.add_argument("--action", choices=["classes", "tutorial", "fleet", "fleet-add", "fleet-remove"], default=None,
289
+ help="the downhole tool classes in use across the industry: classes (--category, --find), tutorial --class ID, and the workspace's own fleet (fleet, fleet-add, fleet-remove)")
290
+ p_tl.add_argument("--category", type=str, default=None, help="classes: one category (bits, drive, measure, bha, casing, completion, logging, fishing, testing)")
291
+ p_tl.add_argument("--find", type=str, default="", help="classes: words to look for in the name and description")
292
+ p_tl.add_argument("--class", dest="class_id", type=str, default=None, help="tutorial / fleet-add: the tool class id, e.g. pdm_motor")
293
+ p_tl.add_argument("--workspace", type=str, default=None, help="fleet actions: the workspace folder")
294
+ p_tl.add_argument("--maker", type=str, default=""); p_tl.add_argument("--model", type=str, default="")
295
+ p_tl.add_argument("--datasheet", type=str, default="", help="fleet-add: the datasheet that rates the tool - title and revision, or its URL (required)")
296
+ p_tl.add_argument("--serial", type=str, default=""); p_tl.add_argument("--od-in", type=float, default=None); p_tl.add_argument("--note", type=str, default="")
297
+ p_tl.add_argument("--id", type=str, default=None, help="fleet-remove: the fleet tool id (gea tools --action fleet lists them)")
298
+ p_tl.add_argument("--actor", type=str, default="cli")
288
299
  p_cc = sub.add_parser("comcat", help="USGS ComCat / any FDSN event service: --lat --lon --radius-km --start --end prints the text-format query to open; --file reads the export it returns")
289
300
  p_cc.add_argument("--lat", type=float, default=None); p_cc.add_argument("--lon", type=float, default=None); p_cc.add_argument("--radius-km", type=float, default=25.0)
290
301
  p_cc.add_argument("--start", type=str, default=None); p_cc.add_argument("--end", type=str, default=None); p_cc.add_argument("--min-mag", type=float, default=1.0)
@@ -2374,6 +2385,71 @@ def main(argv=None) -> int:
2374
2385
  elif a.cmd == "tools":
2375
2386
  import json as _json
2376
2387
  from . import tool_catalog as _TC
2388
+ if a.action:
2389
+ from . import tool_taxonomy as _TX
2390
+ from .cli import _safe_stdout
2391
+ _safe_stdout() # the cited figures carry fractions and degree signs
2392
+ if a.action == "classes":
2393
+ cs = _TX.classes(a.category, a.find)
2394
+ if a.category and a.category not in _TX.CATEGORY_NAMES:
2395
+ raise SystemExit(f"tools: no category {a.category!r}; one of {', '.join(_TX.CATEGORY_NAMES)}")
2396
+ if a.json:
2397
+ print(_json.dumps(cs, indent=1)); return 0
2398
+ sm = _TX.summary()
2399
+ print(f"{len(cs)} of {sm['classes']} tool classes; {sm['sources']} pages checked (fetched {sm['sources_fetched']}); {sm['figures']} published figures, each quoted from its page")
2400
+ last = None
2401
+ for t in cs:
2402
+ if t["category"] != last:
2403
+ print(f"\n{t['category_name']}"); last = t["category"]
2404
+ print(f" {t['id']:<24s} {t['name']}")
2405
+ print(f" {t['one_line']}")
2406
+ print("\n`gea tools --action tutorial --class ID` opens one.")
2407
+ return 0
2408
+ if a.action == "tutorial":
2409
+ if not a.class_id:
2410
+ raise SystemExit("tools: tutorial needs --class, e.g. --class pdm_motor (gea tools --action classes lists them)")
2411
+ try:
2412
+ if a.json:
2413
+ print(_json.dumps(_TX.tool_class(a.class_id), indent=1)); return 0
2414
+ print(_TX.tutorial_text(a.class_id))
2415
+ except KeyError as e:
2416
+ raise SystemExit(f"tools: {e.args[0]}")
2417
+ return 0
2418
+ if not a.workspace:
2419
+ raise SystemExit(f"tools: --action {a.action} needs --workspace, e.g. --workspace C:\\site")
2420
+ from .workspace import Workspace, WorkspaceError
2421
+ try:
2422
+ ws = Workspace(a.workspace)
2423
+ except WorkspaceError as e:
2424
+ raise SystemExit(f"tools: {e}")
2425
+ if a.action == "fleet":
2426
+ fl = _TX.fleet_list(ws.path)
2427
+ if a.json:
2428
+ print(_json.dumps(fl, indent=1)); return 0
2429
+ if not fl:
2430
+ print("the fleet is empty: gea tools --action fleet-add --workspace ... --class pdm_motor --maker ... --model ... --datasheet ...")
2431
+ for t in fl:
2432
+ print(f"{t['id']:<32s} {t['class_name']} - {t['maker']} {t['model']}" + (f" s/n {t['serial']}" if t['serial'] else '') + (f", {t['od_in']:g} in OD" if t['od_in'] else ''))
2433
+ print(f" datasheet: {t['datasheet']}")
2434
+ return 0
2435
+ if a.action == "fleet-add":
2436
+ try:
2437
+ rec = _TX.fleet_add(ws.path, a.class_id or "", a.maker, a.model, a.datasheet, serial=a.serial, od_in=a.od_in, note=a.note, actor=a.actor)
2438
+ except ValueError as e:
2439
+ raise SystemExit(f"tools: {e}")
2440
+ ws.audit(a.actor, 'fleet.add', {'id': rec['id'], 'class': rec['class_id'], 'datasheet': rec['datasheet']})
2441
+ print(f"added {rec['id']}: {rec['maker']} {rec['model']} ({rec['class_name']}); datasheet {rec['datasheet']}")
2442
+ return 0
2443
+ if a.action == "fleet-remove":
2444
+ if not a.id:
2445
+ raise SystemExit("tools: fleet-remove needs --id (gea tools --action fleet lists them)")
2446
+ try:
2447
+ rec = _TX.fleet_remove(ws.path, a.id)
2448
+ except ValueError as e:
2449
+ raise SystemExit(f"tools: {e}")
2450
+ ws.audit(a.actor, 'fleet.remove', {'id': rec['id'], 'class': rec['class_id']})
2451
+ print(f"removed {rec['id']}")
2452
+ return 0
2377
2453
  if a.rating or a.station:
2378
2454
  stations = []
2379
2455
  for spec in (a.station or []):
@@ -7697,10 +7697,11 @@ def section_by_edr(tmp: str) -> None:
7697
7697
  "sections": rec["n_sections"] == 3 and all(x["rows"] == 3000 and x["hours"] == 0.83 for x in secs) and secs[0]["gap_before_hours"] is None
7698
7698
  and secs[1]["gap_before_hours"] == 48.0 and secs[2]["gap_before_hours"] == 48.0 and all(x["connections"] == 25 for x in secs)
7699
7699
  and all(x["rop_on_bottom_mean_ft_h"] == 600.0 and x["on_bottom_hours"] == 0.69 and x["rotating_hours"] == 0.69 for x in secs)
7700
- and secs[0]["hole_depth_at_start_ft"] == 1500.2 and abs(secs[0]["feet_drilled"] - 416.5) < 0.2 and secs[0]["feet_by_increments"] == secs[0]["feet_drilled"]
7701
- and "hole_depth_drops" not in secs[0] and secs[0]["wob_max_klbf"] == 20.0 and secs[0]["rpm_max"] == 60.0,
7700
+ and secs[0]["hole_depth_at_start_ft"] == 1500.2 and abs(secs[0]["feet_drilled"] - 416.5) < 0.2 and "depth_events" not in secs[0]
7701
+ and secs[1]["hole_depth_at_start_ft"] == 1916.7 and secs[0]["wob_max_klbf"] == 20.0 and secs[0]["rpm_max"] == 60.0,
7702
7702
  "well": rec["total_depth_md_ft"] == 2750.0 and rec["tvd_at_td_ft"] == 2015.0 and rec["on_bottom_hours"] == 2.08 and abs(rec["feet_drilled"] - 1249.8) < 0.2
7703
- and rec["feet_by_increments"] == rec["feet_drilled"] and not rec["notes"]
7703
+ and rec["depth_events"] == [] and rec["excluded_hours"] == 0.0 and rec["flag_idle_hours"] == 0.0 and not rec["notes"]
7704
+ and rec["first_hole_depth_ft"] == 1500.2 and [b["status"] for b in rec["boreholes"]] == ["FINAL"]
7704
7705
  and abs(rec["feet_per_on_bottom_hour"] - 600.9) < 0.2 and rec["hours_recorded"] == 2.5 and rec["span_days"] == 4.1 and rec["reached_td"].startswith("2026-06-05T02:29"),
7705
7706
  "md_tvd": rec["md_tvd"]["status"] == "OK" and rec["md_tvd"]["n"] == 13 and rec["md_tvd"]["tvd_decreasing_steps"] == 0 and rec["md_tvd"]["samples_without_tvd"] == 900
7706
7707
  and "not a survey" in rec["md_tvd"]["source"] and rec["lateral"]["lateral_start_md_ft"] == 1999.8 and abs(rec["lateral"]["lateral_length_ft"] - 750.2) < 0.2
@@ -7716,6 +7717,27 @@ def section_by_edr(tmp: str) -> None:
7716
7717
  "registry": PORT_REGISTRY["edr"].status == "IMPLEMENTED" and read_any(r["path"]).source_format == "edr"
7717
7718
  and any(p["name"] == "edr" and p["status"] == "IMPLEMENTED" for p in TC.catalog()["ports"]["files"]),
7718
7719
  }
7720
+ # the depth events: a stale opening, a re-reference, a sidetrack and a correction, each named and handled
7721
+ ev = E.selftest_events(str(d)); er = ev["record"]
7722
+ types = [(e["type"], e["from_ft"], e["to_ft"]) for e in er["depth_events"]]
7723
+ bh = er["boreholes"]
7724
+ c4 = {}
7725
+ c4["events"] = (types == [("STALE OPENING", 9999.0, 5200.2), ("RE-REFERENCE", 5400.0, 2000.0), ("SIDETRACK", 5500.0, 4800.0), ("CORRECTION", 5700.0, 5650.2)]
7726
+ and er["total_depth_md_ft"] == 5750.0 and abs(er["feet_drilled"] - 1449.8) < 0.2 and er["first_hole_depth_ft"] == 5000.2
7727
+ and len(bh) == 2 and bh[0]["status"].startswith("ABANDONED") and bh[0]["deepest_ft"] == 5500.0 and bh[1]["status"] == "FINAL" and bh[1]["from_ft"] == 4800.0
7728
+ and er["on_bottom_hours"] == 8.45 and er["on_bottom_flag_hours"] == 9.39 and er["excluded_hours"] == 1.03
7729
+ and any("largest value is 9,999.0 ft" in x for x in er["notes"])
7730
+ and er["gamma"]["status"] == "OK" and er["gamma"]["bins"] == 951 and er["gamma"]["depth_from_ft"] == 4800.0 and er["gamma"]["depth_to_ft"] == 5750.0
7731
+ and len(ev["gamma"].index) == 951 and bool(np.all(ev["gamma"].channels["GR"].values == 110.0))
7732
+ and er["md_tvd"]["n"] == 10 and any(l.startswith("Depth event, SIDETRACK") and "abandoned" in l for l in er["lines"])
7733
+ and any(l.startswith("Boreholes:") for l in er["lines"]))
7734
+ fl = E.selftest_flag(str(d))["record"]
7735
+ c4["flag_and_corrections"] = ([(e["type"], e["from_ft"], e["to_ft"]) for e in fl["depth_events"]] == [("CORRECTION", 1060.0, 1080.0), ("CORRECTION", 1140.0, 1128.0), ("CORRECTION", 1128.0, 1140.0),
7736
+ ("STALE OPENING", 9999.0, None), ("STALE OPENING", 9999.0, None), ("STALE OPENING", 9999.0, 1200.1)]
7737
+ and fl["total_depth_md_ft"] == 1230.0 and fl["first_hole_depth_ft"] == 1000.1 and abs(fl["feet_drilled"] - 229.9) < 0.05
7738
+ and fl["on_bottom_hours"] == 0.58 and fl["flag_idle_hours"] == 0.56 and fl["excluded_hours"] == 0.18 and fl["on_bottom_flag_hours"] == 1.14
7739
+ and [x.get("hole_depth_at_start_ft") for x in fl["sections"]] == [1000.1, 1128.0, None, None, 1200.0] and len(fl["boreholes"]) == 1
7740
+ and any("nothing drilled" in l for l in fl["lines"]))
7719
7741
  odd = str(d / "odd.csv")
7720
7742
  with open(r["path"], encoding="utf-8") as f, open(odd, "w", encoding="utf-8") as g:
7721
7743
  for i, line in enumerate(f):
@@ -7820,6 +7842,168 @@ def section_by_edr(tmp: str) -> None:
7820
7842
  "written gamma LAS converts its feet once, says so, recognizes the gamma and says what it lacks; the help page and its index entry"
7821
7843
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
7822
7844
 
7845
+ c4["help"] = all(t in help_md for t in ("STALE OPENING", "RE-REFERENCE", "SIDETRACK", "CORRECTION", "UNRESOLVED")) and "daily drilling report" in help_md \
7846
+ and "nothing drilled" in help_md and "total depth less the first depth" in help_md
7847
+ failed4 = [k for k, v in c4.items() if not v]
7848
+ ok(not failed4,
7849
+ "BY4 the depth events: a section opening on another well's depth (STALE OPENING, excluded), a depth set back and regained by counting pipe at 3,600 "
7850
+ "ft/h (RE-REFERENCE, excluded), a 700 ft set-back time-drilled at 5 ft/h (SIDETRACK: total depth 5,750 ft from the final borehole, 1,449.8 ft drilled "
7851
+ "counting the abandoned 4,800-5,500 ft, the gamma log and TVD table kept off the abandoned hole) and a 50 ft overstated depth (CORRECTION) each named; "
7852
+ "a 20 ft set-forward off bottom added, a 12 ft fall regained only in the next section a correction and never a re-reference, the next section drilling "
7853
+ "on from its opening depth not a stale opening, three restarts on another well's depth each a stale opening (judged against the last depth the file can "
7854
+ "be trusted for), and 2,000 s of on-bottom flag with nothing drilled kept out of the drilling hours - footage exactly total depth less the first depth "
7855
+ "(229.9 ft); the help page names every event"
7856
+ + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
7857
+
7858
+
7859
+
7860
+ def section_bz_tool_library(tmp: str) -> None:
7861
+ """Section BZ - the downhole tool library: every class of downhole tool in use across the industry, a one-line
7862
+ description on hover and a tutorial that opens on a click, the published figures quoted from the pages they cite and
7863
+ nothing more, and the operator's own fleet tied to the classes and to the datasheets that rate them."""
7864
+ import http.cookiejar
7865
+ import re as _re
7866
+ import subprocess as _sp
7867
+ import urllib.request
7868
+ import urllib.error
7869
+ from urllib.parse import urlparse
7870
+ from . import tool_taxonomy as TX
7871
+ from . import helplib as H
7872
+ from .client_reports import FORBIDDEN_TERMS
7873
+ from .tool_library import TOOL_LIBRARY
7874
+ from .workspace import Workspace
7875
+ from .service import Service, Users
7876
+ pkg = Path(__file__).parent
7877
+ d = Path(tmp, "bz"); d.mkdir()
7878
+
7879
+ def _raises(fn, exc, words=""):
7880
+ try:
7881
+ fn()
7882
+ except exc as e:
7883
+ return words in str(e)
7884
+ return False
7885
+
7886
+ sm = TX.summary()
7887
+ cls = list(TX.TAXONOMY.values())
7888
+ prose = lambda t: " ".join([t["one_line"]] + [t["tutorial"][p] for p in TX.PARTS])
7889
+ lowtier = _re.compile(r"(wikipedia\.org|rigzone\.com|drillingmanual\.com|petroleumacronyms|turbodrill\.com)$")
7890
+ figs = [f for t in cls for f in t["figures"]]
7891
+ c1 = {
7892
+ "breadth": sm["classes"] >= 90 and len(sm["categories"]) == 9 and all(c["classes"] >= 1 for c in sm["categories"])
7893
+ and {"pdm_motor", "rss_push", "mwd_mud_pulse", "lwd_gamma", "drilling_jar", "production_packer", "esp", "perforating_gun",
7894
+ "pdg", "wl_formation_tester", "cbl", "overshot", "dst"} <= set(TX.TAXONOMY),
7895
+ "hover": all(0 < len(t["one_line"]) <= 140 for t in cls),
7896
+ "tutorial": all(all(len(t["tutorial"][p]) > 20 for p in TX.PARTS) for t in cls) and TX.PARTS == ("what", "where", "how", "reads", "watch", "program"),
7897
+ "no_numbers_in_prose": not any(_re.search(r"\d", prose(t)) for t in cls),
7898
+ "words": not any(w in (t["name"] + " " + prose(t)).lower() for t in cls for w in FORBIDDEN_TERMS),
7899
+ "sources": all(t["sources"] for t in cls) and all(urlparse(x["url"]).scheme == "https" and x.get("publisher") for t in cls for x in t["sources"])
7900
+ and not any(lowtier.search(urlparse(x["url"]).netloc.lower()) for t in cls for x in t["sources"])
7901
+ and (pkg / "reference" / "tool_sources.json").is_file() and sm["sources"] >= 150 and sm["sources_fetched"] == "2026-10-14",
7902
+ "figures": len(figs) == sm["figures"] and len(figs) >= 150 and all(f.get("quote") and f.get("url") and f.get("publisher") and f.get("what") for f in figs)
7903
+ and not any("..." in f["quote"] for f in figs)
7904
+ and all(("not a limit for the class" in t["figures_note"]) if t["figures"] else ("supply your model's datasheet" in t["figures_note"]) for t in cls),
7905
+ "lookup": [t["id"] for t in TX.classes("drive")][:1] == ["pdm_motor"] and {t["id"] for t in TX.classes(None, "packer")} >= {"production_packer"}
7906
+ and TX.classes(None, "no such tool zzz") == [],
7907
+ "unknown": _raises(lambda: TX.tool_class("no_such_tool"), KeyError),
7908
+ "text": all(TX.PART_TITLES[p] + ": " in TX.tutorial_text("pdm_motor") for p in TX.PARTS) and "Sources (fetched 2026-10-14)" in TX.tutorial_text("pdm_motor")
7909
+ and "Published figures (" in TX.tutorial_text("agitator") and "8-12 Hz" in TX.tutorial_text("agitator"),
7910
+ "gauges": all(TX.GAUGE_CLASS_TO_TAXONOMY.get(t.tool_class) in TX.TAXONOMY for t in TOOL_LIBRARY.values()),
7911
+ }
7912
+ failed1 = [k for k, v in c1.items() if not v]
7913
+ ok(not failed1,
7914
+ "BZ1 the downhole tool library: at least ninety classes in nine categories from bits to test tools, each with a one-line description for the hover "
7915
+ "and a six-part tutorial for the pop-open; the prose carries no number at all and none of the words a client report may not; every class checked "
7916
+ "against at least one public page (none of them an encyclopaedia or a re-publisher); every published figure is the cited page's own words with its "
7917
+ "unit and page, never a limit for the class, and a class with none says to supply the datasheet; the classes searchable; an unknown class named; "
7918
+ "every gauge in the gauge library opens its class's tutorial"
7919
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
7920
+
7921
+ wsp = str(d / "ws"); Workspace.create(wsp, "Pad 3", actor="tester")
7922
+ e_ds = _raises(lambda: TX.fleet_add(wsp, "pdm_motor", "Acme", "M1", ""), ValueError, "datasheet")
7923
+ e_cls = _raises(lambda: TX.fleet_add(wsp, "warp_drive", "Acme", "M1", "sheet"), ValueError, "no tool class")
7924
+ r1 = TX.fleet_add(wsp, "pdm_motor", "Acme", "M1", "Acme M1 datasheet rev B", serial="A100", od_in=6.75)
7925
+ e_dup = _raises(lambda: TX.fleet_add(wsp, "pdm_motor", "Acme", "M1", "Acme M1 datasheet rev B", serial="A100"), ValueError, "already")
7926
+ users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator"); users.add("v", "viewer-pass-1", "viewer")
7927
+ svc = Service(wsp, port=0, scheduler=False).start()
7928
+ base_url = svc.url.rstrip("/")
7929
+
7930
+ def session():
7931
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
7932
+
7933
+ def call(method, path, body=None):
7934
+ req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
7935
+ req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
7936
+ try:
7937
+ with o.open(req, timeout=60) as rr_:
7938
+ return rr_.status, json.loads(rr_.read())
7939
+ except urllib.error.HTTPError as e:
7940
+ return e.code, json.loads(e.read())
7941
+ return call
7942
+ try:
7943
+ view, op = session(), session()
7944
+ view("POST", "/api/login", {"name": "v", "password": "viewer-pass-1"}); op("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
7945
+ st_t, tx = view("GET", "/api/tools/taxonomy")
7946
+ st_q, txq = view("GET", "/api/tools/taxonomy?q=jar")
7947
+ st_v, _ = view("POST", "/api/tools/fleet", {"class_id": "esp", "maker": "Acme", "model": "P2", "datasheet": "Acme P2 sheet"})
7948
+ st_nods, nods = op("POST", "/api/tools/fleet", {"class_id": "esp", "maker": "Acme", "model": "P2", "datasheet": ""})
7949
+ st_add, added = op("POST", "/api/tools/fleet", {"class_id": "esp", "maker": "Acme", "model": "P2", "datasheet": "Acme P2 sheet rev A", "od_in": "5.38"})
7950
+ st_fl, fl = view("GET", "/api/tools/fleet")
7951
+ st_rm, _ = op("POST", f"/api/tools/fleet/{r1['id']}/remove", {})
7952
+ st_rm2, _ = op("POST", "/api/tools/fleet/nothing-here/remove", {})
7953
+ finally:
7954
+ svc.stop()
7955
+ audit = Path(wsp, "records", "audit.jsonl").read_text(encoding="utf-8")
7956
+ c2 = {
7957
+ "fleet_rules": e_ds and e_cls and e_dup and r1["id"] == "acme-m1-a100" and r1["class_name"] == TX.TAXONOMY["pdm_motor"]["name"] and r1["od_in"] == 6.75
7958
+ and Path(wsp, "config", "fleet.json").is_file(),
7959
+ "api": st_t == 200 and len(tx["classes"]) == sm["classes"] and [p[0] for p in tx["parts"]] == list(TX.PARTS) and tx["summary"]["figures"] == sm["figures"]
7960
+ and st_q == 200 and {c["id"] for c in txq["classes"]} >= {"drilling_jar", "jar_accelerator"} and len(txq["classes"]) < 10
7961
+ and tx["fleet"] and tx["fleet"][0]["id"] == "acme-m1-a100",
7962
+ "roles": st_v == 403 and st_nods == 400 and "datasheet" in nods.get("error", "") and st_add == 200 and added["od_in"] == 5.38
7963
+ and st_fl == 200 and {t["id"] for t in fl["tools"]} == {"acme-m1-a100", "acme-p2"} and st_rm == 200 and st_rm2 == 404
7964
+ and [t["id"] for t in TX.fleet_list(wsp)] == ["acme-p2"],
7965
+ "audit": '"fleet.add"' in audit and '"fleet.remove"' in audit and "Acme P2 sheet rev A" in audit,
7966
+ }
7967
+ failed2 = [k for k, v in c2.items() if not v]
7968
+ ok(not failed2,
7969
+ "BZ2 the fleet: a tool of yours needs its class and its datasheet citation, and is not added twice; it is kept in the workspace's config; the API "
7970
+ "serves the classes, the tutorial parts and the fleet to any signed-in user, adds and removes for an operator and not a viewer, says why an "
7971
+ "addition without a datasheet is turned away, and every addition and removal is in the audit log"
7972
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
7973
+
7974
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
7975
+ help_md = (pkg / "help" / "tools.md").read_text(encoding="utf-8")
7976
+ r_cls = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "classes"], capture_output=True, text=True)
7977
+ r_cat = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "classes", "--category", "fishing"], capture_output=True, text=True)
7978
+ r_tut = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "tutorial", "--class", "drilling_jar"], capture_output=True, text=True)
7979
+ r_none = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "tutorial"], capture_output=True, text=True)
7980
+ r_bad = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "tutorial", "--class", "warp_drive"], capture_output=True, text=True)
7981
+ r_add = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "fleet-add", "--workspace", wsp, "--class", "drilling_jar", "--maker", "Acme",
7982
+ "--model", "J9", "--datasheet", "Acme J9 sheet rev C"], capture_output=True, text=True)
7983
+ r_nods = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "fleet-add", "--workspace", wsp, "--class", "drilling_jar", "--maker", "Acme",
7984
+ "--model", "J10"], capture_output=True, text=True)
7985
+ r_list = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "fleet", "--workspace", wsp], capture_output=True, text=True)
7986
+ r_nows = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "fleet"], capture_output=True, text=True)
7987
+ c3 = {
7988
+ "page": all(x in page_src for x in ("Downhole tool library", "class=\"tl-chip\"", 'role="tooltip"', "aria-haspopup=\"dialog\"", ".tl-chip:focus-visible .tl-tip",
7989
+ "@media (hover:none)", "showModal()", "dialog.tl-dlg::backdrop", "/api/tools/taxonomy", "/api/tools/fleet", "Add one of yours",
7990
+ "Your fleet", "tl-open", "Esc closes it")),
7991
+ "cli": r_cls.returncode == 0 and f"{sm['classes']} of {sm['classes']} tool classes" in r_cls.stdout and "Fishing, milling and intervention" in r_cls.stdout
7992
+ and r_cat.returncode == 0 and "overshot" in r_cat.stdout and "pdm_motor" not in r_cat.stdout
7993
+ and r_tut.returncode == 0 and "What it is: " in r_tut.stdout and "In this program: " in r_tut.stdout
7994
+ and r_none.returncode != 0 and "--class pdm_motor" in (r_none.stderr + r_none.stdout) and "<" not in (r_none.stderr + r_none.stdout)
7995
+ and r_bad.returncode != 0 and "no tool class 'warp_drive'" in (r_bad.stderr + r_bad.stdout),
7996
+ "cli_fleet": r_add.returncode == 0 and "added acme-j9" in r_add.stdout and r_nods.returncode != 0 and "datasheet" in (r_nods.stderr + r_nods.stdout)
7997
+ and r_list.returncode == 0 and "Acme J9 sheet rev C" in r_list.stdout and r_nows.returncode != 0 and "needs --workspace" in (r_nows.stderr + r_nows.stdout),
7998
+ "help": not H.check("tools") and "tutorial" in help_md and "fleet" in help_md and "the tutorials carry no numbers at all" in help_md.split("will not call a measurement")[1],
7999
+ }
8000
+ failed3 = [k for k, v in c3.items() if not v]
8001
+ ok(not failed3,
8002
+ "BZ3 the page and the command line: a chip per class that shows its description on hover and on keyboard focus (not on a touch screen, where a tap "
8003
+ "opens it), a tutorial dialog that opens on click, tap or Enter and closes on Esc or the backdrop, a find box and a category filter, the fleet card, "
8004
+ "the gauge library's Tutorial buttons; `gea tools --action classes | tutorial | fleet | fleet-add`, each refusing a missing argument by name without "
8005
+ "a placeholder; the help page"
8006
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
7823
8007
 
7824
8008
  def main() -> int:
7825
8009
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
@@ -7901,6 +8085,7 @@ def main() -> int:
7901
8085
  section_bw_catalog_map(tmp)
7902
8086
  section_bx_pasteable(tmp)
7903
8087
  section_by_edr(tmp)
8088
+ section_bz_tool_library(tmp)
7904
8089
  if _FAILS:
7905
8090
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
7906
8091
  for f in _FAILS: