gea-program 0.36.0__tar.gz → 0.37.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 (305) hide show
  1. {gea_program-0.36.0/gea_program.egg-info → gea_program-0.37.0}/PKG-INFO +12 -1
  2. {gea_program-0.36.0 → gea_program-0.37.0}/README.md +11 -0
  3. {gea_program-0.36.0 → gea_program-0.37.0}/gea/__init__.py +1 -1
  4. {gea_program-0.36.0 → gea_program-0.37.0}/gea/__main__.py +55 -0
  5. {gea_program-0.36.0 → gea_program-0.37.0}/gea/acceptance_tests.py +171 -0
  6. gea_program-0.37.0/gea/economics.py +454 -0
  7. gea_program-0.37.0/gea/help/economics.md +11 -0
  8. {gea_program-0.36.0 → gea_program-0.37.0}/gea/helplib.py +2 -1
  9. gea_program-0.37.0/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  10. gea_program-0.37.0/gea/reference/eia_prices.provenance.json +51 -0
  11. gea_program-0.37.0/gea/reference/eia_wti_daily.csv.gz +0 -0
  12. {gea_program-0.36.0 → gea_program-0.37.0}/gea/service.py +29 -0
  13. {gea_program-0.36.0 → gea_program-0.37.0}/gea/web/app.html +70 -1
  14. {gea_program-0.36.0 → gea_program-0.37.0/gea_program.egg-info}/PKG-INFO +12 -1
  15. {gea_program-0.36.0 → gea_program-0.37.0}/gea_program.egg-info/SOURCES.txt +5 -0
  16. {gea_program-0.36.0 → gea_program-0.37.0}/pyproject.toml +1 -1
  17. {gea_program-0.36.0 → gea_program-0.37.0}/LICENSE +0 -0
  18. {gea_program-0.36.0 → gea_program-0.37.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  19. {gea_program-0.36.0 → gea_program-0.37.0}/gea/accuracy_statement.py +0 -0
  20. {gea_program-0.36.0 → gea_program-0.37.0}/gea/alarm_engine.py +0 -0
  21. {gea_program-0.36.0 → gea_program-0.37.0}/gea/backup.py +0 -0
  22. {gea_program-0.36.0 → gea_program-0.37.0}/gea/bench.py +0 -0
  23. {gea_program-0.36.0 → gea_program-0.37.0}/gea/blind_harness.py +0 -0
  24. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  25. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  26. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  27. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  28. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  29. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  30. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  31. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  32. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  33. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  34. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  35. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  36. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  37. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  38. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  39. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  40. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  41. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  42. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  43. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  44. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  45. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  46. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  47. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  48. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  49. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  50. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  51. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  52. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  53. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  54. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  55. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  56. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  57. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  58. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  59. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  60. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  61. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  62. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  63. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  64. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  65. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  66. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  67. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  68. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  69. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  70. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  71. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  72. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  73. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  74. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  75. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  76. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  77. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  78. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  79. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  80. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  81. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  82. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  83. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  84. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  85. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  86. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  87. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  88. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  89. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  90. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  91. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/med_160_sapropels.txt +0 -0
  92. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  93. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  94. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  95. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  96. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  97. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  98. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  99. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  100. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  101. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  102. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  103. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  104. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  105. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  106. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  107. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  108. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  109. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  110. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  111. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  112. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  113. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  114. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  115. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  116. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  117. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  118. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  119. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  120. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  121. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  122. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  123. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  124. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  125. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  126. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  127. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  128. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog_map.py +0 -0
  129. {gea_program-0.36.0 → gea_program-0.37.0}/gea/catalog_register.py +0 -0
  130. {gea_program-0.36.0 → gea_program-0.37.0}/gea/certificates.py +0 -0
  131. {gea_program-0.36.0 → gea_program-0.37.0}/gea/cli.py +0 -0
  132. {gea_program-0.36.0 → gea_program-0.37.0}/gea/client_reports.py +0 -0
  133. {gea_program-0.36.0 → gea_program-0.37.0}/gea/config_versioning.py +0 -0
  134. {gea_program-0.36.0 → gea_program-0.37.0}/gea/conformance.py +0 -0
  135. {gea_program-0.36.0 → gea_program-0.37.0}/gea/control_room.py +0 -0
  136. {gea_program-0.36.0 → gea_program-0.37.0}/gea/correlation.py +0 -0
  137. {gea_program-0.36.0 → gea_program-0.37.0}/gea/dashboard.py +0 -0
  138. {gea_program-0.36.0 → gea_program-0.37.0}/gea/deviation.py +0 -0
  139. {gea_program-0.36.0 → gea_program-0.37.0}/gea/dlis.py +0 -0
  140. {gea_program-0.36.0 → gea_program-0.37.0}/gea/dnp3.py +0 -0
  141. {gea_program-0.36.0 → gea_program-0.37.0}/gea/doctor.py +0 -0
  142. {gea_program-0.36.0 → gea_program-0.37.0}/gea/downhole_engine.py +0 -0
  143. {gea_program-0.36.0 → gea_program-0.37.0}/gea/drift_monitor.py +0 -0
  144. {gea_program-0.36.0 → gea_program-0.37.0}/gea/earth_model.py +0 -0
  145. {gea_program-0.36.0 → gea_program-0.37.0}/gea/edr.py +0 -0
  146. {gea_program-0.36.0 → gea_program-0.37.0}/gea/elevation.py +0 -0
  147. {gea_program-0.36.0 → gea_program-0.37.0}/gea/etp.py +0 -0
  148. {gea_program-0.36.0 → gea_program-0.37.0}/gea/etp12_schemas.json +0 -0
  149. {gea_program-0.36.0 → gea_program-0.37.0}/gea/example_register_map.json +0 -0
  150. {gea_program-0.36.0 → gea_program-0.37.0}/gea/fat_sat.py +0 -0
  151. {gea_program-0.36.0 → gea_program-0.37.0}/gea/files.py +0 -0
  152. {gea_program-0.36.0 → gea_program-0.37.0}/gea/fixtures/README.md +0 -0
  153. {gea_program-0.36.0 → gea_program-0.37.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  154. {gea_program-0.36.0 → gea_program-0.37.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  155. {gea_program-0.36.0 → gea_program-0.37.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  156. {gea_program-0.36.0 → gea_program-0.37.0}/gea/follower.py +0 -0
  157. {gea_program-0.36.0 → gea_program-0.37.0}/gea/forward_model.py +0 -0
  158. {gea_program-0.36.0 → gea_program-0.37.0}/gea/friction_priors.py +0 -0
  159. {gea_program-0.36.0 → gea_program-0.37.0}/gea/gamma.py +0 -0
  160. {gea_program-0.36.0 → gea_program-0.37.0}/gea/gauge_aging.py +0 -0
  161. {gea_program-0.36.0 → gea_program-0.37.0}/gea/gauge_specs.py +0 -0
  162. {gea_program-0.36.0 → gea_program-0.37.0}/gea/geodesy.py +0 -0
  163. {gea_program-0.36.0 → gea_program-0.37.0}/gea/gravity_reference.py +0 -0
  164. {gea_program-0.36.0 → gea_program-0.37.0}/gea/hartip.py +0 -0
  165. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/TESTER_GUIDE.md +0 -0
  166. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/alarms.md +0 -0
  167. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/audit-update.md +0 -0
  168. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/backup.md +0 -0
  169. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/catalog.md +0 -0
  170. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/charts.md +0 -0
  171. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/conformance.md +0 -0
  172. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/control-room.md +0 -0
  173. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/data-in.md +0 -0
  174. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/dlis.md +0 -0
  175. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/doctor.md +0 -0
  176. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/drift.md +0 -0
  177. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/edr.md +0 -0
  178. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/etp.md +0 -0
  179. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/field-ports.md +0 -0
  180. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/files.md +0 -0
  181. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/historian.md +0 -0
  182. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/instruments.md +0 -0
  183. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/interval.md +0 -0
  184. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/map.md +0 -0
  185. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/month-end.md +0 -0
  186. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/notifications.md +0 -0
  187. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/osdu.md +0 -0
  188. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/patches.md +0 -0
  189. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/ppdm.md +0 -0
  190. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/quakeml.md +0 -0
  191. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/quality.md +0 -0
  192. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/seedlink.md +0 -0
  193. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/seismic.md +0 -0
  194. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/setup.md +0 -0
  195. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/site.md +0 -0
  196. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/sites.md +0 -0
  197. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/slip.md +0 -0
  198. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/soak.md +0 -0
  199. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/sra.md +0 -0
  200. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/start.md +0 -0
  201. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/structure.md +0 -0
  202. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/tools.md +0 -0
  203. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/transients.md +0 -0
  204. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/upkeep.md +0 -0
  205. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/vibration.md +0 -0
  206. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/well-tests.md +0 -0
  207. {gea_program-0.36.0 → gea_program-0.37.0}/gea/help/well3d.md +0 -0
  208. {gea_program-0.36.0 → gea_program-0.37.0}/gea/housekeeping.py +0 -0
  209. {gea_program-0.36.0 → gea_program-0.37.0}/gea/interval.py +0 -0
  210. {gea_program-0.36.0 → gea_program-0.37.0}/gea/inverse_engine.py +0 -0
  211. {gea_program-0.36.0 → gea_program-0.37.0}/gea/jobs.py +0 -0
  212. {gea_program-0.36.0 → gea_program-0.37.0}/gea/live_ports.py +0 -0
  213. {gea_program-0.36.0 → gea_program-0.37.0}/gea/loadtest.py +0 -0
  214. {gea_program-0.36.0 → gea_program-0.37.0}/gea/matplotlib_demo.py +0 -0
  215. {gea_program-0.36.0 → gea_program-0.37.0}/gea/modbus.py +0 -0
  216. {gea_program-0.36.0 → gea_program-0.37.0}/gea/modbus_rtu.py +0 -0
  217. {gea_program-0.36.0 → gea_program-0.37.0}/gea/model_card.py +0 -0
  218. {gea_program-0.36.0 → gea_program-0.37.0}/gea/month_end.py +0 -0
  219. {gea_program-0.36.0 → gea_program-0.37.0}/gea/mqtt_port.py +0 -0
  220. {gea_program-0.36.0 → gea_program-0.37.0}/gea/notify.py +0 -0
  221. {gea_program-0.36.0 → gea_program-0.37.0}/gea/opcua_port.py +0 -0
  222. {gea_program-0.36.0 → gea_program-0.37.0}/gea/operator_app.py +0 -0
  223. {gea_program-0.36.0 → gea_program-0.37.0}/gea/osdu.py +0 -0
  224. {gea_program-0.36.0 → gea_program-0.37.0}/gea/patches.py +0 -0
  225. {gea_program-0.36.0 → gea_program-0.37.0}/gea/permits.py +0 -0
  226. {gea_program-0.36.0 → gea_program-0.37.0}/gea/piwebapi.py +0 -0
  227. {gea_program-0.36.0 → gea_program-0.37.0}/gea/ports.py +0 -0
  228. {gea_program-0.36.0 → gea_program-0.37.0}/gea/ppdm.py +0 -0
  229. {gea_program-0.36.0 → gea_program-0.37.0}/gea/profile_catalog.py +0 -0
  230. {gea_program-0.36.0 → gea_program-0.37.0}/gea/project.py +0 -0
  231. {gea_program-0.36.0 → gea_program-0.37.0}/gea/qt6_downhole_app.py +0 -0
  232. {gea_program-0.36.0 → gea_program-0.37.0}/gea/quakeml.py +0 -0
  233. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reconciler.py +0 -0
  234. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  235. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  236. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  237. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  238. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  239. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  240. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/tool_sources.json +0 -0
  241. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/us_outline.json +0 -0
  242. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/us_outline.provenance.json +0 -0
  243. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_3d.provenance.json +0 -0
  244. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_surfaces.json.gz +0 -0
  245. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  246. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  247. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  248. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  249. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  250. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  251. {gea_program-0.36.0 → gea_program-0.37.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  252. {gea_program-0.36.0 → gea_program-0.37.0}/gea/rock_inventory.py +0 -0
  253. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sample_record.py +0 -0
  254. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sample_well_profile.csv +0 -0
  255. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sbom.py +0 -0
  256. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seedlink.py +0 -0
  257. {gea_program-0.36.0 → gea_program-0.37.0}/gea/segy.py +0 -0
  258. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic.py +0 -0
  259. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_array.py +0 -0
  260. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_assoc.py +0 -0
  261. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_detect.py +0 -0
  262. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_film.py +0 -0
  263. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_harmonic.py +0 -0
  264. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_qc.py +0 -0
  265. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_response.py +0 -0
  266. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_signature.py +0 -0
  267. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_track.py +0 -0
  268. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_uncertainty.py +0 -0
  269. {gea_program-0.36.0 → gea_program-0.37.0}/gea/seismic_unlisted.py +0 -0
  270. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sensor_swap.py +0 -0
  271. {gea_program-0.36.0 → gea_program-0.37.0}/gea/series_cache.py +0 -0
  272. {gea_program-0.36.0 → gea_program-0.37.0}/gea/service_life.py +0 -0
  273. {gea_program-0.36.0 → gea_program-0.37.0}/gea/setup_checklist.py +0 -0
  274. {gea_program-0.36.0 → gea_program-0.37.0}/gea/shapefile.py +0 -0
  275. {gea_program-0.36.0 → gea_program-0.37.0}/gea/shell.py +0 -0
  276. {gea_program-0.36.0 → gea_program-0.37.0}/gea/shut_in.py +0 -0
  277. {gea_program-0.36.0 → gea_program-0.37.0}/gea/site_map.py +0 -0
  278. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sla_report.py +0 -0
  279. {gea_program-0.36.0 → gea_program-0.37.0}/gea/slip.py +0 -0
  280. {gea_program-0.36.0 → gea_program-0.37.0}/gea/soak.py +0 -0
  281. {gea_program-0.36.0 → gea_program-0.37.0}/gea/sra.py +0 -0
  282. {gea_program-0.36.0 → gea_program-0.37.0}/gea/store_forward.py +0 -0
  283. {gea_program-0.36.0 → gea_program-0.37.0}/gea/strata_join.py +0 -0
  284. {gea_program-0.36.0 → gea_program-0.37.0}/gea/structure.py +0 -0
  285. {gea_program-0.36.0 → gea_program-0.37.0}/gea/supervisor.py +0 -0
  286. {gea_program-0.36.0 → gea_program-0.37.0}/gea/survey_cmd.py +0 -0
  287. {gea_program-0.36.0 → gea_program-0.37.0}/gea/survey_view.py +0 -0
  288. {gea_program-0.36.0 → gea_program-0.37.0}/gea/telemetry.py +0 -0
  289. {gea_program-0.36.0 → gea_program-0.37.0}/gea/tool_catalog.py +0 -0
  290. {gea_program-0.36.0 → gea_program-0.37.0}/gea/tool_library.py +0 -0
  291. {gea_program-0.36.0 → gea_program-0.37.0}/gea/tool_taxonomy.py +0 -0
  292. {gea_program-0.36.0 → gea_program-0.37.0}/gea/transient.py +0 -0
  293. {gea_program-0.36.0 → gea_program-0.37.0}/gea/vibration.py +0 -0
  294. {gea_program-0.36.0 → gea_program-0.37.0}/gea/well3d.py +0 -0
  295. {gea_program-0.36.0 → gea_program-0.37.0}/gea/well_assembler.py +0 -0
  296. {gea_program-0.36.0 → gea_program-0.37.0}/gea/well_test_validation.py +0 -0
  297. {gea_program-0.36.0 → gea_program-0.37.0}/gea/wits0.py +0 -0
  298. {gea_program-0.36.0 → gea_program-0.37.0}/gea/witsml.py +0 -0
  299. {gea_program-0.36.0 → gea_program-0.37.0}/gea/workspace.py +0 -0
  300. {gea_program-0.36.0 → gea_program-0.37.0}/gea_program.egg-info/dependency_links.txt +0 -0
  301. {gea_program-0.36.0 → gea_program-0.37.0}/gea_program.egg-info/entry_points.txt +0 -0
  302. {gea_program-0.36.0 → gea_program-0.37.0}/gea_program.egg-info/requires.txt +0 -0
  303. {gea_program-0.36.0 → gea_program-0.37.0}/gea_program.egg-info/top_level.txt +0 -0
  304. {gea_program-0.36.0 → gea_program-0.37.0}/setup.cfg +0 -0
  305. {gea_program-0.36.0 → gea_program-0.37.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.36.0
3
+ Version: 0.37.0
4
4
  Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
5
5
  Author: Daniel T. Murphy
6
6
  License: MPL-2.0
@@ -648,6 +648,17 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
648
648
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
649
649
  `rtserve.iris.washington.edu:18000`, network `TX`.
650
650
 
651
+ ### Economics
652
+
653
+ The Economics tab puts each well's days on its drilling record, feet
654
+ drilled and feet per day beside what it cost by the operator's own declared
655
+ day rates, items and AFE - cost per foot, per day and by section, never
656
+ divided while the rates leave time uncovered - and sets each well against
657
+ the U.S. Energy Information Administration's daily WTI and Henry Hub spot
658
+ prices while it was drilled. The prices ship with the program and refresh
659
+ on request; any other series (a rig count) is imported with its source.
660
+ `gea economics`, `gea help economics`.
661
+
651
662
  ### The well in 3D
652
663
 
653
664
  The 3D tab draws each well's path from its directional survey by the
@@ -609,6 +609,17 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
609
609
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
610
610
  `rtserve.iris.washington.edu:18000`, network `TX`.
611
611
 
612
+ ### Economics
613
+
614
+ The Economics tab puts each well's days on its drilling record, feet
615
+ drilled and feet per day beside what it cost by the operator's own declared
616
+ day rates, items and AFE - cost per foot, per day and by section, never
617
+ divided while the rates leave time uncovered - and sets each well against
618
+ the U.S. Energy Information Administration's daily WTI and Henry Hub spot
619
+ prices while it was drilled. The prices ship with the program and refresh
620
+ on request; any other series (a rig count) is imported with its source.
621
+ `gea economics`, `gea help economics`.
622
+
612
623
  ### The well in 3D
613
624
 
614
625
  The 3D tab draws each well's path from its directional survey by the
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.36.0"
126
+ __version__ = "0.37.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -475,6 +475,24 @@ def main(argv=None) -> int:
475
475
  p_ck.add_argument("--note", type=str, default="", help="--confirm: what it was checked against (required)")
476
476
  p_ck.add_argument("--actor", type=str, default="cli")
477
477
 
478
+ p_ec = sub.add_parser("economics", help="each well's time, footage and declared cost against the market while it was drilled; declare rates, items and an AFE; refresh or import price series")
479
+ p_ec.add_argument("--workspace", type=str, required=True)
480
+ p_ec.add_argument("--json", action="store_true")
481
+ p_ec.add_argument("--well", type=str, default=None, help="the well a rate, item or AFE is for")
482
+ p_ec.add_argument("--rate", type=float, default=None, help="a day rate, USD/day, over --from/--to (open when left out)")
483
+ p_ec.add_argument("--item", type=float, default=None, help="a one-off cost, USD (a bit, a cement job), optionally --section N")
484
+ p_ec.add_argument("--afe", type=float, default=None, help="the well's AFE, USD")
485
+ p_ec.add_argument("--what", type=str, default="", help="what the rate or item pays for")
486
+ p_ec.add_argument("--section", type=int, default=None)
487
+ p_ec.add_argument("--from", dest="date_from", type=str, default=None, help="rate start, ISO date or date-time on the recorder's clock")
488
+ p_ec.add_argument("--to", dest="date_to", type=str, default=None, help="rate end")
489
+ p_ec.add_argument("--source", type=str, default="", help="where the number came from (contract, invoice, AFE number)")
490
+ p_ec.add_argument("--refresh-prices", action="store_true", help="fetch the latest EIA spot-price copies into the workspace")
491
+ p_ec.add_argument("--import-series", type=str, default=None, help="a CSV of dates and values to keep beside the prices (a rig count)")
492
+ p_ec.add_argument("--name", type=str, default="")
493
+ p_ec.add_argument("--unit", type=str, default="")
494
+ p_ec.add_argument("--actor", type=str, default="cli")
495
+
478
496
  p_tj = sub.add_parser("trajectory", help="attach a well's directional survey (WITSML trajectory or a table of MD, Inc, Azi), kept verbatim beside the well, so the 3D tab can draw its path")
479
497
  p_tj.add_argument("--workspace", type=str, required=True)
480
498
  p_tj.add_argument("--well", type=str, required=True)
@@ -1954,6 +1972,43 @@ def main(argv=None) -> int:
1954
1972
  else:
1955
1973
  print(_SC.text(r))
1956
1974
  return 0 if r["ready"] else 2
1975
+ elif a.cmd == "economics":
1976
+ import json as _json
1977
+ from . import economics as _EC
1978
+ from .cli import _safe_stdout
1979
+ from .workspace import Workspace, WorkspaceError
1980
+ _safe_stdout()
1981
+ try:
1982
+ ws = Workspace(a.workspace)
1983
+ except WorkspaceError as e:
1984
+ raise SystemExit(f"economics: {e}")
1985
+ try:
1986
+ if a.rate is not None:
1987
+ r = _EC.set_rate(ws, a.well or "", a.actor, a.rate, a.what, a.source, a.date_from, a.date_to)
1988
+ print(f"{a.well}: {r['usd_per_day']:,.2f} USD/day {r['what']} from {r['from'] or 'open'} to {r['to'] or 'open'}")
1989
+ return 0
1990
+ if a.item is not None:
1991
+ r = _EC.add_item(ws, a.well or "", a.actor, a.item, a.what, a.source, a.section)
1992
+ print(f"{a.well}: {r['usd']:,.2f} USD {r['what']}")
1993
+ return 0
1994
+ if a.afe is not None:
1995
+ r = _EC.set_afe(ws, a.well or "", a.actor, a.afe, a.source)
1996
+ print(f"{a.well}: AFE {r['usd']:,.2f} USD ({r['source']})")
1997
+ return 0
1998
+ if a.refresh_prices:
1999
+ for r in _EC.refresh_prices(ws, a.actor):
2000
+ print(f"{r['id']}: {r['status']}" + (f" to {r['as_of']} ({r['rows']} days)" if r.get('as_of') else f" - {r.get('why', '')}"))
2001
+ return 0
2002
+ if a.import_series:
2003
+ with open(a.import_series, "rb") as f:
2004
+ r = _EC.import_series(ws, a.actor, a.name, a.unit, a.source, f.read())
2005
+ print(f"{r['name']}: {r['rows']} values, {r['first']} to {r['as_of']} ({r['unit']})")
2006
+ return 0
2007
+ except (ValueError, KeyError, OSError, WorkspaceError) as e:
2008
+ raise SystemExit(f"economics: {e.args[0] if e.args else e}")
2009
+ e = _EC.economics(ws)
2010
+ print(_json.dumps(e, indent=1) if a.json else _EC.text(e))
2011
+ return 0
1957
2012
  elif a.cmd in ("trajectory", "well3d"):
1958
2013
  import json as _json
1959
2014
  from . import well3d as _W3
@@ -8800,6 +8800,176 @@ def section_ce_well3d(tmp: str) -> None:
8800
8800
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
8801
8801
 
8802
8802
 
8803
+
8804
+ def section_cf_economics(tmp: str) -> None:
8805
+ """Section CF - economics: the shipped EIA spot prices checked against their provenance and against two days EIA
8806
+ published, series refreshed and imported only when they read, a well's cost from declared rates done by hand and
8807
+ left undivided when the rates leave time uncovered, the market while the well was drilled, the API, the CLI, the tab."""
8808
+ import base64 as _b64
8809
+ import gzip as _gz
8810
+ import hashlib as _hl
8811
+ import http.cookiejar
8812
+ import subprocess as _sp
8813
+ import urllib.request
8814
+ import urllib.error
8815
+ from datetime import datetime as _dt
8816
+ from . import economics as EC
8817
+ from . import edr as E
8818
+ from . import helplib as H
8819
+ from .workspace import Workspace
8820
+ from .service import Service, Users
8821
+ pkg = Path(__file__).parent
8822
+ d = Path(tmp, "cf"); d.mkdir()
8823
+
8824
+ prov = json.loads((pkg / "reference" / "eia_prices.provenance.json").read_text(encoding="utf-8"))
8825
+ hashes = all(_hl.sha256((pkg / "reference" / s["file"]).read_bytes()).hexdigest() == s["sha256"]
8826
+ and _hl.sha256(_gz.decompress((pkg / "reference" / s["file"]).read_bytes())).hexdigest() == s["sha256_uncompressed"]
8827
+ and len(s["source"]["commit"]) == 40 for s in prov["series"].values())
8828
+ wd, wv, _ = EC.read_series(None, "wti")
8829
+ hd, hv, _ = EC.read_series(None, "henry_hub")
8830
+ refused = 0
8831
+ for bad in ("Date,Price\n2020-01-01,1\n2020-01-01,2\n", "Date,Price\n2020-01-01,1\n2020-01-02,x\n", "Date,Price\n2020-13-01,1\n2020-01-02,2\n", "Date,Price\n"):
8832
+ try:
8833
+ EC._parse_series(bad)
8834
+ except ValueError:
8835
+ refused += 1
8836
+ bd, bv, _ = EC._parse_series("Date,Price\n2020-01-03,3\n2020-01-01,1\n2020-01-02,\n")
8837
+ ev = E.selftest_events(str(d))
8838
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
8839
+ wb = ws.add_well_file(ev["path"], display="Well B", actor="tester"); wc = ws.add_well_file(ev["path"], display="Well C", actor="tester")
8840
+ later = ("Date,Price\n" + "".join(f"{x},{y}\n" for x, y in zip(wd, wv)) + "2099-01-01,50.0\n").encode()
8841
+ hh_short = "Date,Price\n2001-01-02,9.0\n2001-01-03,9.5\n".encode()
8842
+ calls = {}
8843
+
8844
+ def fake(u):
8845
+ calls[u] = True
8846
+ if "oil-prices" in u:
8847
+ return later
8848
+ if "natural-gas" in u:
8849
+ return hh_short
8850
+ raise OSError("no network")
8851
+ ref = EC.refresh_prices(ws, "tester", fetch=fake)
8852
+ ref_status = {r["id"]: r["status"] for r in ref}
8853
+ imp_refused = 0
8854
+ for kw in ({"name": "", "unit": "rigs", "source": "s"}, {"name": "Rigs", "unit": "", "source": "s"}, {"name": "Rigs", "unit": "rigs", "source": ""}, {"name": "wti", "unit": "x", "source": "s"}):
8855
+ try:
8856
+ EC.import_series(ws, "tester", kw["name"], kw["unit"], kw["source"], b"Date,Count\n2026-06-26,300\n2026-07-03,301\n")
8857
+ except ValueError:
8858
+ imp_refused += 1
8859
+ imp = EC.import_series(ws, "tester", "Permian rig count", "rigs", "test", b"Week,Rigs\n2026-06-26,300\n2026-07-03,301\n2026-07-10,299\n")
8860
+ c1 = {
8861
+ "shipped": hashes and prov["series"]["wti"]["eia_series"] == "RWTC" and prov["series"]["henry_hub"]["eia_series"] == "RNGWHHD"
8862
+ and "public domain" in prov["license"].lower() and wv[wd.index("2020-04-20")] == -36.98 and hv[hd.index("2021-02-17")] == 23.86
8863
+ and len(wd) == prov["series"]["wti"]["rows"] and wd == sorted(wd),
8864
+ "parse": refused == 4 and bd == ["2020-01-01", "2020-01-03"] and bv == [1.0, 3.0],
8865
+ "refresh": ref_status == {"wti": "REFRESHED", "henry_hub": "NOT REFRESHED"} and EC.read_series(ws, "wti")[0][-1] == "2099-01-01"
8866
+ and EC.read_series(ws, "henry_hub")[2]["kind"] == "shipped" and len(calls) == 2,
8867
+ "import": imp_refused == 4 and imp["id"] == "permian_rig_count" and imp["rows"] == 3 and imp["date_column"] == "Week" and imp["value_column"] == "Rigs"
8868
+ and Path(wsp, "economics", "series", "permian_rig_count.csv").read_bytes().startswith(b"Week,Rigs"),
8869
+ }
8870
+ failed1 = [k for k, v in c1.items() if not v]
8871
+ ok(not failed1,
8872
+ "CF1 the price series: EIA's daily WTI (RWTC) and Henry Hub (RNGWHHD) spot prices shipped with their provenance and hashes, carrying the "
8873
+ "-36.98 of 20 April 2020 and the 23.86 of 17 February 2021 as EIA published them; a series with a repeated or bad date or a bad number "
8874
+ "refused, a blank day left out; a refreshed copy kept only when it reads and runs later than the one held; an imported series kept byte "
8875
+ "for byte with its name, unit and source"
8876
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
8877
+
8878
+ ws.refresh_dashboard(actor="tester")
8879
+ rec = json.loads(Path(ws.reports_dir, "wells", wb["id"], "drilling_record.json").read_text(encoding="utf-8"))
8880
+ t0, t1 = _dt.fromisoformat(rec["start"]), _dt.fromisoformat(rec["end"])
8881
+ days = (t1 - t0).total_seconds() / 86400.0
8882
+ mid = "2026-07-02T12:00:00"
8883
+ EC.set_rate(ws, wb["id"], "tester", 30000, "rig spread", "contract", rec["start"], mid)
8884
+ part = EC.economics(ws)
8885
+ pb = next(w for w in part["wells"] if w["well_id"] == wb["id"])
8886
+ bad = 0
8887
+ for args in ((30000, "rig", "c", "2026-07-02", "2026-07-05"), (30000, "", "c", None, None), (30000, "rig", "", None, None), (-5, "rig", "c", "2027-01-01", None),
8888
+ (30000, "rig", "c", "2027-01-02", "2027-01-01")):
8889
+ try:
8890
+ EC.set_rate(ws, wb["id"], "tester", *args)
8891
+ except ValueError:
8892
+ bad += 1
8893
+ EC.set_rate(ws, wb["id"], "tester", 32000, "rig spread", "contract", mid, None)
8894
+ EC.add_item(ws, wb["id"], "tester", 18000, "bit", "invoice", 1)
8895
+ EC.set_afe(ws, wb["id"], "tester", 150000, "AFE 1")
8896
+ full = EC.economics(ws)
8897
+ fb = next(w for w in full["wells"] if w["well_id"] == wb["id"])
8898
+ first = (_dt.fromisoformat(mid) - t0).total_seconds() / 86400.0
8899
+ by_hand = round(30000 * first + 32000 * (days - first) + 18000, 2)
8900
+ win = [v for x, v in zip(wd, wv) if rec["start"][:10] <= x <= rec["end"][:10]]
8901
+ c2 = {
8902
+ "partial": pb["cost"]["complete"] is False and pb["cost"]["per_ft"] is None and "cover" in pb["cost"]["why_incomplete"]
8903
+ and abs(pb["cost"]["usd"] - round(30000 * first, 2)) < 0.01,
8904
+ "refused": bad == 5,
8905
+ "by_hand": fb["cost"]["complete"] and abs(fb["cost"]["usd"] - by_hand) < 0.01 and abs(fb["cost"]["per_ft"] - round(by_hand / rec["feet_drilled"], 2)) < 0.01
8906
+ and abs(fb["cost"]["vs_afe_pct"] - round(100 * (by_hand - 150000) / 150000, 1)) < 0.05 and fb["days"] == round(days, 2),
8907
+ "sections": all(s["complete"] for s in fb["sections"]) and fb["sections"][0]["cost_usd"] > 18000,
8908
+ "market": fb["market"]["wti"]["n"] == len(win) and abs(fb["market"]["wti"]["mean"] - round(sum(win) / len(win), 3)) < 1e-9
8909
+ and full["chart"]["wells"][0]["from"] == rec["start"][:10] and "permian_rig_count" in full["chart"]["series"],
8910
+ "correlation": "spearman_cost_per_ft_vs_wti" not in full["correlation"] and full["correlation"]["n_wells"] == 1
8911
+ and abs(EC._spearman([1, 2, 3, 4], [10, 20, 30, 40]) - 1) < 1e-12 and abs(EC._spearman([1, 2, 3, 4], [4, 3, 2, 1]) + 1) < 1e-12,
8912
+ "audit": all(a in [x["action"] for x in ws.audit_log(500)] for a in ("economics.rate", "economics.item", "economics.afe", "economics.prices.refresh", "economics.series.import")),
8913
+ }
8914
+ failed2 = [k for k, v in c2.items() if not v]
8915
+ ok(not failed2,
8916
+ "CF2 a well's cost: two day rates and a bit over the drilling record's own time come to the dollar the hand sum gives, with cost per foot "
8917
+ "and the difference from the AFE; while the rates leave time uncovered the cost is shown, marked incomplete and not divided by the footage; "
8918
+ "overlapping periods, a rate with no source or no purpose, a negative rate and a period that ends before it starts refused; the WTI mean "
8919
+ "while drilling equal to the days EIA published in that window; no correlation across fewer than ten wells; every declaration audited"
8920
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
8921
+
8922
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
8923
+ svc = Service(wsp, port=0, scheduler=False).start()
8924
+ base_url = svc.url.rstrip("/")
8925
+
8926
+ def opener(name, pw):
8927
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
8928
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw}).encode())
8929
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
8930
+ return o
8931
+
8932
+ def call(o, method, path, body=None):
8933
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
8934
+ if method == "POST":
8935
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
8936
+ try:
8937
+ with o.open(rq, timeout=120) as rr_:
8938
+ return rr_.status, json.loads(rr_.read())
8939
+ except urllib.error.HTTPError as e:
8940
+ return e.code, json.loads(e.read())
8941
+ try:
8942
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
8943
+ st_g, eg = call(V, "GET", "/api/economics")
8944
+ st_vr, _ = call(V, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": 1000, "what": "rig", "source": "s"})
8945
+ st_or, _ = call(O, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": 25000, "what": "rig spread", "source": "contract"})
8946
+ st_bad, _ = call(O, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": "x", "what": "rig", "source": "s"})
8947
+ st_imp, imp2 = call(O, "POST", "/api/economics/import", {"name": "Midland WTI", "unit": "USD/bbl", "source": "test", "content_b64": _b64.b64encode(b"Date,Price\n2026-07-01,70\n2026-07-02,71\n").decode()})
8948
+ st_rm, _ = call(O, "POST", "/api/economics/remove", {"well_id": wc["id"], "kind": "rate", "index": 0})
8949
+ st_rm2, _ = call(O, "POST", "/api/economics/remove", {"well_id": wc["id"], "kind": "rate", "index": 0})
8950
+ st_ac, _ = call(O, "POST", "/api/economics/nope", {})
8951
+ finally:
8952
+ svc.stop()
8953
+ cli = _sp.run([sys.executable, "-m", "gea", "economics", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
8954
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
8955
+ help_md = (pkg / "help" / "economics.md").read_text(encoding="utf-8")
8956
+ c3 = {
8957
+ "api": st_g == 200 and len(eg["wells"]) == 2 and st_vr == 403 and st_or == 200 and st_bad == 400 and st_imp == 200 and imp2["id"] == "midland_wti"
8958
+ and st_rm == 200 and st_rm2 == 404 and st_ac == 404,
8959
+ "cli": cli.returncode == 0 and "USD/ft" in cli.stdout and "incomplete" not in cli.stdout.split("Well B")[1].split("Well C")[0] and "U.S. Energy Information Administration" in cli.stdout,
8960
+ "page": all(x in page_src for x in ("VIEWS.economics", "['#/economics', 'Economics']", "ecChart", "ec-hit", "ArrowLeft", "/api/economics/rate",
8961
+ "/api/economics/import", "/api/economics/refresh", "incomplete", "a gap of more than six days is not bridged")),
8962
+ "help": any(t["topic"] == "economics" for t in H.topics()) and not H.check("economics") and H.VIEW_TOPIC.get("economics") == "economics"
8963
+ and "fewer than ten wells" in help_md.split("will not call a measurement")[1],
8964
+ }
8965
+ failed3 = [k for k, v in c3.items() if not v]
8966
+ ok(not failed3,
8967
+ "CF3 the API (a viewer reads, only an operator declares; a bad number, an unknown action and a second removal of the same rate named), "
8968
+ "the CLI and the Economics tab: one chart per series on its own axis with the wells' drilling windows shaded and a crosshair by pointer or "
8969
+ "arrows, the wells' cost table, the cost-basis forms, refresh and import; the help page"
8970
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
8971
+
8972
+
8803
8973
  def main() -> int:
8804
8974
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
8805
8975
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -8886,6 +9056,7 @@ def main() -> int:
8886
9056
  section_cc_site_map(tmp)
8887
9057
  section_cd_control_room(tmp)
8888
9058
  section_ce_well3d(tmp)
9059
+ section_cf_economics(tmp)
8889
9060
  if _FAILS:
8890
9061
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
8891
9062
  for f in _FAILS: