gea-program 0.40.0__tar.gz → 0.41.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 (320) hide show
  1. {gea_program-0.40.0 → gea_program-0.41.0}/PKG-INFO +26 -3
  2. {gea_program-0.40.0 → gea_program-0.41.0}/README.md +25 -2
  3. {gea_program-0.40.0 → gea_program-0.41.0}/gea/__init__.py +1 -1
  4. {gea_program-0.40.0 → gea_program-0.41.0}/gea/__main__.py +70 -0
  5. {gea_program-0.40.0 → gea_program-0.41.0}/gea/acceptance_tests.py +261 -4
  6. gea_program-0.41.0/gea/declarations.py +622 -0
  7. gea_program-0.41.0/gea/help/declare.md +13 -0
  8. gea_program-0.41.0/gea/help/penetrator.md +15 -0
  9. {gea_program-0.40.0 → gea_program-0.41.0}/gea/helplib.py +3 -1
  10. gea_program-0.41.0/gea/penetrator.py +520 -0
  11. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/mud_motor_specs.json +52 -2
  12. gea_program-0.41.0/gea/reference/penetrator_ratings.json +75 -0
  13. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/tool_sources.json +21 -0
  14. {gea_program-0.40.0 → gea_program-0.41.0}/gea/service.py +31 -0
  15. {gea_program-0.40.0 → gea_program-0.41.0}/gea/tool_taxonomy.py +10 -1
  16. {gea_program-0.40.0 → gea_program-0.41.0}/gea/web/app.html +84 -2
  17. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/PKG-INFO +26 -3
  18. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/SOURCES.txt +5 -0
  19. {gea_program-0.40.0 → gea_program-0.41.0}/pyproject.toml +1 -1
  20. {gea_program-0.40.0 → gea_program-0.41.0}/LICENSE +0 -0
  21. {gea_program-0.40.0 → gea_program-0.41.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  22. {gea_program-0.40.0 → gea_program-0.41.0}/gea/accuracy_statement.py +0 -0
  23. {gea_program-0.40.0 → gea_program-0.41.0}/gea/alarm_engine.py +0 -0
  24. {gea_program-0.40.0 → gea_program-0.41.0}/gea/backup.py +0 -0
  25. {gea_program-0.40.0 → gea_program-0.41.0}/gea/bench.py +0 -0
  26. {gea_program-0.40.0 → gea_program-0.41.0}/gea/blind_harness.py +0 -0
  27. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  28. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  29. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  30. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  31. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  32. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  33. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  34. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  35. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  36. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  37. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  38. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  39. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  40. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  41. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  42. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  43. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  44. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  45. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  46. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  47. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  48. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  49. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  50. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  51. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  52. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  53. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  54. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  55. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  56. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  57. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  58. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  59. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  60. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  61. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  62. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  63. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  64. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  65. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  66. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  67. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  68. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  69. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  70. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  71. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  72. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  73. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  74. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  75. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  76. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  77. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  78. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  79. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  80. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  81. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  82. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  83. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  84. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  85. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  86. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  87. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  88. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  89. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  90. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  91. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  92. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  93. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  94. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/med_160_sapropels.txt +0 -0
  95. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  96. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  97. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  98. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  99. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  100. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  101. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  102. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  103. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  104. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  105. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  106. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  107. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  108. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  109. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  110. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  111. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  112. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  113. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  114. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  115. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  116. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  117. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  118. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  119. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  120. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  121. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  122. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  123. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  124. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  125. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  126. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  127. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  128. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  129. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  130. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  131. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog_map.py +0 -0
  132. {gea_program-0.40.0 → gea_program-0.41.0}/gea/catalog_register.py +0 -0
  133. {gea_program-0.40.0 → gea_program-0.41.0}/gea/certificates.py +0 -0
  134. {gea_program-0.40.0 → gea_program-0.41.0}/gea/cli.py +0 -0
  135. {gea_program-0.40.0 → gea_program-0.41.0}/gea/client_reports.py +0 -0
  136. {gea_program-0.40.0 → gea_program-0.41.0}/gea/config_versioning.py +0 -0
  137. {gea_program-0.40.0 → gea_program-0.41.0}/gea/conformance.py +0 -0
  138. {gea_program-0.40.0 → gea_program-0.41.0}/gea/control_room.py +0 -0
  139. {gea_program-0.40.0 → gea_program-0.41.0}/gea/correlation.py +0 -0
  140. {gea_program-0.40.0 → gea_program-0.41.0}/gea/dashboard.py +0 -0
  141. {gea_program-0.40.0 → gea_program-0.41.0}/gea/deviation.py +0 -0
  142. {gea_program-0.40.0 → gea_program-0.41.0}/gea/dlis.py +0 -0
  143. {gea_program-0.40.0 → gea_program-0.41.0}/gea/dnp3.py +0 -0
  144. {gea_program-0.40.0 → gea_program-0.41.0}/gea/doctor.py +0 -0
  145. {gea_program-0.40.0 → gea_program-0.41.0}/gea/downhole_engine.py +0 -0
  146. {gea_program-0.40.0 → gea_program-0.41.0}/gea/drift_monitor.py +0 -0
  147. {gea_program-0.40.0 → gea_program-0.41.0}/gea/drill_string.py +0 -0
  148. {gea_program-0.40.0 → gea_program-0.41.0}/gea/earth_model.py +0 -0
  149. {gea_program-0.40.0 → gea_program-0.41.0}/gea/economics.py +0 -0
  150. {gea_program-0.40.0 → gea_program-0.41.0}/gea/edr.py +0 -0
  151. {gea_program-0.40.0 → gea_program-0.41.0}/gea/elevation.py +0 -0
  152. {gea_program-0.40.0 → gea_program-0.41.0}/gea/etp.py +0 -0
  153. {gea_program-0.40.0 → gea_program-0.41.0}/gea/etp12_schemas.json +0 -0
  154. {gea_program-0.40.0 → gea_program-0.41.0}/gea/example_register_map.json +0 -0
  155. {gea_program-0.40.0 → gea_program-0.41.0}/gea/fat_sat.py +0 -0
  156. {gea_program-0.40.0 → gea_program-0.41.0}/gea/files.py +0 -0
  157. {gea_program-0.40.0 → gea_program-0.41.0}/gea/fixtures/README.md +0 -0
  158. {gea_program-0.40.0 → gea_program-0.41.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  159. {gea_program-0.40.0 → gea_program-0.41.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  160. {gea_program-0.40.0 → gea_program-0.41.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  161. {gea_program-0.40.0 → gea_program-0.41.0}/gea/follower.py +0 -0
  162. {gea_program-0.40.0 → gea_program-0.41.0}/gea/forward_model.py +0 -0
  163. {gea_program-0.40.0 → gea_program-0.41.0}/gea/friction_priors.py +0 -0
  164. {gea_program-0.40.0 → gea_program-0.41.0}/gea/gamma.py +0 -0
  165. {gea_program-0.40.0 → gea_program-0.41.0}/gea/gauge_aging.py +0 -0
  166. {gea_program-0.40.0 → gea_program-0.41.0}/gea/gauge_specs.py +0 -0
  167. {gea_program-0.40.0 → gea_program-0.41.0}/gea/geodesy.py +0 -0
  168. {gea_program-0.40.0 → gea_program-0.41.0}/gea/gravity_reference.py +0 -0
  169. {gea_program-0.40.0 → gea_program-0.41.0}/gea/hartip.py +0 -0
  170. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/TESTER_GUIDE.md +0 -0
  171. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/alarms.md +0 -0
  172. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/audit-update.md +0 -0
  173. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/backup.md +0 -0
  174. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/catalog.md +0 -0
  175. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/charts.md +0 -0
  176. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/conformance.md +0 -0
  177. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/control-room.md +0 -0
  178. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/data-in.md +0 -0
  179. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/dlis.md +0 -0
  180. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/doctor.md +0 -0
  181. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/drift.md +0 -0
  182. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/drillstring.md +0 -0
  183. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/economics.md +0 -0
  184. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/edr.md +0 -0
  185. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/etp.md +0 -0
  186. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/field-ports.md +0 -0
  187. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/files.md +0 -0
  188. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/historian.md +0 -0
  189. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/instruments.md +0 -0
  190. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/interval.md +0 -0
  191. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/map.md +0 -0
  192. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/month-end.md +0 -0
  193. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/notifications.md +0 -0
  194. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/osdu.md +0 -0
  195. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/patches.md +0 -0
  196. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/ppdm.md +0 -0
  197. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/quakeml.md +0 -0
  198. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/quality.md +0 -0
  199. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/seedlink.md +0 -0
  200. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/seismic.md +0 -0
  201. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/setup.md +0 -0
  202. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/simulation.md +0 -0
  203. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/site.md +0 -0
  204. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/sites.md +0 -0
  205. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/slip.md +0 -0
  206. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/soak.md +0 -0
  207. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/sra.md +0 -0
  208. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/start.md +0 -0
  209. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/structure.md +0 -0
  210. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/tools.md +0 -0
  211. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/transients.md +0 -0
  212. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/upkeep.md +0 -0
  213. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/vibration.md +0 -0
  214. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/well-report.md +0 -0
  215. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/well-tests.md +0 -0
  216. {gea_program-0.40.0 → gea_program-0.41.0}/gea/help/well3d.md +0 -0
  217. {gea_program-0.40.0 → gea_program-0.41.0}/gea/housekeeping.py +0 -0
  218. {gea_program-0.40.0 → gea_program-0.41.0}/gea/interval.py +0 -0
  219. {gea_program-0.40.0 → gea_program-0.41.0}/gea/inverse_engine.py +0 -0
  220. {gea_program-0.40.0 → gea_program-0.41.0}/gea/jobs.py +0 -0
  221. {gea_program-0.40.0 → gea_program-0.41.0}/gea/live_ports.py +0 -0
  222. {gea_program-0.40.0 → gea_program-0.41.0}/gea/loadtest.py +0 -0
  223. {gea_program-0.40.0 → gea_program-0.41.0}/gea/matplotlib_demo.py +0 -0
  224. {gea_program-0.40.0 → gea_program-0.41.0}/gea/modbus.py +0 -0
  225. {gea_program-0.40.0 → gea_program-0.41.0}/gea/modbus_rtu.py +0 -0
  226. {gea_program-0.40.0 → gea_program-0.41.0}/gea/model_card.py +0 -0
  227. {gea_program-0.40.0 → gea_program-0.41.0}/gea/month_end.py +0 -0
  228. {gea_program-0.40.0 → gea_program-0.41.0}/gea/mqtt_port.py +0 -0
  229. {gea_program-0.40.0 → gea_program-0.41.0}/gea/notify.py +0 -0
  230. {gea_program-0.40.0 → gea_program-0.41.0}/gea/opcua_port.py +0 -0
  231. {gea_program-0.40.0 → gea_program-0.41.0}/gea/operator_app.py +0 -0
  232. {gea_program-0.40.0 → gea_program-0.41.0}/gea/osdu.py +0 -0
  233. {gea_program-0.40.0 → gea_program-0.41.0}/gea/patches.py +0 -0
  234. {gea_program-0.40.0 → gea_program-0.41.0}/gea/permits.py +0 -0
  235. {gea_program-0.40.0 → gea_program-0.41.0}/gea/piwebapi.py +0 -0
  236. {gea_program-0.40.0 → gea_program-0.41.0}/gea/ports.py +0 -0
  237. {gea_program-0.40.0 → gea_program-0.41.0}/gea/ppdm.py +0 -0
  238. {gea_program-0.40.0 → gea_program-0.41.0}/gea/profile_catalog.py +0 -0
  239. {gea_program-0.40.0 → gea_program-0.41.0}/gea/project.py +0 -0
  240. {gea_program-0.40.0 → gea_program-0.41.0}/gea/qt6_downhole_app.py +0 -0
  241. {gea_program-0.40.0 → gea_program-0.41.0}/gea/quakeml.py +0 -0
  242. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reconciler.py +0 -0
  243. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  244. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  245. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  246. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/eia_prices.provenance.json +0 -0
  247. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  248. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  249. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  250. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  251. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  252. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/us_outline.json +0 -0
  253. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/us_outline.provenance.json +0 -0
  254. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_3d.provenance.json +0 -0
  255. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  256. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  257. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  258. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_surfaces.json.gz +0 -0
  259. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  260. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  261. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  262. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  263. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  264. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  265. {gea_program-0.40.0 → gea_program-0.41.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  266. {gea_program-0.40.0 → gea_program-0.41.0}/gea/rock_inventory.py +0 -0
  267. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sample_record.py +0 -0
  268. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sample_well_profile.csv +0 -0
  269. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sbom.py +0 -0
  270. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seedlink.py +0 -0
  271. {gea_program-0.40.0 → gea_program-0.41.0}/gea/segy.py +0 -0
  272. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic.py +0 -0
  273. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_array.py +0 -0
  274. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_assoc.py +0 -0
  275. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_detect.py +0 -0
  276. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_film.py +0 -0
  277. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_harmonic.py +0 -0
  278. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_qc.py +0 -0
  279. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_response.py +0 -0
  280. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_signature.py +0 -0
  281. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_track.py +0 -0
  282. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_uncertainty.py +0 -0
  283. {gea_program-0.40.0 → gea_program-0.41.0}/gea/seismic_unlisted.py +0 -0
  284. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sensor_swap.py +0 -0
  285. {gea_program-0.40.0 → gea_program-0.41.0}/gea/series_cache.py +0 -0
  286. {gea_program-0.40.0 → gea_program-0.41.0}/gea/service_life.py +0 -0
  287. {gea_program-0.40.0 → gea_program-0.41.0}/gea/setup_checklist.py +0 -0
  288. {gea_program-0.40.0 → gea_program-0.41.0}/gea/shapefile.py +0 -0
  289. {gea_program-0.40.0 → gea_program-0.41.0}/gea/shell.py +0 -0
  290. {gea_program-0.40.0 → gea_program-0.41.0}/gea/shut_in.py +0 -0
  291. {gea_program-0.40.0 → gea_program-0.41.0}/gea/simulation.py +0 -0
  292. {gea_program-0.40.0 → gea_program-0.41.0}/gea/site_map.py +0 -0
  293. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sla_report.py +0 -0
  294. {gea_program-0.40.0 → gea_program-0.41.0}/gea/slip.py +0 -0
  295. {gea_program-0.40.0 → gea_program-0.41.0}/gea/soak.py +0 -0
  296. {gea_program-0.40.0 → gea_program-0.41.0}/gea/sra.py +0 -0
  297. {gea_program-0.40.0 → gea_program-0.41.0}/gea/store_forward.py +0 -0
  298. {gea_program-0.40.0 → gea_program-0.41.0}/gea/strata_join.py +0 -0
  299. {gea_program-0.40.0 → gea_program-0.41.0}/gea/structure.py +0 -0
  300. {gea_program-0.40.0 → gea_program-0.41.0}/gea/supervisor.py +0 -0
  301. {gea_program-0.40.0 → gea_program-0.41.0}/gea/survey_cmd.py +0 -0
  302. {gea_program-0.40.0 → gea_program-0.41.0}/gea/survey_view.py +0 -0
  303. {gea_program-0.40.0 → gea_program-0.41.0}/gea/telemetry.py +0 -0
  304. {gea_program-0.40.0 → gea_program-0.41.0}/gea/tool_catalog.py +0 -0
  305. {gea_program-0.40.0 → gea_program-0.41.0}/gea/tool_library.py +0 -0
  306. {gea_program-0.40.0 → gea_program-0.41.0}/gea/transient.py +0 -0
  307. {gea_program-0.40.0 → gea_program-0.41.0}/gea/vibration.py +0 -0
  308. {gea_program-0.40.0 → gea_program-0.41.0}/gea/well3d.py +0 -0
  309. {gea_program-0.40.0 → gea_program-0.41.0}/gea/well_assembler.py +0 -0
  310. {gea_program-0.40.0 → gea_program-0.41.0}/gea/well_report.py +0 -0
  311. {gea_program-0.40.0 → gea_program-0.41.0}/gea/well_test_validation.py +0 -0
  312. {gea_program-0.40.0 → gea_program-0.41.0}/gea/wits0.py +0 -0
  313. {gea_program-0.40.0 → gea_program-0.41.0}/gea/witsml.py +0 -0
  314. {gea_program-0.40.0 → gea_program-0.41.0}/gea/workspace.py +0 -0
  315. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/dependency_links.txt +0 -0
  316. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/entry_points.txt +0 -0
  317. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/requires.txt +0 -0
  318. {gea_program-0.40.0 → gea_program-0.41.0}/gea_program.egg-info/top_level.txt +0 -0
  319. {gea_program-0.40.0 → gea_program-0.41.0}/setup.cfg +0 -0
  320. {gea_program-0.40.0 → gea_program-0.41.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.40.0
3
+ Version: 0.41.0
4
4
  Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
5
5
  Author: Daniel T. Murphy
6
6
  License: MPL-2.0
@@ -648,6 +648,29 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
648
648
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
649
649
  `rtserve.iris.washington.edu:18000`, network `TX`.
650
650
 
651
+ ### The declarations sheet
652
+
653
+ Everything the operator declares about a pad comes in from one spreadsheet:
654
+ each well's API number, position and reference elevation, its directional
655
+ survey, the day rates, one-off costs and AFE, the fleet, the drill-string
656
+ runs and their components, and the ESP penetrator and drive. Every row is
657
+ checked against the workspace and planned NEW, CHANGE or SAME before
658
+ anything is written; one error and nothing is. The sheet the program writes
659
+ for a workspace is already filled with what is declared, so a site fills in
660
+ the blanks. On the Setup tab, or `gea declare --template`, `gea declare
661
+ --file`, `gea help declare`.
662
+
663
+ ### Completion - the ESP wellhead penetrator
664
+
665
+ The Completion tab keeps each ESP well's penetrator installation record -
666
+ the installer and their certification, the motor, the gauge distance, the
667
+ insulation readings, the void test - and checks the well's ESP drive, motor
668
+ nameplate and wellhead against the penetrator's ratings as its maker's
669
+ document prints them, one line each: voltage, current, frequency, phases,
670
+ hazardous area, gas group, temperature class, ambient range and altitude.
671
+ It carries the permanent magnet motor safety note with its sources.
672
+ `gea penetrator`, `gea help penetrator`.
673
+
651
674
  ### Drill string
652
675
 
653
676
  The Drill String tab holds each run's string from the bit up - drawn as a
@@ -657,7 +680,7 @@ its declared service interval. The downhole motor tracker charts the
657
680
  differential against the maker's limits, the stalls, the motor's and the
658
681
  bit's speed and a torque estimate, and judges the motor's stability from
659
682
  the slow oscillations of the torque and the differential, block by block.
660
- The tool catalogue carries 41 downhole motors with the maker's figures.
683
+ The tool catalogue carries 43 downhole motors with the maker's figures.
661
684
  `gea drillstring`, `gea tools --action motors`, `gea help drillstring`.
662
685
 
663
686
  ### Simulation
@@ -785,7 +808,7 @@ library**: every class of downhole tool in use across the industry - bits
785
808
  and hole openers, motors and rotary steerable systems, MWD and LWD, the drill
786
809
  string and its subs, casing and cementing equipment, completion and
787
810
  artificial lift, wireline and cased-hole tools, fishing and milling, test
788
- tools - ninety-six classes in nine categories. Point at a tool, or Tab to
811
+ tools - ninety-seven classes in nine categories. Point at a tool, or Tab to
789
812
  it, for a one-line description; click, tap or press Enter for its tutorial:
790
813
  what it is, where it runs, how it works, what it shows in the data, what to
791
814
  watch, and what this program does with it, with the published figures
@@ -609,6 +609,29 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
609
609
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
610
610
  `rtserve.iris.washington.edu:18000`, network `TX`.
611
611
 
612
+ ### The declarations sheet
613
+
614
+ Everything the operator declares about a pad comes in from one spreadsheet:
615
+ each well's API number, position and reference elevation, its directional
616
+ survey, the day rates, one-off costs and AFE, the fleet, the drill-string
617
+ runs and their components, and the ESP penetrator and drive. Every row is
618
+ checked against the workspace and planned NEW, CHANGE or SAME before
619
+ anything is written; one error and nothing is. The sheet the program writes
620
+ for a workspace is already filled with what is declared, so a site fills in
621
+ the blanks. On the Setup tab, or `gea declare --template`, `gea declare
622
+ --file`, `gea help declare`.
623
+
624
+ ### Completion - the ESP wellhead penetrator
625
+
626
+ The Completion tab keeps each ESP well's penetrator installation record -
627
+ the installer and their certification, the motor, the gauge distance, the
628
+ insulation readings, the void test - and checks the well's ESP drive, motor
629
+ nameplate and wellhead against the penetrator's ratings as its maker's
630
+ document prints them, one line each: voltage, current, frequency, phases,
631
+ hazardous area, gas group, temperature class, ambient range and altitude.
632
+ It carries the permanent magnet motor safety note with its sources.
633
+ `gea penetrator`, `gea help penetrator`.
634
+
612
635
  ### Drill string
613
636
 
614
637
  The Drill String tab holds each run's string from the bit up - drawn as a
@@ -618,7 +641,7 @@ its declared service interval. The downhole motor tracker charts the
618
641
  differential against the maker's limits, the stalls, the motor's and the
619
642
  bit's speed and a torque estimate, and judges the motor's stability from
620
643
  the slow oscillations of the torque and the differential, block by block.
621
- The tool catalogue carries 41 downhole motors with the maker's figures.
644
+ The tool catalogue carries 43 downhole motors with the maker's figures.
622
645
  `gea drillstring`, `gea tools --action motors`, `gea help drillstring`.
623
646
 
624
647
  ### Simulation
@@ -746,7 +769,7 @@ library**: every class of downhole tool in use across the industry - bits
746
769
  and hole openers, motors and rotary steerable systems, MWD and LWD, the drill
747
770
  string and its subs, casing and cementing equipment, completion and
748
771
  artificial lift, wireline and cased-hole tools, fishing and milling, test
749
- tools - ninety-six classes in nine categories. Point at a tool, or Tab to
772
+ tools - ninety-seven classes in nine categories. Point at a tool, or Tab to
750
773
  it, for a one-line description; click, tap or press Enter for its tutorial:
751
774
  what it is, where it runs, how it works, what it shows in the data, what to
752
775
  watch, and what this program does with it, with the published figures
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.40.0"
126
+ __version__ = "0.41.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -524,6 +524,21 @@ def main(argv=None) -> int:
524
524
  p_ds.add_argument("--actor", type=str, default="cli")
525
525
  p_ds.add_argument("--json", action="store_true")
526
526
 
527
+ p_dc = sub.add_parser("declare", help="the declarations sheet: wells' identity, position and elevation, surveys, rates, items and AFEs, fleet tools, drill-string runs, ESP penetrators and drives - one CSV, checked against the workspace before anything is written")
528
+ p_dc.add_argument("--workspace", type=str, required=True)
529
+ p_dc.add_argument("--file", type=str, default=None, help="the sheet (CSV); every row is planned NEW, CHANGE or SAME and nothing is written without --apply")
530
+ p_dc.add_argument("--apply", action="store_true", help="write the NEW and CHANGE rows (only when the sheet has no error)")
531
+ p_dc.add_argument("--template", type=str, default=None, help="write the sheet for this workspace, filled with what is already declared, to this path")
532
+ p_dc.add_argument("--actor", type=str, default="cli")
533
+ p_dc.add_argument("--json", action="store_true")
534
+
535
+ p_pn = sub.add_parser("penetrator", help="the ESP wellhead penetrator: the penetrators the program knows with their ratings, the permanent magnet motor safety note, and each well's installation record and drive checked against the ratings")
536
+ p_pn.add_argument("--workspace", type=str, default=None)
537
+ p_pn.add_argument("--well", type=str, default=None)
538
+ p_pn.add_argument("--remove", type=str, default=None, choices=("install", "drive"), help="remove the well's installation record or its drive")
539
+ p_pn.add_argument("--actor", type=str, default="cli")
540
+ p_pn.add_argument("--json", action="store_true")
541
+
527
542
  p_sm = sub.add_parser("simulation", help="the simulation in words: the scenes (Volve 15/9-F-4 with its logs and interpretation, Volve F-5 and F-10 on the survey clock, every drilling-recorder well), and what the record says at a depth (the Simulation tab replays it)")
528
543
  p_sm.add_argument("--workspace", type=str, default=None)
529
544
  p_sm.add_argument("--scene", type=str, default=None, help="e.g. volve-f4, or well:Well-30636033")
@@ -2074,6 +2089,61 @@ def main(argv=None) -> int:
2074
2089
  raise SystemExit(f"drillstring: {e.args[0] if e.args else e}")
2075
2090
  print(_json.dumps(p, default=str) if a.json else _DS.text(p))
2076
2091
  return 0
2092
+ elif a.cmd == "declare":
2093
+ import json as _json
2094
+ from . import declarations as _DC
2095
+ from .cli import _safe_stdout
2096
+ from .workspace import Workspace, WorkspaceError
2097
+ _safe_stdout()
2098
+ try:
2099
+ ws = Workspace(a.workspace)
2100
+ except WorkspaceError as e:
2101
+ raise SystemExit(f"declare: {e}")
2102
+ if a.template:
2103
+ with open(a.template, "w", encoding="utf-8", newline="") as f:
2104
+ f.write(_DC.template(ws))
2105
+ print(f"wrote {a.template}: fill in the blanks, then gea declare --workspace {a.workspace} --file {a.template}")
2106
+ return 0
2107
+ if not a.file:
2108
+ raise SystemExit("declare: give --file <sheet.csv> (or --template <out.csv> to start one)")
2109
+ try:
2110
+ with open(a.file, encoding="utf-8-sig") as f:
2111
+ text_ = f.read()
2112
+ except OSError as e:
2113
+ raise SystemExit(f"declare: {e}")
2114
+ p = _DC.apply(ws, text_, a.actor, os.path.dirname(os.path.abspath(a.file)), write=a.apply)
2115
+ print(_json.dumps(p, default=str) if a.json else _DC.text(p))
2116
+ return 1 if p["errors"] else 0
2117
+ elif a.cmd == "penetrator":
2118
+ import json as _json
2119
+ from . import penetrator as _PN
2120
+ from .cli import _safe_stdout
2121
+ from .workspace import Workspace, WorkspaceError
2122
+ _safe_stdout()
2123
+ ws = None
2124
+ if a.workspace:
2125
+ try:
2126
+ ws = Workspace(a.workspace)
2127
+ except WorkspaceError as e:
2128
+ raise SystemExit(f"penetrator: {e}")
2129
+ try:
2130
+ if a.remove:
2131
+ if ws is None or not a.well:
2132
+ raise SystemExit("penetrator: --remove needs --workspace and --well")
2133
+ _PN.remove(ws, a.actor, a.well, a.remove)
2134
+ print(f"removed the {a.remove} record of {a.well}")
2135
+ return 0
2136
+ if a.well:
2137
+ if ws is None:
2138
+ raise SystemExit("penetrator: --well needs --workspace")
2139
+ c = _PN.check(ws, a.well)
2140
+ print(_json.dumps(c, default=str) if a.json else _PN.text_check(c))
2141
+ return 0
2142
+ p = _PN.page(ws)
2143
+ except (ValueError, KeyError, OSError) as e:
2144
+ raise SystemExit(f"penetrator: {e.args[0] if e.args else e}")
2145
+ print(_json.dumps(p, default=str) if a.json else _PN.text(p))
2146
+ return 0
2077
2147
  elif a.cmd == "simulation":
2078
2148
  import json as _json
2079
2149
  from . import simulation as _SIM
@@ -9309,8 +9309,8 @@ def section_ci_drill_string(tmp: str) -> None:
9309
9309
  ms = cat["motors"]
9310
9310
  m64, m89 = DS.motor_spec("msd-6-3-4-78-6.4"), DS.motor_spec("msd-8-3-8-89-4.0")
9311
9311
  c1 = {
9312
- "catalogue": len(ms) == 41 and len({m["id"] for m in ms}) == 41 and cat["publisher"].startswith("MS Directional") and cat["url"].startswith("https://")
9313
- and cat["fetched"] == "2026-10-17" and TC.catalog()["counts"]["motors"] == 41,
9312
+ "catalogue": len(ms) == 43 and len({m["id"] for m in ms}) == 43 and cat["publisher"].startswith("MS Directional") and cat["url"].startswith("https://")
9313
+ and cat["fetched"] == "2026-10-17" and TC.catalog()["counts"]["motors"] == 43,
9314
9314
  "sane": all(m["flow_gpm"][0] < m["flow_gpm"][1] and 0 < m["rev_per_gal"] < 2 and m["max_operating_diff_psi"] <= m["max_diff_psi"]
9315
9315
  and m["stator_lobes"] == m["rotor_lobes"] + 1 and m["max_torque_ft_lb"] > 0 and m["od_in"] > 4 for m in ms),
9316
9316
  "as_printed": (m64["rev_per_gal"], m64["flow_gpm"], m64["max_operating_diff_psi"], m64["max_diff_psi"], m64["max_torque_ft_lb"]) == (0.28, [300, 600], 1000, 1820, 14360)
@@ -9323,7 +9323,7 @@ def section_ci_drill_string(tmp: str) -> None:
9323
9323
  c1["unknown"] = True
9324
9324
  failed1 = [k for k, v in c1.items() if not v]
9325
9325
  ok(not failed1,
9326
- "CI1 the downhole motor catalogue: 41 power sections with the maker's flow range, revolutions per gallon, maximum operating and "
9326
+ "CI1 the downhole motor catalogue: 43 power sections with the maker's flow range, revolutions per gallon, maximum operating and "
9327
9327
  "maximum differential, maximum torque and weight on bit as its spec sheets print them, the handbook and the date it was read; "
9328
9328
  "every lobe count one more on the stator than the rotor; counted in the tool catalogue; an unknown motor named"
9329
9329
  + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
@@ -9430,7 +9430,7 @@ def section_ci_drill_string(tmp: str) -> None:
9430
9430
  cli_m = _sp.run([sys.executable, "-m", "gea", "tools", "--action", "motors"], capture_output=True, text=True, cwd=str(pkg.parent))
9431
9431
  page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
9432
9432
  c3.update({
9433
- "api": s_g == 200 and len(pg["runs"]) == 2 and pg["runs"][0]["track"]["usage"]["circulating_hours"] == 16.0 and s_m == 200 and len(mm["motors"]) == 41
9433
+ "api": s_g == 200 and len(pg["runs"]) == 2 and pg["runs"][0]["track"]["usage"]["circulating_hours"] == 16.0 and s_m == 200 and len(mm["motors"]) == 43
9434
9434
  and s_vp == 403 and s_op == 200 and s_bad == 400 and s_nw == 404 and s_rm == 200 and s_rm2 == 404 and s_uw == 404 and s_ac == 404,
9435
9435
  "cli": cli.returncode == 0 and "stability: CHANGED" in cli.stdout and "DUE: Positive displacement mud motor" in cli.stdout
9436
9436
  and cli_m.returncode == 0 and "msd-6-3-4-78-6.4" in cli_m.stdout,
@@ -9447,6 +9447,262 @@ def section_ci_drill_string(tmp: str) -> None:
9447
9447
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
9448
9448
 
9449
9449
 
9450
+ def section_cj_declarations(tmp: str) -> None:
9451
+ """Section CJ - the declarations sheet and the ESP penetrator: the penetrator ratings as the maker's documents print
9452
+ them; an installation record and a drive checked line by line, each way a line can fail found; the sheet planned row
9453
+ by row against the workspace, one error writing nothing, the write going through each declaration's own function,
9454
+ the same sheet again planning only SAME, the template round-tripping; the two motors added to the catalogue; the API,
9455
+ the CLI, the Setup card, the Completion tab and the help pages."""
9456
+ import http.cookiejar
9457
+ import subprocess as _sp
9458
+ import urllib.request
9459
+ import urllib.error
9460
+ from . import declarations as DC
9461
+ from . import drill_string as DS
9462
+ from . import economics as EC
9463
+ from . import helplib as H
9464
+ from . import penetrator as PN
9465
+ from . import tool_taxonomy as TX
9466
+ from .workspace import Workspace
9467
+ from .service import Service, Users
9468
+ pkg = Path(__file__).parent
9469
+ d = Path(tmp, "cj"); d.mkdir()
9470
+ wsp = str(d / "ws")
9471
+ ws = Workspace.create(wsp, "Pad CJ")
9472
+ w1 = ws.add_well_file(str(pkg / "sample_well_profile.csv"), display="ESP-1")
9473
+ w2 = ws.add_well_file(str(pkg / "sample_well_profile.csv"), display="ESP-2")
9474
+
9475
+ # -- CJ1 the penetrator: ratings and the check --------------------------------------------------------------
9476
+ g5 = PN.resolve(None, "genco-gps-5")
9477
+ r5 = g5["ratings"]
9478
+ c1 = {
9479
+ "ratings": (r5["max_volts"], r5["max_amps_fla"], r5["phases"], r5["freq_hz"], r5["proof_test_psi"], r5["working_psi_api"], r5["ambient_f"], r5["altitude_max_m"])
9480
+ == (5000, 300, 3, [10, 100], 10000, 5000, [-30, 110], 2000)
9481
+ and r5["divisions"] == ["Class I, Division 1", "Class I, Division 2"] and r5["zone_group"] == "IIA" and r5["temp_class"] == "T5"
9482
+ and g5["pmm"] is True and g5["certified_installer_required"] is True and "IP-40" in g5["document"] and "Appendix A" in g5["section"],
9483
+ "div2_only": PN.resolve(None, "genco-gps-5d")["ratings"] == {"divisions": ["Class I, Division 2"]} and "IP-20" in PN.resolve(None, "genco-gps-5d")["document"]
9484
+ and "IP-06" in PN.resolve(None, "genco-gps-3")["document"],
9485
+ "no_procedure_text": all(len(json.dumps(p)) < 1500 for p in PN.penetrators()) and "Hot Gloves" not in (pkg / "reference" / "penetrator_ratings.json").read_text(encoding="utf-8"),
9486
+ "pmm_note": len(PN.pmm_note()["text"]) == 3 and "generator" in PN.pmm_note()["text"][0] and any((s.get("url") or "").startswith("https://read.nxtbook.com") for s in PN.pmm_note()["sources"]),
9487
+ "taxonomy": TX.tool_class("wellhead_penetrator")["category"] == "completion" and "Sources (fetched 2026-10-17)" in TX.tutorial_text("wellhead_penetrator")
9488
+ and "Sources (fetched 2026-10-14)" in TX.tutorial_text("pdm_motor"),
9489
+ }
9490
+ bad = {}
9491
+ for k, kw in (("motor", dict(motor_type="diesel")), ("phase", dict(insulation={"D": 5})), ("negative", dict(insulation={"A": -1})),
9492
+ ("min_source", dict(ir_min_megohm=500)), ("yesno", dict(void_held="maybe")), ("date", dict(installed="last week"))):
9493
+ try:
9494
+ PN.install_record(ws, w1["id"], "genco-gps-5", **kw); bad[k] = False
9495
+ except ValueError:
9496
+ bad[k] = True
9497
+ for k, kw in (("source", dict()), ("area", dict(area="Zone 9", source="s")), ("freq", dict(freq_min_hz=90, freq_max_hz=30, source="s")),
9498
+ ("group", dict(gas_group="Q", source="s"))):
9499
+ try:
9500
+ PN.drive_record(ws, w1["id"], **kw); bad["drive_" + k] = False
9501
+ except ValueError:
9502
+ bad["drive_" + k] = True
9503
+ try:
9504
+ PN.install_record(ws, w1["id"], "nope"); bad["unknown"] = False
9505
+ except KeyError:
9506
+ bad["unknown"] = True
9507
+ c1["refused"] = all(bad.values())
9508
+ PN.save_install(ws, "tester", w1["id"], "genco-gps-5", installed="2026-09-01", installer="J. Doe", certified="yes", certificate="GSC-1", motor_type="pmm",
9509
+ gauge_distance_in=31.25, insulation={"A": 2000, "B": 1800, "C": 2200}, ir_test_volts=5000, void_test_psi=5000, void_test_minutes=15, void_held="yes")
9510
+ part = PN.check(ws, w1["id"])
9511
+ PN.save_drive(ws, "tester", w1["id"], maker="D", model="V", max_volts=4800, max_amps=280, freq_min_hz=30, freq_max_hz=90, phases=3, motor_volts=3200,
9512
+ motor_amps=95, area="CL 1 Div 1", gas_group="D", temp_class="T3", ambient_min_f=10, ambient_max_f=105, source="datasheet")
9513
+ ws.set_well(w1["id"], surface={"lat": 31.9, "lon": -102.1, "datum": "NAD83", "elevation": {"ft": 2900, "datum": "KB", "source": "W-2"}}, actor="tester")
9514
+ good = PN.check(ws, w1["id"])
9515
+ st = {r["item"]: r for r in good["rows"]}
9516
+ PN.save_drive(ws, "tester", w1["id"], maker="D", model="V", max_volts=5200, max_amps=320, freq_min_hz=5, freq_max_hz=120, phases=3, motor_volts=3200,
9517
+ motor_amps=95, area="Class I, Zone 0", gas_group="IIC", temp_class="T6", ambient_min_f=-40, ambient_max_f=105, source="datasheet")
9518
+ PN.save_install(ws, "tester", w1["id"], "genco-gps-5", certified="no", motor_type="PMM", gauge_distance_in=31.25, insulation={"A": 2000, "B": 300, "C": 2200},
9519
+ ir_min_megohm=500, ir_min_source="site standard 4", void_test_psi=6000, void_held="yes")
9520
+ out = PN.check(ws, w1["id"])
9521
+ so = {r["item"]: r["status"] for r in out["rows"]}
9522
+ PN.save_install(ws, "tester", w2["id"], "genco-gps-5d", motor_type="PMM", void_test_psi=3000, void_held="no")
9523
+ PN.save_drive(ws, "tester", w2["id"], area="Class I, Division 1", source="drawing")
9524
+ d2 = {r["item"]: r["status"] for r in PN.check(ws, w2["id"])["rows"]}
9525
+ c1.update({
9526
+ "incomplete": part["status"] == "INCOMPLETE" and {r["item"]: r["status"] for r in part["rows"]}["drive output voltage"] == "NOT DECLARED",
9527
+ "within": good["status"] == "WITHIN" and st["altitude"]["declared"] == round(2900 * 0.3048, 1) and st["hazardous area"]["status"] == "WITHIN"
9528
+ and st["temperature class"]["status"] == "WITHIN" and st["void test"]["status"] == "WITHIN" and st["insulation"]["status"] == "RECORDED"
9529
+ and any("PMM safety note" in n for n in good["notes"]),
9530
+ "outside": out["status"] == "OUTSIDE" and all(so[k] == "OUTSIDE" for k in ("certified installer", "insulation", "void test", "drive output voltage",
9531
+ "drive output current", "drive frequency", "hazardous area", "gas group",
9532
+ "temperature class", "ambient temperature"))
9533
+ and "set its current limit at or below 300 A" in next(r["why"] for r in out["rows"] if r["item"] == "drive output current"),
9534
+ "div2_pen": d2["hazardous area"] == "OUTSIDE" and d2["motor"] == "NOT STATED" and d2["void test"] == "OUTSIDE" and d2["drive output voltage"] == "NOT RATED"
9535
+ and d2["certified installer"] == "NOT STATED",
9536
+ "audit": sum(a["action"].startswith("penetrator.") for a in ws.audit_log(500)) >= 6,
9537
+ })
9538
+ PN.remove(ws, "tester", w2["id"], "drive")
9539
+ try:
9540
+ PN.remove(ws, "tester", w2["id"], "drive"); c1["remove"] = False
9541
+ except KeyError:
9542
+ c1["remove"] = "drive" not in PN.records(ws)[w2["id"]]
9543
+ failed1 = [k for k, v in c1.items() if not v]
9544
+ ok(not failed1,
9545
+ "CJ1 the ESP wellhead penetrator: the GPS-5's ratings as its maker's IP-40 Appendix A prints them, the GPS-5D and GPS-3 with only "
9546
+ "the Division 2 of their titles, none of the instructions' procedure text; the PMM safety note with its sources; the penetrator a "
9547
+ "class of the tool library; a bad motor, conductor, reading, date or classification refused; the check INCOMPLETE until the drive "
9548
+ "is declared, WITHIN when every line is, and each of ten lines found OUTSIDE when it is; a Division 2 penetrator at a Division 1 "
9549
+ "wellhead OUTSIDE, its unprinted ratings NOT RATED and its silence on PMM NOT STATED"
9550
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
9551
+
9552
+ # -- CJ2 the sheet ------------------------------------------------------------------------------------------
9553
+ (d / "survey.csv").write_text("MD,Inc,Azi\n0,0,0\n1000,2,45\n2000,5,50\n", encoding="utf-8")
9554
+ sheet = "\n".join([
9555
+ "# Pad CJ declarations",
9556
+ "[wells],well,api_number,well_name,operator,lat,lon,datum,elevation_ft,elevation_datum,elevation_source",
9557
+ ",ESP-2,42-317-41234,Test 2H,Op LLC,31.95,-102.15,NAD83,2910,KB,W-2 completion report",
9558
+ "[fleet],class_id,maker,model,serial,od_in,datasheet,note,ratings",
9559
+ ',wellhead_penetrator,Acme,P-9,SN1,,Acme DS-9 rev A,,"max_volts=4160; max_amps_fla=250; freq_hz=10-90; divisions=Class I, Division 2"',
9560
+ "[rates],well,what,usd_per_day,from,to,source",
9561
+ ',ESP-2,rig spread,"28,500",2026-01-01,2026-02-01,contract sched B',
9562
+ ",ESP-2,rig spread,29000,2026-02-01,,contract sched B amd 1",
9563
+ "[items],well,what,usd,section,source",
9564
+ ",ESP-2,8.5 in PDC bit,18000.5,3,invoice 77",
9565
+ "[afe],well,usd,source",
9566
+ ",ESP-2,4512345.67,AFE 26-114",
9567
+ "[surveys],well,file,azimuth_reference,grid,declination_deg,md_unit,depth_reference,source",
9568
+ ",ESP-2,survey.csv,true,,,ft,KB,operator MWD report",
9569
+ "[runs],well,run,start,end,depth_in_ft,depth_out_ft,note",
9570
+ ",ESP-2,Run 1,2026-01-03T06:00,2026-01-06T12:00,1500,9800,",
9571
+ ",ESP-2,Run 2,2026-01-07T06:00,,9800,,",
9572
+ "[components],well,run,class_id,maker,model,serial,od_in,length_ft,motor_id,service_limit,service_basis,service_source,motor_rev_per_gal,motor_flow_min_gpm,motor_flow_max_gpm,motor_lobes,motor_source",
9573
+ ",ESP-2,Run 1,pdc_bit,Bitco,X616,B1,8.5,1,,,,,,,,,",
9574
+ ",ESP-2,Run 1,pdm_motor,,,M77,,28,msd-8-x-7-3-4-hybrid-78-5.9,200,circulating_hours,rental contract,,,,,",
9575
+ ",ESP-2,Run 2,pdc_bit,Bitco,X616,B2,8.5,,,,,,,,,,",
9576
+ ",ESP-2,Run 2,pdm_motor,Other,OM-1,M9,6.75,,,,,,0.29,300,600,7/8,OM-1 sheet",
9577
+ "[penetrators],well,penetrator,installed,installer,certified,motor,gauge_distance_in,ir_a_megohm,ir_b_megohm,ir_c_megohm,void_test_psi,void_held",
9578
+ ",ESP-2,acme-p-9-sn1,2026-09-02,K. Roe,yes,induction,30.5,1500,1600,1700,4000,yes",
9579
+ "[esp_drives],well,maker,model,max_volts,max_amps,freq_min_hz,freq_max_hz,phases,area,source",
9580
+ ',ESP-2,DriveCo,VSD 520,4000,240,30,85,3,"Class I, Division 2",VSD 520 datasheet; site drawing E-1',
9581
+ ]) + "\n"
9582
+ p0 = DC.apply(ws, sheet, "importer", str(d))
9583
+ nothing = not DC.plan(ws, sheet, str(d))["errors"] and not EC.costs(ws)["wells"].get(w2["id"]) and not DS.runs(ws, w2["id"])
9584
+ errsheet = sheet.replace(",ESP-2,rig spread,29000,2026-02-01,", ",ESP-2,rig spread,29000,2026-01-15,").replace("[afe],well,usd,source", "[afe],well,usd,source,colour") \
9585
+ + "[runs],well,run\n,ESP-9,Run 9\n,ESP-2,Run 1\n" + "Pad,stray\n[nope],x\n"
9586
+ pe = DC.apply(ws, errsheet, "importer", str(d), write=True)
9587
+ clean_after_error = not EC.costs(ws)["wells"].get(w2["id"]) and not DS.runs(ws, w2["id"]) and not pe["written"]
9588
+ pw = DC.apply(ws, sheet, "importer", str(d), write=True)
9589
+ again = DC.plan(ws, sheet, str(d))
9590
+ tpl = DC.template(ws)
9591
+ tp = DC.plan(ws, tpl, str(d))
9592
+ w2r = ws.well(w2["id"])
9593
+ cw = EC.costs(ws)["wells"][w2["id"]]
9594
+ rs = DS.runs(ws, w2["id"])
9595
+ ec = " | ".join(pe["errors"])
9596
+ c2 = {
9597
+ "plan": p0["counts"] == {"NEW": 10, "CHANGE": 1, "SAME": 0} and not p0["errors"] and not p0["applied"] and nothing,
9598
+ "errors": all(x in ec for x in ("the period overlaps the rate on row", "has no column colour", "no well 'ESP-9'", "is on the sheet twice",
9599
+ "the first column holds a table name", "no table [nope]")) and clean_after_error,
9600
+ "written": pw["applied"] and len(pw["written"]) == 11 and w2r["identity"]["api_number"] == "42-317-41234" and w2r["surface"]["elevation"]["ft"] == 2910
9601
+ and w2r["trajectory"]["stations"] == 3 and len(cw["rates"]) == 2 and cw["rates"][0]["usd_per_day"] == 28500 and cw["afe"]["usd"] == 4512345.67
9602
+ and cw["items"][0]["usd"] == 18000.5 and [r["name"] for r in rs] == ["Run 1", "Run 2"]
9603
+ and rs[0]["components"][1]["motor"]["id"] == "msd-8-x-7-3-4-hybrid-78-5.9" and rs[1]["components"][1]["motor"]["rev_per_gal"] == 0.29
9604
+ and PN.records(ws)[w2["id"]]["install"]["penetrator"] == "acme-p-9-sn1" and PN.records(ws)[w2["id"]]["drive"]["area"] == "Class I, Division 2",
9605
+ "fleet_pen": {r["item"]: r for r in PN.check(ws, w2["id"])["rows"]}["drive frequency"]["status"] == "WITHIN"
9606
+ and {r["item"]: r for r in PN.check(ws, w2["id"])["rows"]}["drive output voltage"]["rated"] == 4160,
9607
+ "same": again["counts"] == {"NEW": 0, "CHANGE": 0, "SAME": 11} and not again["errors"],
9608
+ "template": not tp["errors"] and tp["counts"]["NEW"] == 0 and tp["counts"]["CHANGE"] == 0 and tp["counts"]["SAME"] >= 10 and "[esp_drives]" in tpl
9609
+ and "4512345.67" in tpl and tpl.count("[") >= 10,
9610
+ "audited": any(a["action"] == "declarations.import" and a["actor"] == "importer" for a in ws.audit_log(500))
9611
+ and any(a["action"] == "economics.rate" and a["actor"] == "importer" for a in ws.audit_log(500))
9612
+ and any(a["action"] == "fleet.add" and a["actor"] == "importer" for a in ws.audit_log(500)),
9613
+ }
9614
+ changed = sheet.replace(",ESP-2,4512345.67,AFE 26-114", ",ESP-2,4600000,AFE 26-114 rev 1")
9615
+ pc = DC.plan(ws, changed, str(d))
9616
+ c2["change"] = pc["counts"]["CHANGE"] == 1 and next(s_ for s_ in pc["steps"] if s_["table"] == "afe")["action"] == "CHANGE"
9617
+ by_name = DC.plan(ws, "[afe],well,usd,source\n,ESP-1,1000,a\n", "")
9618
+ c2["by_name"] = by_name["counts"]["NEW"] == 1 and by_name["steps"][0]["well"] == w1["id"]
9619
+ failed2 = [k for k, v in c2.items() if not v]
9620
+ ok(not failed2,
9621
+ "CJ2 the declarations sheet: eleven rows over ten tables planned (ten NEW, the installation recorded above a CHANGE) and nothing written until asked; an overlapping rate, an unknown "
9622
+ "column, well or table, a run twice and a stray first cell each named by its row, and with one error nothing written; written, every "
9623
+ "declaration lands where its own command puts it - the well's number, position and elevation, its survey, two rates, an item, the AFE, "
9624
+ "the fleet penetrator whose ratings then drive the check, two runs with a catalogued and a declared motor, the installation and the "
9625
+ "drive - audited under the importer; the same sheet again all SAME; the template it writes round-trips; a changed AFE a CHANGE"
9626
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
9627
+
9628
+ # -- CJ3 the motors, the API, the CLI, the pages ------------------------------------------------------------
9629
+ cat = DS.catalogue()
9630
+ m7, m8 = DS.motor_spec("msd-7-89-4.3"), DS.motor_spec("msd-8-x-7-3-4-hybrid-78-5.9")
9631
+ c3 = {
9632
+ "motors": len(cat["motors"]) == 43 and (m7["rev_per_gal"], m7["flow_gpm"], m7["max_diff_psi"], m7["max_torque_ft_lb"], m7["max_operating_wob_lb"], m7["lube"])
9633
+ == (0.15, [400, 700], 1400, 22828, 55000, "mud lube")
9634
+ and (m8["rev_per_gal"], m8["flow_gpm"], m8["max_operating_diff_psi"], m8["max_diff_psi"], m8["max_torque_ft_lb"], m8["max_operating_wob_lb"], m8["od_in"])
9635
+ == (0.16, [400, 900], 750, 1390, 21870, 60000, 8.0)
9636
+ and "third time" in cat["how"] and any("keeps the sheet's figure" in n for n in cat["notes"]),
9637
+ }
9638
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
9639
+ svc = Service(wsp, port=0, scheduler=False).start()
9640
+ base_url = svc.url.rstrip("/")
9641
+
9642
+ def opener(name, pw_):
9643
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
9644
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
9645
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
9646
+ return o
9647
+
9648
+ def call(o, method, path, body=None):
9649
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
9650
+ if method == "POST":
9651
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
9652
+ try:
9653
+ with o.open(rq, timeout=120) as rr_:
9654
+ return rr_.status, json.loads(rr_.read())
9655
+ except urllib.error.HTTPError as e:
9656
+ return e.code, json.loads(e.read())
9657
+ try:
9658
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
9659
+ s_t, tj = call(V, "GET", "/api/declare/template")
9660
+ s_p, pj = call(V, "GET", "/api/penetrator")
9661
+ s_vd, _ = call(V, "POST", "/api/declare", {"text": tj.get("csv", "")})
9662
+ s_od, oj = call(O, "POST", "/api/declare", {"text": tj.get("csv", "")})
9663
+ s_sv, sv = call(O, "POST", "/api/declare", {"text": sheet})
9664
+ s_e, _ = call(O, "POST", "/api/declare", {"text": ""})
9665
+ s_vi, _ = call(V, "POST", "/api/penetrator/install", {"well_id": w1["id"], "penetrator": "genco-gps-5"})
9666
+ s_oi, _ = call(O, "POST", "/api/penetrator/install", {"well_id": w1["id"], "penetrator": "genco-gps-5", "motor_type": "PMM", "insulation": {"A": 900}})
9667
+ s_bi, _ = call(O, "POST", "/api/penetrator/install", {"well_id": w1["id"], "penetrator": "nope"})
9668
+ s_bd, _ = call(O, "POST", "/api/penetrator/drive", {"well_id": w1["id"], "max_volts": 4000})
9669
+ s_od2, _ = call(O, "POST", "/api/penetrator/drive", {"well_id": w1["id"], "max_volts": 4000, "source": "ds"})
9670
+ s_rm, _ = call(O, "POST", "/api/penetrator/remove", {"well_id": w2["id"], "what": "drive"})
9671
+ s_rm2, _ = call(O, "POST", "/api/penetrator/remove", {"well_id": w2["id"], "what": "drive"})
9672
+ s_ac, _ = call(O, "POST", "/api/penetrator/nope", {})
9673
+ finally:
9674
+ svc.stop()
9675
+ wells_p = {x["well_id"]: x for x in pj.get("wells", [])}
9676
+ sheet_path = d / "pad.csv"
9677
+ sheet_path.write_text(sheet.replace("ESP-2", "ESP-1").replace("Run ", "Lateral ").replace("acme-p-9-sn1", "genco-gps-5"), encoding="utf-8")
9678
+ cli_t = _sp.run([sys.executable, "-m", "gea", "declare", "--workspace", wsp, "--template", str(d / "t.csv")], capture_output=True, text=True, cwd=str(pkg.parent))
9679
+ cli_p = _sp.run([sys.executable, "-m", "gea", "declare", "--workspace", wsp, "--file", str(sheet_path)], capture_output=True, text=True, cwd=str(pkg.parent))
9680
+ cli_e = _sp.run([sys.executable, "-m", "gea", "declare", "--workspace", wsp, "--file", str(d / "survey.csv")], capture_output=True, text=True, cwd=str(pkg.parent))
9681
+ cli_n = _sp.run([sys.executable, "-m", "gea", "penetrator"], capture_output=True, text=True, cwd=str(pkg.parent))
9682
+ cli_w = _sp.run([sys.executable, "-m", "gea", "penetrator", "--workspace", wsp, "--well", w1["id"]], capture_output=True, text=True, cwd=str(pkg.parent))
9683
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
9684
+ c3.update({
9685
+ "api": s_t == 200 and "[penetrators]" in tj["csv"] and s_p == 200 and len(pj["penetrators"]) == 3 and wells_p[w2["id"]]["install"]["penetrator"] == "acme-p-9-sn1"
9686
+ and s_vd == 403 and s_od == 200 and oj["counts"]["NEW"] == 0 and not oj["errors"] and s_sv == 200 and any("carries no files" in e for e in sv["errors"])
9687
+ and s_e == 400 and s_vi == 403 and s_oi == 200 and s_bi == 404 and s_bd == 400 and s_od2 == 200 and s_rm == 200 and s_rm2 == 404 and s_ac == 404,
9688
+ "cli": cli_t.returncode == 0 and (d / "t.csv").is_file() and cli_p.returncode == 0 and "nothing written yet" in cli_p.stdout and "NEW" in cli_p.stdout
9689
+ and cli_e.returncode == 1 and "this is not a declarations sheet" in cli_e.stdout
9690
+ and cli_n.returncode == 0 and "genco-gps-5" in cli_n.stdout and "Permanent magnet motors" in cli_n.stdout
9691
+ and cli_w.returncode == 0 and "ESP-1:" in cli_w.stdout and "drive output voltage" in cli_w.stdout,
9692
+ "page": all(x in page_src for x in ("VIEWS.completion", "['#/completion', 'Completion']", "id=\"dcCard\"", "/api/declare/template", "Check the sheet",
9693
+ "/api/penetrator/install", "Permanent magnet motors", "Record the installation", "Declare the drive and site")),
9694
+ "help": all(any(t[0] == k for t in H.INDEX) and not H.check(k) for k in ("declare", "penetrator")) and H.VIEW_TOPIC.get("completion") == "penetrator",
9695
+ })
9696
+ failed3 = [k for k, v in c3.items() if not v]
9697
+ ok(not failed3,
9698
+ "CJ3 the two motors missing from the catalogue (7 in 8/9 4.3 and 8 in x 7 3/4 in 7/8 5.9) added as their sheets print them and the "
9699
+ "rest read a third time; the API (anyone signed in downloads the sheet and reads the Completion tab, only an operator checks or writes "
9700
+ "one; a sheet through the browser names its survey as carrying no file; an unknown penetrator, a drive with no source, a second removal "
9701
+ "and an unknown action named), the CLI (template, check, a file that is not a sheet, the penetrators, a well's check), the Setup card, "
9702
+ "the Completion tab and the two help pages"
9703
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
9704
+
9705
+
9450
9706
  def main() -> int:
9451
9707
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
9452
9708
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -9537,6 +9793,7 @@ def main() -> int:
9537
9793
  section_cg_well_report(tmp)
9538
9794
  section_ch_simulation(tmp)
9539
9795
  section_ci_drill_string(tmp)
9796
+ section_cj_declarations(tmp)
9540
9797
  if _FAILS:
9541
9798
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
9542
9799
  for f in _FAILS: