gea-program 0.65.0__tar.gz → 0.66.1__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 (377) hide show
  1. {gea_program-0.65.0/gea_program.egg-info → gea_program-0.66.1}/PKG-INFO +5 -4
  2. {gea_program-0.65.0 → gea_program-0.66.1}/README.md +4 -3
  3. {gea_program-0.65.0 → gea_program-0.66.1}/gea/__init__.py +1 -1
  4. {gea_program-0.65.0 → gea_program-0.66.1}/gea/__main__.py +1 -0
  5. {gea_program-0.65.0 → gea_program-0.66.1}/gea/acceptance_tests.py +170 -0
  6. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/IT_SECURITY.md +5 -3
  7. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/install.md +3 -3
  8. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/site.md +1 -1
  9. {gea_program-0.65.0 → gea_program-0.66.1}/gea/launcher.py +50 -7
  10. {gea_program-0.65.0 → gea_program-0.66.1}/gea/service.py +18 -2
  11. {gea_program-0.65.0 → gea_program-0.66.1}/gea/web/app.html +4 -4
  12. {gea_program-0.65.0 → gea_program-0.66.1/gea_program.egg-info}/PKG-INFO +5 -4
  13. {gea_program-0.65.0 → gea_program-0.66.1}/pyproject.toml +1 -1
  14. {gea_program-0.65.0 → gea_program-0.66.1}/LICENSE +0 -0
  15. {gea_program-0.65.0 → gea_program-0.66.1}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  16. {gea_program-0.65.0 → gea_program-0.66.1}/gea/accuracy_statement.py +0 -0
  17. {gea_program-0.65.0 → gea_program-0.66.1}/gea/alarm_engine.py +0 -0
  18. {gea_program-0.65.0 → gea_program-0.66.1}/gea/alerts.py +0 -0
  19. {gea_program-0.65.0 → gea_program-0.66.1}/gea/backup.py +0 -0
  20. {gea_program-0.65.0 → gea_program-0.66.1}/gea/bench.py +0 -0
  21. {gea_program-0.65.0 → gea_program-0.66.1}/gea/blind_harness.py +0 -0
  22. {gea_program-0.65.0 → gea_program-0.66.1}/gea/casing_design.py +0 -0
  23. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  24. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  25. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  26. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  27. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  28. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/barbados_110_consolidation.txt +0 -0
  29. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  30. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  31. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  32. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  33. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  34. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  35. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  36. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  37. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  38. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  39. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  40. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  41. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  42. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  43. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  44. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  45. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  46. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  47. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  48. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  49. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  50. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  51. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  52. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  53. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  54. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  55. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  56. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  57. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  58. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  59. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  60. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  61. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  62. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  63. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  64. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  65. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  66. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  67. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  68. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  69. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  70. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  71. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  72. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  73. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  74. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  75. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  76. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  77. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  78. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  79. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  80. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  81. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  82. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  83. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/l06_06_nl_survey.csv +0 -0
  84. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  85. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  86. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  87. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  88. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  89. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  90. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/med_160_sapropels.txt +0 -0
  91. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  92. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  93. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  94. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  95. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  96. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  97. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  98. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  99. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  100. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  101. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  102. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  103. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  104. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  105. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  106. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  107. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  108. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  109. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  110. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  111. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  112. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  113. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  114. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  115. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  116. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  117. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  118. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  119. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  120. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  121. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  122. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  123. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  124. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  125. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  126. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  127. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog_map.py +0 -0
  128. {gea_program-0.65.0 → gea_program-0.66.1}/gea/catalog_register.py +0 -0
  129. {gea_program-0.65.0 → gea_program-0.66.1}/gea/certificates.py +0 -0
  130. {gea_program-0.65.0 → gea_program-0.66.1}/gea/cli.py +0 -0
  131. {gea_program-0.65.0 → gea_program-0.66.1}/gea/client_reports.py +0 -0
  132. {gea_program-0.65.0 → gea_program-0.66.1}/gea/collision.py +0 -0
  133. {gea_program-0.65.0 → gea_program-0.66.1}/gea/config_versioning.py +0 -0
  134. {gea_program-0.65.0 → gea_program-0.66.1}/gea/conformance.py +0 -0
  135. {gea_program-0.65.0 → gea_program-0.66.1}/gea/control_room.py +0 -0
  136. {gea_program-0.65.0 → gea_program-0.66.1}/gea/correlation.py +0 -0
  137. {gea_program-0.65.0 → gea_program-0.66.1}/gea/dashboard.py +0 -0
  138. {gea_program-0.65.0 → gea_program-0.66.1}/gea/declarations.py +0 -0
  139. {gea_program-0.65.0 → gea_program-0.66.1}/gea/decline.py +0 -0
  140. {gea_program-0.65.0 → gea_program-0.66.1}/gea/deviation.py +0 -0
  141. {gea_program-0.65.0 → gea_program-0.66.1}/gea/dlis.py +0 -0
  142. {gea_program-0.65.0 → gea_program-0.66.1}/gea/dnp3.py +0 -0
  143. {gea_program-0.65.0 → gea_program-0.66.1}/gea/doctor.py +0 -0
  144. {gea_program-0.65.0 → gea_program-0.66.1}/gea/dossier.py +0 -0
  145. {gea_program-0.65.0 → gea_program-0.66.1}/gea/dossier_verify.py +0 -0
  146. {gea_program-0.65.0 → gea_program-0.66.1}/gea/downhole_engine.py +0 -0
  147. {gea_program-0.65.0 → gea_program-0.66.1}/gea/drift_monitor.py +0 -0
  148. {gea_program-0.65.0 → gea_program-0.66.1}/gea/drill_string.py +0 -0
  149. {gea_program-0.65.0 → gea_program-0.66.1}/gea/earth_model.py +0 -0
  150. {gea_program-0.65.0 → gea_program-0.66.1}/gea/economics.py +0 -0
  151. {gea_program-0.65.0 → gea_program-0.66.1}/gea/ed25519.py +0 -0
  152. {gea_program-0.65.0 → gea_program-0.66.1}/gea/edr.py +0 -0
  153. {gea_program-0.65.0 → gea_program-0.66.1}/gea/elevation.py +0 -0
  154. {gea_program-0.65.0 → gea_program-0.66.1}/gea/etp.py +0 -0
  155. {gea_program-0.65.0 → gea_program-0.66.1}/gea/etp12_schemas.json +0 -0
  156. {gea_program-0.65.0 → gea_program-0.66.1}/gea/example_register_map.json +0 -0
  157. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fat_sat.py +0 -0
  158. {gea_program-0.65.0 → gea_program-0.66.1}/gea/files.py +0 -0
  159. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fixtures/README.md +0 -0
  160. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  161. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  162. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  163. {gea_program-0.65.0 → gea_program-0.66.1}/gea/fixtures/ne_110m_coastline.json +0 -0
  164. {gea_program-0.65.0 → gea_program-0.66.1}/gea/follower.py +0 -0
  165. {gea_program-0.65.0 → gea_program-0.66.1}/gea/forward_model.py +0 -0
  166. {gea_program-0.65.0 → gea_program-0.66.1}/gea/frac_hits.py +0 -0
  167. {gea_program-0.65.0 → gea_program-0.66.1}/gea/friction_priors.py +0 -0
  168. {gea_program-0.65.0 → gea_program-0.66.1}/gea/gamma.py +0 -0
  169. {gea_program-0.65.0 → gea_program-0.66.1}/gea/gauge_aging.py +0 -0
  170. {gea_program-0.65.0 → gea_program-0.66.1}/gea/gauge_specs.py +0 -0
  171. {gea_program-0.65.0 → gea_program-0.66.1}/gea/geodesy.py +0 -0
  172. {gea_program-0.65.0 → gea_program-0.66.1}/gea/geomag.py +0 -0
  173. {gea_program-0.65.0 → gea_program-0.66.1}/gea/gravity_reference.py +0 -0
  174. {gea_program-0.65.0 → gea_program-0.66.1}/gea/hartip.py +0 -0
  175. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/TESTER_GUIDE.md +0 -0
  176. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/alarms.md +0 -0
  177. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/alerts.md +0 -0
  178. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/audit-update.md +0 -0
  179. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/backup.md +0 -0
  180. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/casing.md +0 -0
  181. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/catalog.md +0 -0
  182. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/charts.md +0 -0
  183. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/collision.md +0 -0
  184. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/company-sign-in.md +0 -0
  185. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/conformance.md +0 -0
  186. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/control-room.md +0 -0
  187. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/data-in.md +0 -0
  188. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/declare.md +0 -0
  189. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/decline.md +0 -0
  190. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/dlis.md +0 -0
  191. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/doctor.md +0 -0
  192. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/dossier.md +0 -0
  193. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/drift.md +0 -0
  194. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/drillstring.md +0 -0
  195. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/economics.md +0 -0
  196. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/edr.md +0 -0
  197. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/etp.md +0 -0
  198. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/field-ports.md +0 -0
  199. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/files.md +0 -0
  200. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/frac-hits.md +0 -0
  201. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/historian.md +0 -0
  202. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/https.md +0 -0
  203. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/hydraulics.md +0 -0
  204. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/instruments.md +0 -0
  205. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/interval.md +0 -0
  206. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/invoice-check.md +0 -0
  207. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/map.md +0 -0
  208. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/month-end.md +0 -0
  209. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/notifications.md +0 -0
  210. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/opseis.md +0 -0
  211. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/osdu.md +0 -0
  212. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/pad-summary.md +0 -0
  213. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/patches.md +0 -0
  214. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/penetrator.md +0 -0
  215. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/plan.md +0 -0
  216. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/pore-pressure.md +0 -0
  217. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/ppdm.md +0 -0
  218. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/quakeml.md +0 -0
  219. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/quality.md +0 -0
  220. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/report-history.md +0 -0
  221. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/rig-time.md +0 -0
  222. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/seedlink.md +0 -0
  223. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/seismic.md +0 -0
  224. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/setup.md +0 -0
  225. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/simulation.md +0 -0
  226. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/sites.md +0 -0
  227. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/slip.md +0 -0
  228. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/soak.md +0 -0
  229. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/sra.md +0 -0
  230. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/start.md +0 -0
  231. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/structure.md +0 -0
  232. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/tools.md +0 -0
  233. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/torque-drag.md +0 -0
  234. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/transients.md +0 -0
  235. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/upkeep.md +0 -0
  236. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/vibration.md +0 -0
  237. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/well-report.md +0 -0
  238. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/well-tests.md +0 -0
  239. {gea_program-0.65.0 → gea_program-0.66.1}/gea/help/well3d.md +0 -0
  240. {gea_program-0.65.0 → gea_program-0.66.1}/gea/helplib.py +0 -0
  241. {gea_program-0.65.0 → gea_program-0.66.1}/gea/housekeeping.py +0 -0
  242. {gea_program-0.65.0 → gea_program-0.66.1}/gea/hydraulics.py +0 -0
  243. {gea_program-0.65.0 → gea_program-0.66.1}/gea/icon.py +0 -0
  244. {gea_program-0.65.0 → gea_program-0.66.1}/gea/interval.py +0 -0
  245. {gea_program-0.65.0 → gea_program-0.66.1}/gea/inverse_engine.py +0 -0
  246. {gea_program-0.65.0 → gea_program-0.66.1}/gea/invoice_check.py +0 -0
  247. {gea_program-0.65.0 → gea_program-0.66.1}/gea/jobs.py +0 -0
  248. {gea_program-0.65.0 → gea_program-0.66.1}/gea/live_ports.py +0 -0
  249. {gea_program-0.65.0 → gea_program-0.66.1}/gea/loadtest.py +0 -0
  250. {gea_program-0.65.0 → gea_program-0.66.1}/gea/matplotlib_demo.py +0 -0
  251. {gea_program-0.65.0 → gea_program-0.66.1}/gea/modbus.py +0 -0
  252. {gea_program-0.65.0 → gea_program-0.66.1}/gea/modbus_rtu.py +0 -0
  253. {gea_program-0.65.0 → gea_program-0.66.1}/gea/model_card.py +0 -0
  254. {gea_program-0.65.0 → gea_program-0.66.1}/gea/month_end.py +0 -0
  255. {gea_program-0.65.0 → gea_program-0.66.1}/gea/mqtt_port.py +0 -0
  256. {gea_program-0.65.0 → gea_program-0.66.1}/gea/notify.py +0 -0
  257. {gea_program-0.65.0 → gea_program-0.66.1}/gea/oidc.py +0 -0
  258. {gea_program-0.65.0 → gea_program-0.66.1}/gea/oidc_mock.py +0 -0
  259. {gea_program-0.65.0 → gea_program-0.66.1}/gea/op_seismicity.py +0 -0
  260. {gea_program-0.65.0 → gea_program-0.66.1}/gea/opcua_port.py +0 -0
  261. {gea_program-0.65.0 → gea_program-0.66.1}/gea/operator_app.py +0 -0
  262. {gea_program-0.65.0 → gea_program-0.66.1}/gea/osdu.py +0 -0
  263. {gea_program-0.65.0 → gea_program-0.66.1}/gea/pad_summary.py +0 -0
  264. {gea_program-0.65.0 → gea_program-0.66.1}/gea/patches.py +0 -0
  265. {gea_program-0.65.0 → gea_program-0.66.1}/gea/pdf_report.py +0 -0
  266. {gea_program-0.65.0 → gea_program-0.66.1}/gea/penetrator.py +0 -0
  267. {gea_program-0.65.0 → gea_program-0.66.1}/gea/permits.py +0 -0
  268. {gea_program-0.65.0 → gea_program-0.66.1}/gea/piwebapi.py +0 -0
  269. {gea_program-0.65.0 → gea_program-0.66.1}/gea/pore_pressure.py +0 -0
  270. {gea_program-0.65.0 → gea_program-0.66.1}/gea/ports.py +0 -0
  271. {gea_program-0.65.0 → gea_program-0.66.1}/gea/ppdm.py +0 -0
  272. {gea_program-0.65.0 → gea_program-0.66.1}/gea/profile_catalog.py +0 -0
  273. {gea_program-0.65.0 → gea_program-0.66.1}/gea/project.py +0 -0
  274. {gea_program-0.65.0 → gea_program-0.66.1}/gea/qt6_downhole_app.py +0 -0
  275. {gea_program-0.65.0 → gea_program-0.66.1}/gea/quakeml.py +0 -0
  276. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reconciler.py +0 -0
  277. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  278. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  279. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/WMM2025.COF +0 -0
  280. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  281. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/eia_prices.provenance.json +0 -0
  282. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/eia_wti_daily.csv.gz +0 -0
  283. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/iscwsa_rev4_example.json +0 -0
  284. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/iscwsa_rev5_example.json +0 -0
  285. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  286. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  287. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/libmseed_steim.provenance.json +0 -0
  288. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  289. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  290. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/libmseed_steim2_512.mseed +0 -0
  291. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/mahoning_example.json +0 -0
  292. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/mud_motor_specs.json +0 -0
  293. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/owsg_tool_models.json +0 -0
  294. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/penetrator_ratings.json +0 -0
  295. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/seismicity_protocols.json +0 -0
  296. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/tool_sources.json +0 -0
  297. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/us_outline.json +0 -0
  298. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/us_outline.provenance.json +0 -0
  299. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_3d.provenance.json +0 -0
  300. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  301. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_f4_logs.provenance.json +0 -0
  302. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_f4_lwd.las.gz +0 -0
  303. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_surfaces.json.gz +0 -0
  304. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  305. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  306. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  307. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  308. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  309. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  310. {gea_program-0.65.0 → gea_program-0.66.1}/gea/reference/volve_well_picks.csv.gz +0 -0
  311. {gea_program-0.65.0 → gea_program-0.66.1}/gea/report_history.py +0 -0
  312. {gea_program-0.65.0 → gea_program-0.66.1}/gea/rig_state.py +0 -0
  313. {gea_program-0.65.0 → gea_program-0.66.1}/gea/rock_inventory.py +0 -0
  314. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sample_record.py +0 -0
  315. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sample_well_profile.csv +0 -0
  316. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sbom.py +0 -0
  317. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seedlink.py +0 -0
  318. {gea_program-0.65.0 → gea_program-0.66.1}/gea/segy.py +0 -0
  319. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic.py +0 -0
  320. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_array.py +0 -0
  321. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_assoc.py +0 -0
  322. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_detect.py +0 -0
  323. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_film.py +0 -0
  324. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_harmonic.py +0 -0
  325. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_qc.py +0 -0
  326. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_response.py +0 -0
  327. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_signature.py +0 -0
  328. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_track.py +0 -0
  329. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_uncertainty.py +0 -0
  330. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_unlisted.py +0 -0
  331. {gea_program-0.65.0 → gea_program-0.66.1}/gea/seismic_watch.py +0 -0
  332. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sensor_swap.py +0 -0
  333. {gea_program-0.65.0 → gea_program-0.66.1}/gea/series_cache.py +0 -0
  334. {gea_program-0.65.0 → gea_program-0.66.1}/gea/service_life.py +0 -0
  335. {gea_program-0.65.0 → gea_program-0.66.1}/gea/setup_checklist.py +0 -0
  336. {gea_program-0.65.0 → gea_program-0.66.1}/gea/shapefile.py +0 -0
  337. {gea_program-0.65.0 → gea_program-0.66.1}/gea/shell.py +0 -0
  338. {gea_program-0.65.0 → gea_program-0.66.1}/gea/shut_in.py +0 -0
  339. {gea_program-0.65.0 → gea_program-0.66.1}/gea/simulation.py +0 -0
  340. {gea_program-0.65.0 → gea_program-0.66.1}/gea/site_map.py +0 -0
  341. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sla_report.py +0 -0
  342. {gea_program-0.65.0 → gea_program-0.66.1}/gea/slip.py +0 -0
  343. {gea_program-0.65.0 → gea_program-0.66.1}/gea/soak.py +0 -0
  344. {gea_program-0.65.0 → gea_program-0.66.1}/gea/sra.py +0 -0
  345. {gea_program-0.65.0 → gea_program-0.66.1}/gea/store_forward.py +0 -0
  346. {gea_program-0.65.0 → gea_program-0.66.1}/gea/strata_join.py +0 -0
  347. {gea_program-0.65.0 → gea_program-0.66.1}/gea/structure.py +0 -0
  348. {gea_program-0.65.0 → gea_program-0.66.1}/gea/supervisor.py +0 -0
  349. {gea_program-0.65.0 → gea_program-0.66.1}/gea/survey_cmd.py +0 -0
  350. {gea_program-0.65.0 → gea_program-0.66.1}/gea/survey_view.py +0 -0
  351. {gea_program-0.65.0 → gea_program-0.66.1}/gea/telemetry.py +0 -0
  352. {gea_program-0.65.0 → gea_program-0.66.1}/gea/timestamp.py +0 -0
  353. {gea_program-0.65.0 → gea_program-0.66.1}/gea/tls.py +0 -0
  354. {gea_program-0.65.0 → gea_program-0.66.1}/gea/tool_catalog.py +0 -0
  355. {gea_program-0.65.0 → gea_program-0.66.1}/gea/tool_library.py +0 -0
  356. {gea_program-0.65.0 → gea_program-0.66.1}/gea/tool_taxonomy.py +0 -0
  357. {gea_program-0.65.0 → gea_program-0.66.1}/gea/torque_drag.py +0 -0
  358. {gea_program-0.65.0 → gea_program-0.66.1}/gea/transient.py +0 -0
  359. {gea_program-0.65.0 → gea_program-0.66.1}/gea/tray.py +0 -0
  360. {gea_program-0.65.0 → gea_program-0.66.1}/gea/vibration.py +0 -0
  361. {gea_program-0.65.0 → gea_program-0.66.1}/gea/well3d.py +0 -0
  362. {gea_program-0.65.0 → gea_program-0.66.1}/gea/well_assembler.py +0 -0
  363. {gea_program-0.65.0 → gea_program-0.66.1}/gea/well_report.py +0 -0
  364. {gea_program-0.65.0 → gea_program-0.66.1}/gea/well_test_validation.py +0 -0
  365. {gea_program-0.65.0 → gea_program-0.66.1}/gea/wellplan.py +0 -0
  366. {gea_program-0.65.0 → gea_program-0.66.1}/gea/wellpos.py +0 -0
  367. {gea_program-0.65.0 → gea_program-0.66.1}/gea/wits0.py +0 -0
  368. {gea_program-0.65.0 → gea_program-0.66.1}/gea/witsml.py +0 -0
  369. {gea_program-0.65.0 → gea_program-0.66.1}/gea/workspace.py +0 -0
  370. {gea_program-0.65.0 → gea_program-0.66.1}/gea/xlsx_read.py +0 -0
  371. {gea_program-0.65.0 → gea_program-0.66.1}/gea_program.egg-info/SOURCES.txt +0 -0
  372. {gea_program-0.65.0 → gea_program-0.66.1}/gea_program.egg-info/dependency_links.txt +0 -0
  373. {gea_program-0.65.0 → gea_program-0.66.1}/gea_program.egg-info/entry_points.txt +0 -0
  374. {gea_program-0.65.0 → gea_program-0.66.1}/gea_program.egg-info/requires.txt +0 -0
  375. {gea_program-0.65.0 → gea_program-0.66.1}/gea_program.egg-info/top_level.txt +0 -0
  376. {gea_program-0.65.0 → gea_program-0.66.1}/setup.cfg +0 -0
  377. {gea_program-0.65.0 → gea_program-0.66.1}/tests/test_acceptance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gea-program
3
- Version: 0.65.0
3
+ Version: 0.66.1
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
@@ -1426,11 +1426,12 @@ attaches the zips to the GitHub release of that tag.
1426
1426
 
1427
1427
  ### The Windows installer
1428
1428
 
1429
- Every release also carries `GEA-Program-Setup-0.65.0.exe` (for 0.65.0): the
1429
+ Every release also carries `GEA-Program-Setup-0.66.1.exe` (for 0.66.1): the
1430
1430
  same Windows kit installed like any Windows program, for the signed-in user
1431
1431
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1432
1432
  It asks for the site workspace (an existing one such as `C:\site` is used as
1433
- it is), runs the kit's `install.cmd` with no network, and adds Start-menu and
1433
+ it is, its name included; a new one is made with the site name it asks for,
1434
+ which heads every report), runs the kit's `install.cmd` with no network, and adds Start-menu and
1434
1435
  desktop shortcuts, an Add/Remove Programs entry and, when chosen, a start at
1435
1436
  sign-in. The shortcut **GEA-Program** starts the control panel when it is not
1436
1437
  running and opens it in Microsoft Edge's app window (its own window and
@@ -1442,7 +1443,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1442
1443
  software deployment:
1443
1444
 
1444
1445
  ```
1445
- GEA-Program-Setup-0.65.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /TASKS="desktopicon"
1446
+ GEA-Program-Setup-0.66.1.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1446
1447
  ```
1447
1448
 
1448
1449
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -1387,11 +1387,12 @@ attaches the zips to the GitHub release of that tag.
1387
1387
 
1388
1388
  ### The Windows installer
1389
1389
 
1390
- Every release also carries `GEA-Program-Setup-0.65.0.exe` (for 0.65.0): the
1390
+ Every release also carries `GEA-Program-Setup-0.66.1.exe` (for 0.66.1): the
1391
1391
  same Windows kit installed like any Windows program, for the signed-in user
1392
1392
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1393
1393
  It asks for the site workspace (an existing one such as `C:\site` is used as
1394
- it is), runs the kit's `install.cmd` with no network, and adds Start-menu and
1394
+ it is, its name included; a new one is made with the site name it asks for,
1395
+ which heads every report), runs the kit's `install.cmd` with no network, and adds Start-menu and
1395
1396
  desktop shortcuts, an Add/Remove Programs entry and, when chosen, a start at
1396
1397
  sign-in. The shortcut **GEA-Program** starts the control panel when it is not
1397
1398
  running and opens it in Microsoft Edge's app window (its own window and
@@ -1403,7 +1404,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1403
1404
  software deployment:
1404
1405
 
1405
1406
  ```
1406
- GEA-Program-Setup-0.65.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /TASKS="desktopicon"
1407
+ GEA-Program-Setup-0.66.1.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1407
1408
  ```
1408
1409
 
1409
1410
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.65.0"
126
+ __version__ = "0.66.1"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -823,6 +823,7 @@ def main(argv=None) -> int:
823
823
  p_la.add_argument("--selftest", action="store_true", help="say what a launch would do here (the kit, the workspace, the window, the command), start nothing")
824
824
  p_la.add_argument("--json", action="store_true")
825
825
  p_la.add_argument("--set-workspace", type=str, default=None, help="record the site workspace for this installation (the installer runs it; never a folder inside the program's own)")
826
+ p_la.add_argument("--site-name", type=str, default=None, help="with --set-workspace: make the workspace with this name when there is none there yet (an existing one is left as it is; empty names it after its folder)")
826
827
  p_ty = sub.add_parser("tray", help="the program's icon in the Windows notification area: running or stopped, and a menu to open, start or stop the control panel")
827
828
  p_ty.add_argument("--workspace", type=str, default=None, help="the workspace folder (as gea launch finds it)")
828
829
  p_ty.add_argument("--port", type=int, default=8765)
@@ -14549,6 +14549,174 @@ def section_dh_windows_installer(tmp: str) -> None:
14549
14549
  + (f" [failed: {', '.join(failed6)}]" if failed6 else ""))
14550
14550
 
14551
14551
 
14552
+ def section_di_site_name(tmp: str) -> None:
14553
+ """Section DI - the site's name: asked for by the installer and given to a workspace it makes, an existing workspace
14554
+ left with its own; never written into the kit's scripts; changed on the Administration page and in the first-run
14555
+ guide, heading every report from then on, audited; the workflow's own install checks it."""
14556
+ import argparse as _ap
14557
+ import contextlib as _cl
14558
+ import http.cookiejar
14559
+ import importlib.util
14560
+ import io as _io
14561
+ import urllib.error
14562
+ import urllib.request
14563
+ from . import launcher as L
14564
+ from .doctor import kit_dir as _doctor_kit
14565
+ from .service import Service, Users
14566
+ from .workspace import Workspace
14567
+ pkg = Path(__file__).parent
14568
+ d = Path(tmp, "site_name"); d.mkdir()
14569
+ checkout = (pkg.parent / "pyproject.toml").exists() and (pkg.parent / "tools" / "installer.iss").exists()
14570
+
14571
+ # -- DI1 the installer's workspace, made with its name ---------------------------------------------------------------------------
14572
+ kit = d / "Programs" / "GEA-Program"; (kit / "python").mkdir(parents=True)
14573
+ (kit / "start-dashboard.cmd").write_text("@echo off\n", encoding="utf-8")
14574
+ new_ws, old_ws, plain_ws = str(d / "new site"), str(d / "old site"), str(d / "Pad 7")
14575
+ Workspace.create(old_ws, "Pad 3", actor="tester")
14576
+ old_bytes = Path(old_ws, "workspace.json").read_bytes()
14577
+ saved = L.kit_dir
14578
+ outs = {}
14579
+ try:
14580
+ L.kit_dir = lambda executable=None: kit
14581
+ for key, ws_, nm in (("new", new_ws, "Jörg's Pad 3 – east"), ("old", old_ws, "Something else"), ("plain", plain_ws, ""), ("quote", str(d / "q site"), 'Pad "3"'),
14582
+ ("record_only", str(d / "rec site"), None)):
14583
+ buf = _io.StringIO()
14584
+ with _cl.redirect_stdout(buf):
14585
+ rc = L.main(_ap.Namespace(set_workspace=ws_, site_name=nm, selftest=False, workspace=None, port=8765, background=False, no_tray=False))
14586
+ outs[key] = (rc, buf.getvalue())
14587
+ finally:
14588
+ L.kit_dir = saved
14589
+ name_of = lambda p: json.loads(Path(p, "workspace.json").read_text(encoding="utf-8"))["name"] if Path(p, "workspace.json").exists() else None
14590
+ c1 = {
14591
+ "made_named": outs["new"][0] == 0 and name_of(new_ws) == "Jörg's Pad 3 – east" and "made, named Jörg's Pad 3 – east" in outs["new"][1]
14592
+ and Workspace(new_ws).audit_log()[-1]["actor"] == "installer",
14593
+ "existing_kept": outs["old"][0] == 0 and Path(old_ws, "workspace.json").read_bytes() == old_bytes and "exists and is used as it is" in outs["old"][1],
14594
+ "empty_is_folder": outs["plain"][0] == 0 and name_of(plain_ws) == "Pad 7",
14595
+ "quote_not_taken": outs["quote"][0] == 2 and name_of(str(d / "q site")) is None and "quotation marks" in outs["quote"][1],
14596
+ "record_only": outs["record_only"][0] == 0 and name_of(str(d / "rec site")) is None,
14597
+ "limits": L.check_site_name("x" * 81) is not None and L.check_site_name("x" * 80) is None and L.check_site_name("a\tb") is not None,
14598
+ }
14599
+ failed1 = [k for k, v in c1.items() if not v]
14600
+ ok(not failed1,
14601
+ "DI1 the installer's site name: a workspace the setup makes is made with the name asked for (any letters; no quotation marks, tabs or "
14602
+ "line breaks; 80 characters at most), or the folder's name when none is given; an existing workspace is used exactly as it is, its "
14603
+ "own name kept, byte for byte" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
14604
+
14605
+ # -- DI2 no site's name in the kit; the setup asks only when it makes the workspace ------------------------------------------------
14606
+ if checkout:
14607
+ spec = importlib.util.spec_from_file_location("di_installer", str(pkg.parent / "tools" / "build_installer.py"))
14608
+ bi = importlib.util.module_from_spec(spec); spec.loader.exec_module(bi)
14609
+ start_cmd, start_sh = bi.WIN_SCRIPTS["start-dashboard.cmd"], bi.LINUX_SCRIPTS["start-dashboard.sh"]
14610
+ iss = (pkg.parent / "tools" / "installer.iss").read_text(encoding="utf-8")
14611
+ wf = (pkg.parent / ".github" / "workflows" / "build-installer.yml").read_text(encoding="utf-8")
14612
+ code = iss[iss.index("[Code]"):]
14613
+ nb = code.split("function NextButtonClick", 1)[1].split("\nend;", 1)[0]
14614
+ c2 = {
14615
+ "page": "CreateInputQueryPage(WsPage.ID" in code and "{param:SITENAME|}" in code and "Result := WorkspaceExists();" in code.split("function ShouldSkipPage", 1)[1]
14616
+ and "SiteNameProblem(SiteName())" in nb and code.count("SiteNameProblem(SiteName())") == 2,
14617
+ "made_by_program": "--site-name \"' + SiteName() + '\"'" in code and "Pos('\"', S) > 0" in code,
14618
+ "workflow": '/SITENAME="CI Pad"' in wf and "expected 'CI Pad'" in wf and "the upgrade changed the existing workspace" in wf,
14619
+ }
14620
+ where2 = "the checkout's templates and installer"
14621
+ else:
14622
+ kd = _doctor_kit(); where2 = f"the installed kit at {kd}"
14623
+ start_cmd = open(os.path.join(kd, "start-dashboard.cmd"), encoding="utf-8").read() if kd and os.path.isfile(os.path.join(kd, "start-dashboard.cmd")) else ""
14624
+ start_sh = open(os.path.join(kd, "start-dashboard.sh"), encoding="utf-8").read() if kd and os.path.isfile(os.path.join(kd, "start-dashboard.sh")) else ""
14625
+ c2 = {}
14626
+ init_lines = [ln for ln in (start_cmd + "\n" + start_sh).splitlines() if "--action init" in ln]
14627
+ c2["kit_unnamed"] = all("--name" not in ln for ln in init_lines) and "Pad 3" not in start_cmd + start_sh and (bool(init_lines) or not (start_cmd or start_sh))
14628
+ failed2 = [k for k, v in c2.items() if not v]
14629
+ ok(not failed2,
14630
+ f"DI2 ({where2}) the kit's launchers make a missing workspace named after its folder - no site's name is written into them (before "
14631
+ "v0.66 every kit named a new workspace Pad 3); the setup asks for the name only when it makes the workspace, skips the question for "
14632
+ "one that exists, passes it to the program to make it, and its build makes one named and upgrades it unchanged"
14633
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
14634
+
14635
+ # -- DI3 renamed in the control panel ----------------------------------------------------------------------------------------------
14636
+ wsp = str(d / "panel"); Workspace.create(wsp, "site", actor="tester")
14637
+ users = Users(str(Path(wsp, "users.json")))
14638
+ users.add("adm", "admin-pass-123", "admin"); users.add("op", "op-pass-123", "operator")
14639
+ svc = Service(wsp, port=0, scheduler=False).start()
14640
+ base_url = svc.url.rstrip("/")
14641
+
14642
+ def opener(name, pw_):
14643
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
14644
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
14645
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
14646
+ return o
14647
+
14648
+ def call(o, method, path, body=None):
14649
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
14650
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
14651
+ try:
14652
+ with o.open(rq, timeout=60) as rr_:
14653
+ return rr_.status, json.loads(rr_.read())
14654
+ except urllib.error.HTTPError as e:
14655
+ return e.code, json.loads(e.read())
14656
+ try:
14657
+ adm, op = opener("adm", "admin-pass-123"), opener("op", "op-pass-123")
14658
+ s_op, _ = call(op, "POST", "/api/site", {"site": {"name": "Pad 9"}})
14659
+ s_empty, e_empty = call(adm, "POST", "/api/site", {"site": {"name": " "}})
14660
+ s_q, e_q = call(adm, "POST", "/api/site", {"site": {"name": 'Pad "9"'}})
14661
+ s_ok, r_ok = call(adm, "POST", "/api/site", {"site": {"name": "Pad 9 – north", "units": "si"}})
14662
+ s_only, r_only = call(adm, "POST", "/api/site", {"site": {"name": "Pad 9 – north"}})
14663
+ _, sess = call(op, "GET", "/api/session")
14664
+ finally:
14665
+ svc.stop()
14666
+ renames = [e for e in Workspace(wsp).audit_log() if e.get("action") == "workspace.rename"]
14667
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
14668
+ c3 = {
14669
+ "admin_only": s_op == 403,
14670
+ "checked": s_empty == 400 and "not empty" in e_empty.get("error", "") and s_q == 400 and "quotation" in e_q.get("error", ""),
14671
+ "renamed": s_ok == 200 and r_ok.get("site", {}).get("units") == "si" and sess.get("workspace") == "Pad 9 – north" and Workspace(wsp).manifest["name"] == "Pad 9 – north",
14672
+ "audited_once": len(renames) == 1 and renames[0]["actor"] == "adm" and renames[0]["detail"] == {"from": "site", "to": "Pad 9 – north"} and s_only == 200,
14673
+ "page": all(x in page_src for x in ('id="stSiteName"', 'id="wSiteName"', "{name: nm}", "The site's name heads every report")),
14674
+ }
14675
+ failed3 = [k for k, v in c3.items() if not v]
14676
+ ok(not failed3,
14677
+ "DI3 the site's name changed in the control panel (Administration's site settings, and the first-run guide's first step), by an "
14678
+ "administrator only: an empty name or one with quotation marks answered, the new name heading the page and every report from then on, "
14679
+ "one audit line per change" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
14680
+
14681
+
14682
+ def section_dj_shortcut_start(tmp: str) -> None:
14683
+ """Section DJ - the control panel started the way the shortcuts start it: from pythonw.exe, which has no console and
14684
+ no standard handles. The launcher gives the panel's window a console of its own and passes no handles, so the
14685
+ panel's output reaches that console (v0.65.0 and v0.66.0 passed one, Python filled in the others with a pipe nobody
14686
+ read, and the panel stopped at its first line); the build's install test now starts it the same way."""
14687
+ from . import launcher as L
14688
+ pkg = Path(__file__).parent
14689
+ kw_win, kw_posix = L.service_popen_kwargs(True), L.service_popen_kwargs(False)
14690
+ calls = []
14691
+ saved = L.is_windows
14692
+ try:
14693
+ L.is_windows = lambda: True
14694
+ L.start_service(None, str(Path(tmp, "dj site")), 8765, popen=lambda cmd, **kw: calls.append((cmd, kw)))
14695
+ finally:
14696
+ L.is_windows = saved
14697
+ src = (pkg / "launcher.py").read_text(encoding="utf-8")
14698
+ body = src[src.index("def start_service("):src.index("def tray_command(")]
14699
+ c1 = {
14700
+ "no_handles": not ({"stdin", "stdout", "stderr"} & set(kw_win)) and kw_win["creationflags"] & L.CREATE_NEW_CONSOLE
14701
+ and kw_win["startupinfo"].wShowWindow == L.SW_SHOWMINNOACTIVE and kw_win["startupinfo"].dwFlags & L.STARTF_USESHOWWINDOW,
14702
+ "used": len(calls) == 1 and not ({"stdin", "stdout", "stderr"} & set(calls[0][1])) and calls[0][1].get("env", {}).get("GEA_PORT") == "8765"
14703
+ and "DEVNULL" not in body,
14704
+ "posix": kw_posix.get("start_new_session") is True,
14705
+ }
14706
+ wf = pkg.parent / ".github" / "workflows" / "build-installer.yml"
14707
+ if wf.exists():
14708
+ y = wf.read_text(encoding="utf-8")
14709
+ win = y[y.index("windows-kit:"):y.index("linux-kit:")]
14710
+ c1["ci_like_shortcut"] = win.count("Join-Path $app 'python\\pythonw.exe') -ArgumentList @('-m', 'gea', 'launch', '--background', '--no-tray')") == 2 \
14711
+ and "& $py -m gea launch --background" not in win
14712
+ failed = [k for k, v in c1.items() if not v]
14713
+ ok(not failed,
14714
+ "DJ1 the shortcut's start: the launcher (pythonw.exe, no console, no standard handles) starts the control panel in a minimised "
14715
+ "console of its own and passes it no handles, so its output reaches that console - one passed handle made Python give it a pipe "
14716
+ "nobody read, and the panel stopped at its first line before it opened its port (v0.65.0, v0.66.0); the build's install test starts "
14717
+ "it from pythonw.exe, as the shortcuts do" + (f" [failed: {', '.join(failed)}]" if failed else ""))
14718
+
14719
+
14552
14720
  def main() -> int:
14553
14721
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
14554
14722
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -14664,6 +14832,8 @@ def main() -> int:
14664
14832
  section_df_trusted_timestamp(tmp)
14665
14833
  section_dg_casing_program(tmp)
14666
14834
  section_dh_windows_installer(tmp)
14835
+ section_di_site_name(tmp)
14836
+ section_dj_shortcut_start(tmp)
14667
14837
  if _FAILS:
14668
14838
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
14669
14839
  for f in _FAILS:
@@ -24,9 +24,11 @@ system service (a per-user scheduled task only if the site runs
24
24
  and desktop, an optional shortcut in the user's Startup folder, and the
25
25
  user's own Add/Remove Programs entry (`HKCU`, the only registry key it
26
26
  writes, removed by its uninstaller). Silent install for a software
27
- deployment: `/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site"`.
28
- The setup never writes into the site workspace and will not take one inside the
29
- program's own folder; the uninstaller stops the control panel, removes the
27
+ deployment: `/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site"`
28
+ (`/SITENAME="Pad 3"` names a workspace the setup makes).
29
+ The setup never writes into an existing site workspace (a new one it makes,
30
+ with the site name asked for) and will not take one inside the program's own
31
+ folder; the uninstaller stops the control panel, removes the
30
32
  program and leaves the workspace. The program's window is Microsoft Edge's
31
33
  app window (`msedge --app=` on the local address); its notification-area
32
34
  icon (standard library `ctypes`, `Shell_NotifyIconW`) reads only whether the
@@ -2,7 +2,7 @@
2
2
 
3
3
  > GEA-Program-Setup: the program installed for you like any Windows program - a window of its own, an icon in the notification area, Start-menu and desktop shortcuts, an Add/Remove Programs entry, upgrades in place - with no administrator rights and the site's data left where it is.
4
4
 
5
- **The command**: download the setup from the release on GitHub (`GEA-Program-Setup-0.65.0.exe` for this version) and run it. It installs for you alone, under `%LOCALAPPDATA%\Programs\GEA-Program`, and asks for one thing: the site workspace - the folder that holds the wells, reports, records, accounts and keys (an existing one such as `C:\site` is used as it is; otherwise `%LOCALAPPDATA%\GEA-Program\site`, made at the first start). Then the Start menu's **GEA-Program** (and the desktop shortcut) starts the control panel when it is not running and opens it in a window of its own. A newer setup installs over the older one: it stops the control panel, replaces the program, keeps the workspace and the choices made before. For a site's software deployment: `GEA-Program-Setup-0.65.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /TASKS="desktopicon"`. From a terminal: `gea launch` does what the shortcut does, `gea launch --selftest` says what it would do, `gea tray --selftest` checks the icon.
5
+ **The command**: download the setup from the release on GitHub (`GEA-Program-Setup-0.66.1.exe` for this version) and run it. It installs for you alone, under `%LOCALAPPDATA%\Programs\GEA-Program`, and asks for the site workspace - the folder that holds the wells, reports, records, accounts and keys. An existing one such as `C:\site` is used exactly as it is, its name included; for a new one (by default `%LOCALAPPDATA%\GEA-Program\site`) it also asks the site's name, which heads every report, and makes the workspace with it (left empty, the workspace is named after its folder; an administrator renames the site in Administration's site settings). Then the Start menu's **GEA-Program** (and the desktop shortcut) starts the control panel when it is not running and opens it in a window of its own. A newer setup installs over the older one: it stops the control panel, replaces the program, keeps the workspace and the choices made before. For a site's software deployment: `GEA-Program-Setup-0.66.1.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"` (`/SITENAME` names a workspace the setup makes; one that exists keeps its own name). From a terminal: `gea launch` does what the shortcut does, `gea launch --selftest` says what it would do, `gea tray --selftest` checks the icon.
6
6
 
7
7
  **The window**: Microsoft Edge's app window (`msedge --app=`: the control panel with no tabs and no address bar, its own button on the taskbar, signed in as the browser is), else Google Chrome's, else a tab in the default browser. The control panel itself runs in the window titled "GEA - Operations Control Panel", minimised on the taskbar: the window that starts it again when the panel asks for a restart and says why it stopped when it stops. Closing that window stops the panel.
8
8
 
@@ -10,11 +10,11 @@
10
10
 
11
11
  **Starting at sign-in**: the setup's "Start the control panel when I sign in to Windows" puts a GEA-Program shortcut in the Startup folder: the control panel starts in the background with its icon, and no window opens until you open one. The install kit's `register-service.cmd` (a scheduled task) does the same for a kit that was not installed by the setup.
12
12
 
13
- **What it writes**: the program under `%LOCALAPPDATA%\Programs\GEA-Program` (the install kit, and the package installed into the kit's own Python by its `install.cmd`, from its own wheels, with no network); `site-workspace.txt` there, the workspace you chose; the shortcuts; the Add/Remove Programs entry under the current user. Setup and uninstall never write into the workspace, never move it and never delete it, and will not take a workspace inside the program's own folder (which an upgrade replaces and the uninstaller removes). Uninstalling stops the control panel and its icon, then removes the program and what `install.cmd` added to it.
13
+ **What it writes**: the program under `%LOCALAPPDATA%\Programs\GEA-Program` (the install kit, and the package installed into the kit's own Python by its `install.cmd`, from its own wheels, with no network); `site-workspace.txt` there, the workspace you chose; the shortcuts; the Add/Remove Programs entry under the current user. Setup and uninstall never write into an existing workspace (a new one the setup makes, with its name, and leaves at that), never move it and never delete it, and will not take a workspace inside the program's own folder (which an upgrade replaces and the uninstaller removes). Uninstalling stops the control panel and its icon, then removes the program and what `install.cmd` added to it.
14
14
 
15
15
  **The number to check**: Add/Remove Programs lists GEA-Program with the version you installed; `gea doctor --workspace C:\site` ends with `nothing blocks serving`; the Start menu's **Verify GEA-Program** runs the acceptance gate from the installation and ends with `[ACCEPTANCE] OK - N checks passed`. The release's build installs, upgrades and uninstalls the setup silently before it is published, with the control panel running, and checks the workspace untouched afterwards.
16
16
 
17
- **Signed**: when the publisher's code-signing certificate is configured for the build, the setup is signed with it (Authenticode, timestamped) and Windows names the publisher; until then it ships unsigned and Windows SmartScreen says "unknown publisher" (More info, then Run anyway). The build's provenance holds either way: `gh attestation verify GEA-Program-Setup-0.65.0.exe -R Daniel8Murphy0007/GEA-Program`. The uninstaller the setup leaves behind is Inno Setup's own and is not signed separately.
17
+ **Signed**: when the publisher's code-signing certificate is configured for the build, the setup is signed with it (Authenticode, timestamped) and Windows names the publisher; until then it ships unsigned and Windows SmartScreen says "unknown publisher" (More info, then Run anyway). The build's provenance holds either way: `gh attestation verify GEA-Program-Setup-0.66.1.exe -R Daniel8Murphy0007/GEA-Program`. The uninstaller the setup leaves behind is Inno Setup's own and is not signed separately.
18
18
 
19
19
  **What this page will not call a measurement**: the icon's running or stopped is whether the control panel's port answers on this computer, read every three seconds - not whether every patch, job or report behind it is healthy (Home and the Verification page say that).
20
20
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > The workspace folder, the four roles and what each cannot do, the audit log.
4
4
 
5
- **The command**: `gea workspace --path C:\site --action init --name "Pad 3"`; `gea serve --workspace C:\site` (loopback by default; it opens the control panel in your browser once it is listening, `--no-browser` for a scheduled start, and the window it runs in prints what happens on the page - sign-ins, jobs, stops - as it happens; `--behind-proxy` with a TLS proxy in front, see `deploy/README.md`); `gea users --workspace C:\site --action add --name ... --role admin` (the password from `GEA_PASSWORD`, never the command line). `gea workspace --path C:\site --action rename --name "Pad 3"` sets the site's name as every report prints it.
5
+ **The command**: `gea workspace --path C:\site --action init --name "Pad 3"`; `gea serve --workspace C:\site` (loopback by default; it opens the control panel in your browser once it is listening, `--no-browser` for a scheduled start, and the window it runs in prints what happens on the page - sign-ins, jobs, stops - as it happens; `--behind-proxy` with a TLS proxy in front, see `deploy/README.md`); `gea users --workspace C:\site --action add --name ... --role admin` (the password from `GEA_PASSWORD`, never the command line). `gea workspace --path C:\site --action rename --name "Pad 3"` sets the site's name as every report prints it - as does Site name in Administration's site settings (and the first-run guide's first step), for an administrator; reports already written keep the name they were written with.
6
6
 
7
7
  **What it writes**: `workspace.json`, `wells/<id>/{source,records}`, `config/` (every configuration versioned, diffed, reversible), `monitor/`, `reports/`, `jobs/<id>/{job.json,log.txt}`, `records/audit.jsonl` (append-only, every action with the actor and the SHA-256 of its inputs), `users.json` (salted PBKDF2-SHA256, never a password).
8
8
 
@@ -134,6 +134,33 @@ def set_workspace(kit: Optional[Path], path: str) -> str:
134
134
  return path
135
135
 
136
136
 
137
+ SITE_NAME_MAX = 80
138
+
139
+
140
+ def check_site_name(name: Optional[str]) -> Optional[str]:
141
+ """Why a site name cannot be used, or None. Empty is allowed: the workspace is then named after its folder."""
142
+ name = (name or '').strip()
143
+ if len(name) > SITE_NAME_MAX:
144
+ return f'a site name of {len(name)} characters; {SITE_NAME_MAX} at most (it heads every report)'
145
+ if any(c in name for c in '"\r\n\t'):
146
+ return 'a site name holds no quotation marks, line breaks or tabs'
147
+ return None
148
+
149
+
150
+ def create_workspace(path: str, site_name: Optional[str], actor: str = 'installer') -> Optional[str]:
151
+ """Make the workspace with its name when there is none at `path` yet; an existing workspace is left exactly as it
152
+ is (its name is changed on the Administration page). Returns the name it was made with, or None when it existed."""
153
+ if os.path.isfile(os.path.join(path, 'workspace.json')):
154
+ return None
155
+ why = check_site_name(site_name)
156
+ if why:
157
+ raise LaunchError(why)
158
+ from .workspace import Workspace
159
+ name = (site_name or '').strip() or os.path.basename(os.path.normpath(path))
160
+ Workspace.create(path, name, actor=actor)
161
+ return name
162
+
163
+
137
164
  def port_open(port: int, host: str = '127.0.0.1', timeout: float = 0.4) -> bool:
138
165
  try:
139
166
  with socket.create_connection((host, int(port)), timeout=timeout):
@@ -265,19 +292,27 @@ def service_command(kit: Optional[Path], ws: str, port: int, executable: Optiona
265
292
  return [console_python(executable), '-m', 'gea', 'serve', '--workspace', ws, '--port', str(int(port)), '--no-browser']
266
293
 
267
294
 
268
- def _minimised_console() -> dict:
269
- if not is_windows():
270
- return {'start_new_session': True}
271
- si = subprocess.STARTUPINFO()
295
+ def service_popen_kwargs(windows: Optional[bool] = None) -> dict:
296
+ """How the control panel's window is started. On Windows: a console of its own, minimised, and NO standard
297
+ handles passed. Passing any of stdin, stdout or stderr makes Python hand the child the parent's other handles
298
+ too - and the launcher runs under pythonw.exe, which has none, so Python gives the child a pipe nobody reads:
299
+ the panel's first line of output then fails and it stops before it opens its port (v0.65.0 and v0.66.0: "did
300
+ not start within 120 seconds" from the Start-menu shortcut, while the build's test, run from a console,
301
+ passed). With no handles given, the new console's own are used."""
302
+ windows = is_windows() if windows is None else windows
303
+ if not windows:
304
+ return {'start_new_session': True, 'stdin': subprocess.DEVNULL, 'close_fds': True}
305
+ si_cls = getattr(subprocess, 'STARTUPINFO', None) # Windows only; a stand-in where the check runs elsewhere
306
+ si = si_cls() if si_cls else __import__('types').SimpleNamespace(dwFlags=0, wShowWindow=0)
272
307
  si.dwFlags |= STARTF_USESHOWWINDOW
273
308
  si.wShowWindow = SW_SHOWMINNOACTIVE
274
- return {'creationflags': CREATE_NEW_CONSOLE, 'startupinfo': si}
309
+ return {'creationflags': CREATE_NEW_CONSOLE, 'startupinfo': si, 'close_fds': True}
275
310
 
276
311
 
277
312
  def start_service(kit: Optional[Path], ws: str, port: int, popen: Callable = subprocess.Popen) -> list:
278
313
  cmd = service_command(kit, ws, port)
279
314
  env = dict(os.environ, GEA_WORKSPACE=ws, GEA_PORT=str(int(port)))
280
- popen(cmd, cwd=str(kit) if kit else None, env=env, stdin=subprocess.DEVNULL, close_fds=True, **_minimised_console())
315
+ popen(cmd, cwd=str(kit) if kit else None, env=env, **service_popen_kwargs())
281
316
  return cmd
282
317
 
283
318
 
@@ -407,7 +442,15 @@ def main(a) -> int:
407
442
  except LaunchError as e:
408
443
  say('GEA-Program', f'The site workspace was not recorded: {e}', error=True)
409
444
  return 2
410
- say('GEA-Program', f'site workspace: {path} (recorded in {Path(kit_dir()) / WORKSPACE_FILE})')
445
+ made = None
446
+ if getattr(a, 'site_name', None) is not None:
447
+ try:
448
+ made = create_workspace(path, a.site_name)
449
+ except (LaunchError, OSError) as e:
450
+ say('GEA-Program', f'The site workspace was recorded but not made: {e}', error=True)
451
+ return 2
452
+ say('GEA-Program', f'site workspace: {path} (recorded in {Path(kit_dir()) / WORKSPACE_FILE})'
453
+ + (f'; made, named {made}' if made else ('; it exists and is used as it is' if getattr(a, 'site_name', None) is not None else '')))
411
454
  return 0
412
455
  if getattr(a, 'selftest', False):
413
456
  p = plan(a.workspace, a.port)
@@ -1260,9 +1260,25 @@ class App:
1260
1260
  return {'jobs': out}
1261
1261
 
1262
1262
  def set_site(self, user: dict, site: dict) -> dict:
1263
- """Site-wide display defaults in the manifest: units, time zone, display name, contact line. Admin only."""
1263
+ """Site-wide display defaults in the manifest: units, time zone, display name, contact line - and `name`, the
1264
+ site's name that heads every report (the workspace's own name, audited as workspace.rename). Admin only."""
1265
+ site = dict(site or {})
1266
+ if 'name' in site:
1267
+ from .launcher import check_site_name
1268
+ nm = site.pop('name')
1269
+ if not isinstance(nm, str) or not nm.strip():
1270
+ raise ApiError(400, 'the site name must be text, and not empty: it heads every report')
1271
+ why = check_site_name(nm)
1272
+ if why:
1273
+ raise ApiError(400, why)
1274
+ try:
1275
+ self.ws.rename(nm.strip(), actor=user['name'])
1276
+ except WorkspaceError as e:
1277
+ raise ApiError(400, str(e))
1278
+ if not site:
1279
+ return dict(self.ws.manifest.get('site') or {})
1264
1280
  cur = dict(self.ws.manifest.get('site') or {})
1265
- for k, v in (site or {}).items():
1281
+ for k, v in site.items():
1266
1282
  if k not in ('units', 'time_zone', 'display_name', 'contact', 'wizard_done'):
1267
1283
  raise ApiError(400, f'unknown site setting: {k}')
1268
1284
  if k == 'units' and v not in ('field', 'si'):
@@ -1804,7 +1804,7 @@ VIEWS.admin = async () => {
1804
1804
  const nt = await get('/api/notifications');
1805
1805
  let SS = {sessions: []}; try { SS = await get('/api/sessions'); } catch (e) {}
1806
1806
  h += `<div class="card"><h3>Live sessions (${SS.sessions.length})</h3><p class="small muted">Who is signed in, from where, since when. Revoking signs that session out at its next request. ${esc(SS.note || '')}</p><table><thead><tr><th>User</th><th>Role</th><th>From</th><th>Signed in</th><th>Last seen</th><th>Expires</th><th></th></tr></thead><tbody>${SS.sessions.map(x => `<tr><td>${esc(x.name)}${x.via === 'company' ? ' <span class="small muted">(company)</span>' : ''}</td><td>${esc(x.role)}</td><td class="mono small">${esc(x.client)}</td><td class="small">${tz(x.created_utc)}</td><td class="small">${tz(x.last_seen_utc)}</td><td class="small">${tz(x.expires_utc)}</td><td><button class="secondary" data-rs="${esc(x.sid)}" type="button">Revoke</button> <button class="secondary danger" data-ru="${esc(x.name)}" type="button">All of ${esc(x.name)}</button></td></tr>`).join('') || '<tr><td colspan="7" class="muted">none</td></tr>'}</tbody></table></div>`;
1807
- h += `<div class="card"><h3>Site settings</h3><p class="small muted">Defaults for everyone who has not chosen their own: display units and time zone, the name on the home page, a contact line for the sign-in screen.</p><div class="row"><label>Display name<input id="stName" value="${esc(SITE.display_name || '')}" placeholder="${esc(workspaceName)}"></label><label>Units<select id="stUnits"><option value="field" ${(SITE.units || 'field') === 'field' ? 'selected' : ''}>field (psi, °F, ft)</option><option value="si" ${SITE.units === 'si' ? 'selected' : ''}>SI (kPa, °C, m)</option></select></label><label>Time zone<input id="stTz" value="${esc(SITE.time_zone || 'UTC')}" placeholder="UTC or America/Chicago" list="tzList"></label><label>Contact<input id="stContact" value="${esc(SITE.contact || '')}" placeholder="who to call"></label><button id="stSave" type="button">Save</button></div><datalist id="tzList">${['UTC', 'America/Chicago', 'America/Denver', 'America/New_York', 'America/Los_Angeles', 'America/Anchorage', 'Europe/London', 'Europe/Oslo', 'Asia/Dubai', 'Asia/Kuala_Lumpur', 'Australia/Perth'].map(z => `<option value="${z}">`).join('')}</datalist></div>`;
1807
+ h += `<div class="card"><h3>Site settings</h3><p class="small muted">The site's name heads every report written from now on and the top of this page (reports already written keep theirs; a rename is in the audit log). Then defaults for everyone who has not chosen their own: display units and time zone, the name on the home page, a contact line for the sign-in screen.</p><div class="row"><label>Site name (on every report)<input id="stSiteName" maxlength="80" required value="${esc(workspaceName)}"></label><label>Display name<input id="stName" value="${esc(SITE.display_name || '')}" placeholder="${esc(workspaceName)}"></label><label>Units<select id="stUnits"><option value="field" ${(SITE.units || 'field') === 'field' ? 'selected' : ''}>field (psi, °F, ft)</option><option value="si" ${SITE.units === 'si' ? 'selected' : ''}>SI (kPa, °C, m)</option></select></label><label>Time zone<input id="stTz" value="${esc(SITE.time_zone || 'UTC')}" placeholder="UTC or America/Chicago" list="tzList"></label><label>Contact<input id="stContact" value="${esc(SITE.contact || '')}" placeholder="who to call"></label><button id="stSave" type="button">Save</button></div><datalist id="tzList">${['UTC', 'America/Chicago', 'America/Denver', 'America/New_York', 'America/Los_Angeles', 'America/Anchorage', 'Europe/London', 'Europe/Oslo', 'Asia/Dubai', 'Asia/Kuala_Lumpur', 'Australia/Perth'].map(z => `<option value="${z}">`).join('')}</datalist></div>`;
1808
1808
  h += `<div class="card"><h3>Notifications</h3><p class="small muted">Rules route events (alarm activations by priority, failed jobs, patches down, items waiting for approval) to channels: a webhook URL or an SMTP mailbox. Secrets stay out of the configuration - an SMTP password is read from the environment variable the channel names. The configuration is versioned as <span class="mono">notifications</span>.</p>`;
1809
1809
  h += nt.configured ? `<table><thead><tr><th>Channel</th><th>Kind</th><th>Target</th><th></th></tr></thead><tbody>${nt.channels.map(c => `<tr><td>${esc(c.name)}</td><td>${esc(c.kind)}</td><td class="mono small">${esc(c.target)}${c.secret ? ' <span class="muted">(with credentials)</span>' : ''}</td><td><button class="secondary" data-nt="${esc(c.name)}" type="button">Send a test</button></td></tr>`).join('')}</tbody></table><p class="small">${nt.rules.length} rule(s): ${nt.rules.map(r => `${esc(r.event)}${r.priority ? ' ' + esc(r.priority.join('/')) : ''} → ${esc((r.channels || []).join(', '))}`).join(' · ')} · quiet window ${nt.quiet_s} s</p>` : `<p class="small">Not configured. <button class="secondary" id="ntExample" type="button">Load the example</button> into the editor, change the URL or mailbox, validate and commit.</p>`;
1810
1810
  h += `<details><summary>Edit the configuration</summary><label>JSON<textarea id="ntCfg" spellcheck="false"></textarea></label><div class="row"><label>Note<input id="ntNote"></label><button class="secondary" id="ntLoad" type="button">Load current</button><button id="ntSave" type="button">Validate and commit</button></div></details>`;
@@ -1831,7 +1831,7 @@ VIEWS.admin = async () => {
1831
1831
  const o = await get('/api/overview'); const wsPath = o.workspace.path;
1832
1832
  document.querySelectorAll('[data-rs]').forEach(b => b.onclick = async () => { try { const r = await post('/api/sessions/revoke', {sid: b.dataset.rs}); toast(`${r.revoked} session(s) revoked`, 'ok'); route(); } catch (e) { toast(e.message, 'err'); } });
1833
1833
  document.querySelectorAll('[data-ru]').forEach(b => b.onclick = async () => { if (!confirm(`Sign ${b.dataset.ru} out of every session?`)) return; try { const r = await post('/api/sessions/revoke', {name: b.dataset.ru}); toast(`${r.revoked} session(s) revoked`, 'ok'); if (b.dataset.ru === me.name) { me = null; boot(); } else route(); } catch (e) { toast(e.message, 'err'); } });
1834
- $('#stSave').onclick = async () => { try { const r = await post('/api/site', {site: {display_name: $('#stName').value, units: $('#stUnits').value, time_zone: $('#stTz').value, contact: $('#stContact').value}}); SITE = r.site; toast('Site settings saved', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1834
+ $('#stSave').onclick = async () => { const nm = $('#stSiteName').value.trim(); if (!nm) return toast('The site needs a name: it heads every report', 'err'); try { const r = await post('/api/site', {site: {...(nm !== workspaceName ? {name: nm} : {}), display_name: $('#stName').value, units: $('#stUnits').value, time_zone: $('#stTz').value, contact: $('#stContact').value}}); SITE = r.site; workspaceName = nm; $('#siteName').textContent = nm; toast('Site settings saved', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1835
1835
  const ntEx = $('#ntExample'); if (ntEx) ntEx.onclick = () => { $('#ntCfg').value = JSON.stringify({channels: [{name: 'ops-hook', kind: 'webhook', url: 'https://hooks.example.com/gea', headers: {}}, {name: 'mail', kind: 'smtp', host: 'smtp.example.com', port: 587, starttls: true, from: 'gea@example.com', to: ['operations@example.com'], user: 'gea', password_env: 'GEA_SMTP_PASSWORD'}], rules: [{event: 'alarm.activated', priority: ['P1'], channels: ['ops-hook', 'mail']}, {event: 'job.failed', channels: ['ops-hook']}, {event: 'patch.down', channels: ['ops-hook', 'mail']}, {event: 'approval.pending', channels: ['mail']}], quiet_s: 300}, null, 1); $('details').open = true; };
1836
1836
  $('#ntLoad').onclick = async () => { try { const c = await get('/api/config/notifications'); $('#ntCfg').value = JSON.stringify(c.current, null, 1); } catch (e) { toast('no committed notifications yet: ' + e.message, 'err'); } };
1837
1837
  $('#ntSave').onclick = async () => { let c; try { c = JSON.parse($('#ntCfg').value); } catch (e) { return toast('not valid JSON: ' + e.message, 'err'); } try { const r = await post('/api/config/commit', {name: 'notifications', content: c, note: $('#ntNote').value}); toast(r.unchanged ? 'Unchanged' : `Committed notifications v${r.version}`, 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
@@ -1876,12 +1876,12 @@ VIEWS.welcome = async () => {
1876
1876
  const step = o.workspace.n_wells === 0 ? (SITE.units ? 2 : 1) : (users.length < 2 ? 3 : 4);
1877
1877
  const steps = ['Site', 'First well', 'People', 'First refresh'].map((t, i) => `<span class="${i + 1 === step ? 'on' : ''}">${i + 1}. ${t}</span>`).join('');
1878
1878
  let h = `<h2>Setting up ${esc(workspaceName)}</h2>${help('welcome')}<div class="steps">${steps}</div>`;
1879
- h += `<div class="card wizard"><h3>1. The site</h3><p class="small muted">How numbers and times are shown to everyone here, and the name on the home page.</p><div class="row"><label>Display name<input id="wName" value="${esc(SITE.display_name || workspaceName)}"></label><label>Units<select id="wUnits"><option value="field" ${(SITE.units || 'field') === 'field' ? 'selected' : ''}>field (psi, °F, ft)</option><option value="si" ${SITE.units === 'si' ? 'selected' : ''}>SI (kPa, °C, m)</option></select></label><label>Time zone<input id="wTz" value="${esc(SITE.time_zone || 'UTC')}" placeholder="UTC or America/Chicago"></label><button id="wSite" type="button">Save</button></div></div>`;
1879
+ h += `<div class="card wizard"><h3>1. The site</h3><p class="small muted">The site's name heads every report; how numbers and times are shown to everyone here, and the name on the home page.</p><div class="row"><label>Site name (on every report)<input id="wSiteName" maxlength="80" value="${esc(workspaceName)}"></label><label>Display name<input id="wName" value="${esc(SITE.display_name || workspaceName)}"></label><label>Units<select id="wUnits"><option value="field" ${(SITE.units || 'field') === 'field' ? 'selected' : ''}>field (psi, °F, ft)</option><option value="si" ${SITE.units === 'si' ? 'selected' : ''}>SI (kPa, °C, m)</option></select></label><label>Time zone<input id="wTz" value="${esc(SITE.time_zone || 'UTC')}" placeholder="UTC or America/Chicago"></label><button id="wSite" type="button">Save</button></div></div>`;
1880
1880
  h += `<div class="card wizard"><h3>2. The first well</h3><p class="small muted">Upload a historian CSV, LAS, SEG-Y or operator table - or import a file from a folder you allow the dashboard to read.</p><div class="row"><label>Well name<input id="wWell" placeholder="A-12"></label><label>File<input id="wFile" type="file"></label><label>Gauge station MD (ft)<input id="wMd" type="number" placeholder="10000"></label><button id="wAdd" type="button">Add the well</button></div><p class="small">Or: <a href="#/files">browse the import roots</a>${fr.import.length ? '' : ' (none yet - add one below)'}.</p>${fr.import.length ? '' : `<div class="row"><label>Import root name<input id="wRootName" placeholder="historian"></label><label>Folder on this machine<input id="wRootPath" placeholder="C:\\data\\historian"></label><button class="secondary" id="wRoot" type="button">Allow this folder</button></div>`}</div>`;
1881
1881
  h += `<div class="card wizard"><h3>3. People</h3><p class="small muted">${users.length} account(s). Operators run and acknowledge; approvers decide on well tests and re-fits; viewers only look.</p><div class="row"><label>Name<input id="wUser"></label><label>Password (8+)<input id="wPw" type="password"></label><label>Role<select id="wRole">${['operator', 'approver', 'viewer'].map(r => `<option>${r}</option>`).join('')}</select></label><button id="wUserAdd" type="button">Add</button></div></div>`;
1882
1882
  h += `<div class="card wizard"><h3>4. The first refresh</h3><p class="small muted">Runs every calculation on every well and builds the reports. Takes a minute per well.</p><div class="row"><button id="wRefresh" type="button" ${o.workspace.n_wells ? '' : 'disabled'}>Refresh every report</button><button class="secondary" id="wDone" type="button">Finish the guide</button></div></div>`;
1883
1883
  $('#main').innerHTML = h;
1884
- $('#wSite').onclick = async () => { try { const r = await post('/api/site', {site: {display_name: $('#wName').value, units: $('#wUnits').value, time_zone: $('#wTz').value}}); SITE = r.site; toast('Saved', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1884
+ $('#wSite').onclick = async () => { try { const nm = $('#wSiteName').value.trim(); const r = await post('/api/site', {site: {...(nm && nm !== workspaceName ? {name: nm} : {}), display_name: ($('#wName').value === workspaceName && nm) ? nm : $('#wName').value, units: $('#wUnits').value, time_zone: $('#wTz').value}}); SITE = r.site; if (nm) { workspaceName = nm; $('#siteName').textContent = nm; } toast('Saved', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1885
1885
  $('#wAdd').onclick = async () => { const f = $('#wFile').files[0]; if (!f) return toast('Choose a file', 'err'); try { const b64 = await readFile(f); await post('/api/wells/file', {display: $('#wWell').value || f.name, filename: f.name, content_b64: b64, station_md: $('#wMd').value ? +$('#wMd').value : null}); toast('Well added', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1886
1886
  const wr = $('#wRoot'); if (wr) wr.onclick = async () => { try { await post('/api/files/roots/add', {which: 'import', name: $('#wRootName').value, path: $('#wRootPath').value}); toast('Folder allowed', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
1887
1887
  $('#wUserAdd').onclick = async () => { try { await post('/api/users/add', {name: $('#wUser').value, password: $('#wPw').value, role: $('#wRole').value}); toast('User added', 'ok'); route(); } catch (e) { toast(e.message, 'err'); } };
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gea-program
3
- Version: 0.65.0
3
+ Version: 0.66.1
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
@@ -1426,11 +1426,12 @@ attaches the zips to the GitHub release of that tag.
1426
1426
 
1427
1427
  ### The Windows installer
1428
1428
 
1429
- Every release also carries `GEA-Program-Setup-0.65.0.exe` (for 0.65.0): the
1429
+ Every release also carries `GEA-Program-Setup-0.66.1.exe` (for 0.66.1): the
1430
1430
  same Windows kit installed like any Windows program, for the signed-in user
1431
1431
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1432
1432
  It asks for the site workspace (an existing one such as `C:\site` is used as
1433
- it is), runs the kit's `install.cmd` with no network, and adds Start-menu and
1433
+ it is, its name included; a new one is made with the site name it asks for,
1434
+ which heads every report), runs the kit's `install.cmd` with no network, and adds Start-menu and
1434
1435
  desktop shortcuts, an Add/Remove Programs entry and, when chosen, a start at
1435
1436
  sign-in. The shortcut **GEA-Program** starts the control panel when it is not
1436
1437
  running and opens it in Microsoft Edge's app window (its own window and
@@ -1442,7 +1443,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1442
1443
  software deployment:
1443
1444
 
1444
1445
  ```
1445
- GEA-Program-Setup-0.65.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /TASKS="desktopicon"
1446
+ GEA-Program-Setup-0.66.1.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1446
1447
  ```
1447
1448
 
1448
1449
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gea-program"
7
- version = "0.65.0"
7
+ version = "0.66.1"
8
8
  description = "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."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes