DescripyTor 0.3.1__tar.gz → 0.3.2__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.

Potentially problematic release.


This version of DescripyTor might be problematic. Click here for more details.

Files changed (242) hide show
  1. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/gui_server.py +23 -8
  2. descripytor-0.3.2/M3_modeler/bassa_explorer/running.py +61 -0
  3. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/server.py +10 -3
  4. {descripytor-0.3.1/descripytor.egg-info → descripytor-0.3.2}/PKG-INFO +1 -1
  5. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/__init__.py +1 -1
  6. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/cli.py +13 -2
  7. {descripytor-0.3.1 → descripytor-0.3.2/descripytor.egg-info}/PKG-INFO +1 -1
  8. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor.egg-info/SOURCES.txt +1 -0
  9. {descripytor-0.3.1 → descripytor-0.3.2}/pyproject.toml +1 -1
  10. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_bassa_explorer.py +89 -35
  11. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_gui_server.py +35 -28
  12. {descripytor-0.3.1 → descripytor-0.3.2}/Getting_started_with_examples/descriptor_extraction_toolkit/config_example.json +0 -0
  13. {descripytor-0.3.1 → descripytor-0.3.2}/Getting_started_with_examples/input_example.json +0 -0
  14. {descripytor-0.3.1 → descripytor-0.3.2}/LICENSE +0 -0
  15. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/__init__.py +0 -0
  16. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/esp_grid_driver.py +0 -0
  17. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/main.py +0 -0
  18. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/outwards_senders.py +0 -0
  19. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/__init__.py +0 -0
  20. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/adopt.py +0 -0
  21. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/build.py +0 -0
  22. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/checks.py +0 -0
  23. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/cluster.py +0 -0
  24. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/examples/phosphines.csv +0 -0
  25. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/examples/protocol_bgu.json +0 -0
  26. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/protocol.py +0 -0
  27. {descripytor-0.3.1 → descripytor-0.3.2}/M1_pre_calculations/pipeline/stages.py +0 -0
  28. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/__init__.py +0 -0
  29. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/_folder_dialog.py +0 -0
  30. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/atom_picker.html +0 -0
  31. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/crest_boltzmann_sterimol.py +0 -0
  32. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/cube_sterimol.py +0 -0
  33. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/data_extractor.py +0 -0
  34. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/descriptor_extractor.py +0 -0
  35. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/__init__.py +0 -0
  36. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/bond_angle_length_utils.py +0 -0
  37. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/bond_graph.py +0 -0
  38. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/dipole_utils.py +0 -0
  39. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/esp_grid_utils.py +0 -0
  40. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/sterimol_utils.py +0 -0
  41. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/extractor_utils/vibrations_utils.py +0 -0
  42. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feather_extractor.py +0 -0
  43. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/__init__.py +0 -0
  44. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/build.py +0 -0
  45. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/data.py +0 -0
  46. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/feature_explorer.html +0 -0
  47. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/009_phos.xyz +0 -0
  48. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/009_phos_CuCl.xyz +0 -0
  49. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/026_box_CuCl.xyz +0 -0
  50. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/026_lig.xyz +0 -0
  51. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/032_lig.xyz +0 -0
  52. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/041_lig.xyz +0 -0
  53. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/045_box_CuCl.xyz +0 -0
  54. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/045_lig.xyz +0 -0
  55. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/046_lig.xyz +0 -0
  56. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/055_phos.xyz +0 -0
  57. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_008.xyz +0 -0
  58. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_018.xyz +0 -0
  59. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_020.xyz +0 -0
  60. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_047.xyz +0 -0
  61. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Cu_PyOx_041.xyz +0 -0
  62. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_1.data.json +0 -0
  63. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_1.xyz +0 -0
  64. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_12.data.json +0 -0
  65. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_12.xyz +0 -0
  66. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_13_str_target.data.json +0 -0
  67. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_13_str_target.xyz +0 -0
  68. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_7.data.json +0 -0
  69. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/FL_lig_7.xyz +0 -0
  70. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Ni_BOX_072.xyz +0 -0
  71. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/Ni_BOX_078.xyz +0 -0
  72. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/benzoic_acid.xyz +0 -0
  73. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-CN.data.json +0 -0
  74. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-CN.xyz +0 -0
  75. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-CN_BA.data.json +0 -0
  76. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-CN_BA.xyz +0 -0
  77. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-OMe.data.json +0 -0
  78. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-OMe.xyz +0 -0
  79. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-OMe_BA.data.json +0 -0
  80. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-OMe_BA.xyz +0 -0
  81. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-SO2Me_BA.data.json +0 -0
  82. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/m-SO2Me_BA.xyz +0 -0
  83. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/naphthyl_BA.data.json +0 -0
  84. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/naphthyl_BA.xyz +0 -0
  85. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/o-Me_BA.data.json +0 -0
  86. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/o-Me_BA.xyz +0 -0
  87. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-CF3.data.json +0 -0
  88. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-CF3.xyz +0 -0
  89. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-CF3_substrate.xyz +0 -0
  90. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-CO2Me_BA.data.json +0 -0
  91. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-CO2Me_BA.xyz +0 -0
  92. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-COMe_BA.data.json +0 -0
  93. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-COMe_BA.xyz +0 -0
  94. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-Cl_BA.data.json +0 -0
  95. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-Cl_BA.xyz +0 -0
  96. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-F_BA.data.json +0 -0
  97. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-F_BA.xyz +0 -0
  98. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-H_BA.data.json +0 -0
  99. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-H_BA.xyz +0 -0
  100. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-NO2.data.json +0 -0
  101. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-NO2.xyz +0 -0
  102. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-OMe_BA.data.json +0 -0
  103. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-OMe_BA.xyz +0 -0
  104. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-Otf.data.json +0 -0
  105. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-Otf.xyz +0 -0
  106. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-Otf_substrate.xyz +0 -0
  107. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-t-Bu_BA.data.json +0 -0
  108. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/fixtures/p-t-Bu_BA.xyz +0 -0
  109. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/gate.js +0 -0
  110. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/gate_pos.js +0 -0
  111. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/make_pos_refs.py +0 -0
  112. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/make_refs.py +0 -0
  113. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/pos_refs.json +0 -0
  114. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/promol.js +0 -0
  115. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/rdkit/RDKit_minimal.js +0 -0
  116. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/rdkit/RDKit_minimal.wasm +0 -0
  117. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/refs.json +0 -0
  118. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/render_presets.js +0 -0
  119. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/scene.js +0 -0
  120. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/smarts.js +0 -0
  121. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/smoke.js +0 -0
  122. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/sterimol.js +0 -0
  123. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/template.html +0 -0
  124. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_explorer/vbur.js +0 -0
  125. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_extraction_gui.html +0 -0
  126. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/feature_packages.py +0 -0
  127. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/gaussian_handler.py +0 -0
  128. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/ligand_topology.py +0 -0
  129. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/metal_complex.py +0 -0
  130. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/morfeus_sterics.py +0 -0
  131. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/placement_search.py +0 -0
  132. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/static/3Dmol-min.js +0 -0
  133. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/sterimol_standalone.py +0 -0
  134. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/theta_explorer/__init__.py +0 -0
  135. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/theta_explorer/build.py +0 -0
  136. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/theta_explorer/data.py +0 -0
  137. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/theta_explorer/sterimol.js +0 -0
  138. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/theta_explorer/theta_explorer.html +0 -0
  139. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/xtb_singlepoint.py +0 -0
  140. {descripytor-0.3.1 → descripytor-0.3.2}/M2_data_extractor/xyz_io.py +0 -0
  141. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/__init__.py +0 -0
  142. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/__init__.py +0 -0
  143. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/batch.py +0 -0
  144. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/build_case.py +0 -0
  145. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/core_fixed.py +0 -0
  146. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/engine.py +0 -0
  147. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/example.js +0 -0
  148. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/example_cs2.csv +0 -0
  149. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/example_small.csv +0 -0
  150. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/index.html +0 -0
  151. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/influence_check.py +0 -0
  152. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/read_structures.py +0 -0
  153. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/settings.py +0 -0
  154. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/bassa_explorer/test_page.py +0 -0
  155. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/modeling.py +0 -0
  156. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/modeling_utils.py +0 -0
  157. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/nested_cv.py +0 -0
  158. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/plot.py +0 -0
  159. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/ridge_search.py +0 -0
  160. {descripytor-0.3.1 → descripytor-0.3.2}/M3_modeler/validation.py +0 -0
  161. {descripytor-0.3.1 → descripytor-0.3.2}/MANIFEST.in +0 -0
  162. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/MCS_optimization.py +0 -0
  163. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/RDKIT_utils.py +0 -0
  164. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/__init__.py +0 -0
  165. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/df_funs.py +0 -0
  166. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/fmcs.py +0 -0
  167. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/gaussian_handler.py +0 -0
  168. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/loss.py +0 -0
  169. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/renumbering.py +0 -0
  170. {descripytor-0.3.1 → descripytor-0.3.2}/MolAlign/rotations.py +0 -0
  171. {descripytor-0.3.1 → descripytor-0.3.2}/README.md +0 -0
  172. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/__main__.py +0 -0
  173. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/compat.py +0 -0
  174. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/__init__.py +0 -0
  175. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/README.md +0 -0
  176. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/m-CN.feather +0 -0
  177. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/m-COMe.feather +0 -0
  178. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/m-NO2.feather +0 -0
  179. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/m-OMe.feather +0 -0
  180. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/m-cyclo.feather +0 -0
  181. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/o-CN.feather +0 -0
  182. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/outcomes.csv +0 -0
  183. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-CF3.feather +0 -0
  184. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-CHO.feather +0 -0
  185. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-CN.feather +0 -0
  186. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-COOMe.feather +0 -0
  187. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-Cl.feather +0 -0
  188. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-MeO.feather +0 -0
  189. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-NO2.feather +0 -0
  190. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-OMe.feather +0 -0
  191. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/p-Otf.feather +0 -0
  192. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/presets.json +0 -0
  193. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/pyridine-m-OMe.feather +0 -0
  194. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/thiophene.feather +0 -0
  195. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/baptiste_products/unsub.feather +0 -0
  196. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/config_example.json +0 -0
  197. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/README.md +0 -0
  198. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/basic.feather +0 -0
  199. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/input_example.json +0 -0
  200. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/m_Br.feather +0 -0
  201. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/m_Cl.feather +0 -0
  202. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/m_nitro.feather +0 -0
  203. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/o_Br.feather +0 -0
  204. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/o_Cl.feather +0 -0
  205. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/o_F.feather +0 -0
  206. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/p_Cl.feather +0 -0
  207. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/p_F.feather +0 -0
  208. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/p_I.feather +0 -0
  209. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/feather_example/presets.json +0 -0
  210. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor/examples/input_example.json +0 -0
  211. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor.egg-info/dependency_links.txt +0 -0
  212. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor.egg-info/entry_points.txt +0 -0
  213. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor.egg-info/requires.txt +0 -0
  214. {descripytor-0.3.1 → descripytor-0.3.2}/descripytor.egg-info/top_level.txt +0 -0
  215. {descripytor-0.3.1 → descripytor-0.3.2}/setup.cfg +0 -0
  216. {descripytor-0.3.1 → descripytor-0.3.2}/setup.py +0 -0
  217. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_arm_fixes.py +0 -0
  218. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_bond_graph.py +0 -0
  219. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_cli.py +0 -0
  220. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_compat.py +0 -0
  221. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_connectivity.py +0 -0
  222. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_esp_grid.py +0 -0
  223. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_examples.py +0 -0
  224. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_extract_and_model.py +0 -0
  225. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_feature_fixes.py +0 -0
  226. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_metal_complex.py +0 -0
  227. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_metal_complex_frame.py +0 -0
  228. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_pipeline.py +0 -0
  229. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_ridge_search.py +0 -0
  230. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_sterimol_b1_normal.py +0 -0
  231. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_sterimol_loc_b1.py +0 -0
  232. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_two_csv.py +0 -0
  233. {descripytor-0.3.1 → descripytor-0.3.2}/tests/test_validation.py +0 -0
  234. {descripytor-0.3.1 → descripytor-0.3.2}/utils/__init__.py +0 -0
  235. {descripytor-0.3.1 → descripytor-0.3.2}/utils/console.py +0 -0
  236. {descripytor-0.3.1 → descripytor-0.3.2}/utils/constants.py +0 -0
  237. {descripytor-0.3.1 → descripytor-0.3.2}/utils/file_handlers.py +0 -0
  238. {descripytor-0.3.1 → descripytor-0.3.2}/utils/help_functions.py +0 -0
  239. {descripytor-0.3.1 → descripytor-0.3.2}/utils/mode_viewer.html +0 -0
  240. {descripytor-0.3.1 → descripytor-0.3.2}/utils/mode_viewer.py +0 -0
  241. {descripytor-0.3.1 → descripytor-0.3.2}/utils/rdkit_utils.py +0 -0
  242. {descripytor-0.3.1 → descripytor-0.3.2}/utils/visualize.py +0 -0
@@ -241,7 +241,7 @@ def status():
241
241
  return jsonify({
242
242
  "ok": True,
243
243
  "version": "1.0",
244
- "api": 2,
244
+ "api": GUI_API,
245
245
  "example_feather_dir": example_feather_dir(),
246
246
  "example_basic_feather": example_basic_feather(),
247
247
  "example_reference_feather": example_reference_feather(),
@@ -275,12 +275,25 @@ def _listening(port):
275
275
  return s.connect_ex(("127.0.0.1", port)) == 0
276
276
 
277
277
 
278
+ def _bassa_running():
279
+ root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
280
+ if root_dir not in sys.path:
281
+ sys.path.insert(0, root_dir)
282
+ from M3_modeler.bassa_explorer import running
283
+ return running
284
+
285
+
278
286
  def _bassa_url():
279
287
  """The BASSA model explorer's address. It has its own server (it may run under another Python): started here if it
280
- is not up. None when it does not come up."""
288
+ is not up. None when it does not come up, or when the port is held by a server from before an update."""
281
289
  import subprocess
282
290
  import time
291
+ global BASSA_DOWN
283
292
  port = int(os.environ.get("BASSA_EXPLORER_PORT", "8765"))
293
+ BASSA_DOWN = BASSA_NOT_STARTED
294
+ if _bassa_running().state(port) == "old":
295
+ BASSA_DOWN = _bassa_running().stop_hint(port, "the BASSA model explorer")
296
+ return None
284
297
  if not _listening(port):
285
298
  root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
286
299
  env = dict(os.environ, PYTHONPATH=root_dir + os.pathsep + os.environ.get("PYTHONPATH", ""))
@@ -294,7 +307,8 @@ def _bassa_url():
294
307
  return f"http://127.0.0.1:{port}/"
295
308
 
296
309
 
297
- BASSA_DOWN = "The BASSA model explorer did not start; run `descripytor bassa_explorer` to see why."
310
+ BASSA_NOT_STARTED = "The BASSA model explorer did not start; run `descripytor bassa_explorer` to see why."
311
+ BASSA_DOWN = BASSA_NOT_STARTED # what the last failed _bassa_url() has to say
298
312
 
299
313
 
300
314
  @app.route("/bassa_explorer")
@@ -2253,7 +2267,10 @@ def _pd_from_numpy(arr, index, columns):
2253
2267
  return pd.DataFrame(arr, index=index, columns=columns)
2254
2268
 
2255
2269
 
2256
- GUI_API = 2
2270
+ # Raised whenever the pages need a route an older server does not have. A server still running from before then answers
2271
+ # /status with a lower number, and a new start tells the user to stop it instead of reusing it (its pages are read from
2272
+ # disk, so they would be new while its routes are old: the links to /feature_explorer and /bassa_explorer gave Not Found).
2273
+ GUI_API = 3
2257
2274
 
2258
2275
 
2259
2276
  def _tcp_port_open(port):
@@ -2280,9 +2297,7 @@ def _status_from_running_gui(port):
2280
2297
  def _gui_payload_is_current(payload):
2281
2298
  if not isinstance(payload, dict) or not payload.get("ok"):
2282
2299
  return False
2283
- if payload.get("api") == GUI_API:
2284
- return True
2285
- return "example_presets" in payload and "example_reference_name" in payload
2300
+ return payload.get("api") == GUI_API
2286
2301
 
2287
2302
 
2288
2303
  def _pids_listening_on_port(port):
@@ -2347,7 +2362,7 @@ def _reuse_or_refuse_existing_gui(host, port, open_browser, page="/visual"):
2347
2362
  if payload is not None or _tcp_port_open(port):
2348
2363
  pids = _pids_listening_on_port(port)
2349
2364
  print(f"\n Port {port} is already in use by an older or different process.")
2350
- print(" Stop every listener, then run descripytor visual again.")
2365
+ print(" It was started before the package was updated, so it lacks the newer pages. Stop it, then run the command again.")
2351
2366
  if pids:
2352
2367
  print(" PID(s): " + ", ".join(pids))
2353
2368
  if os.name == "nt":
@@ -0,0 +1,61 @@
1
+ """Is a BASSA explorer server already on a port, and is it this version's?
2
+
3
+ A server left running from before an update keeps its old code while the page it serves is read from disk, so
4
+ reusing it gives a new page on old routes. Everything that would reuse a running server asks here first.
5
+ Standard library only: the command line, the visual extractor's server and server.py all import it.
6
+ """
7
+ import json
8
+ import os
9
+ import socket
10
+ import subprocess
11
+ import urllib.request
12
+
13
+ API = 1 # raised whenever the page needs something an older server.py does not have
14
+ GUI_API_NEEDED = 3 # the lowest /status "api" of the visual extractor's server that has the pages this one links to
15
+
16
+
17
+ def listening(port):
18
+ with socket.socket() as s:
19
+ s.settimeout(0.3)
20
+ return s.connect_ex(("127.0.0.1", int(port))) == 0
21
+
22
+
23
+ def _status(port):
24
+ try:
25
+ return json.loads(urllib.request.urlopen("http://127.0.0.1:%d/status" % int(port), timeout=3).read())
26
+ except Exception:
27
+ return None # no /status: something else, or a server from before this check
28
+
29
+
30
+ def state(port):
31
+ """None: nothing listens on the port. "current": this version's BASSA explorer. "old": anything else."""
32
+ if not listening(port):
33
+ return None
34
+ d = _status(port)
35
+ return "current" if isinstance(d, dict) and d.get("app") == "bassa_explorer" and d.get("api") == API else "old"
36
+
37
+
38
+ def gui_state(port):
39
+ """the same for the visual extractor's server, seen from here: None, "current" or "old\""""
40
+ if not listening(port):
41
+ return None
42
+ d = _status(port)
43
+ return "current" if isinstance(d, dict) and d.get("ok") and int(d.get("api") or 0) >= GUI_API_NEEDED else "old"
44
+
45
+
46
+ def stop_hint(port, what):
47
+ """what to tell the user when the port is held by an older server"""
48
+ pids = []
49
+ try:
50
+ if os.name == "nt":
51
+ out = subprocess.run(["netstat", "-ano", "-p", "tcp"], capture_output=True, text=True, timeout=4).stdout
52
+ for p in (line.split() for line in out.splitlines()):
53
+ if len(p) >= 5 and p[0].upper() == "TCP" and "LISTEN" in p[3].upper() and p[1].endswith(":%d" % int(port)) and p[-1].isdigit() and p[-1] not in pids:
54
+ pids.append(p[-1])
55
+ else:
56
+ pids = subprocess.run(["lsof", "-iTCP:%d" % int(port), "-sTCP:LISTEN", "-n", "-P", "-t"], capture_output=True, text=True, timeout=4).stdout.split()
57
+ except Exception:
58
+ pass
59
+ how = ("taskkill /PID %s /F" if os.name == "nt" else "kill %s") % " ".join(pids) if pids else "stop the process that listens on that port"
60
+ return ("Port %d is held by an older or different process, not by this version of %s (it was probably started before the "
61
+ "package was updated). Stop it, then try again: %s" % (int(port), what, how))
@@ -10,6 +10,7 @@ POST /run {csv, response, iters, name, structures} -> the same data the pa
10
10
  POST /checks {csv, response} -> {job}; the reference lines and the fold counts are computed in the background
11
11
  POST /handoff {csv, response, structures, name} -> {id}; another page (the atom picker) leaves a table here
12
12
  GET /handoff?id=N -> that table, once: the page opened with #handoff=N runs it
13
+ GET /status -> {ok, app, api, sampler, cases}: lets a new start tell this server from an older one
13
14
  GET /checks?job=N -> {done, total, chance, nested_q2, fold_top, n_folds, error}
14
15
  POST /keep {name, case} -> writes <name>.js beside the page and lists it, so the case is there next time
15
16
  POST /export {cases} -> one HTML file holding the page, the explorer and those cases
@@ -28,6 +29,7 @@ from urllib.parse import parse_qs, urlparse
28
29
  import numpy as np, pandas as pd
29
30
 
30
31
  import build_case as BC
32
+ import running as RUN
31
33
 
32
34
  H = Path(__file__).resolve().parent; C = BC.C; LOCK = threading.Lock(); JOBS = {}; HANDOFF = {}; N_PERM, NULL_ITERS = 100, 6000
33
35
  READER = BC.S["reader_python"]
@@ -39,13 +41,14 @@ STYLE = dict(look="descriptors", preset="glossy", palette="cylview", ink="colour
39
41
  NEEDS = "new tables need the sampler: pip install bassa-reg into this Python, or start with --bassa-python <a Python that has it>" # the environment with pyarrow and the DescriPyTor readers
40
42
 
41
43
 
42
- def listening(port):
43
- with socket.socket() as s: s.settimeout(0.3); return s.connect_ex(("127.0.0.1", port)) == 0
44
+ listening = RUN.listening
44
45
 
45
46
 
46
47
  def gui(page):
47
- """the address of a page of the package's other server (the atom picker, the full feature explorer); that server is started if it is not up"""
48
+ """the address of a page of the package's other server (the atom picker, the full feature explorer); that server is
49
+ started if it is not up. None when the port is held by a server from before an update: its routes would be old."""
48
50
  port = int(os.environ.get("GUI_PORT", "7432"))
51
+ if RUN.gui_state(port) == "old": return None
49
52
  if not listening(port):
50
53
  env = dict(os.environ, PYTHONPATH=BC.S["descripytor"] + os.pathsep + os.environ.get("PYTHONPATH", ""))
51
54
  subprocess.Popen([READER, "-m", "descripytor.cli", "visual_extractor", "--no-browser", "--port", str(port)], env=env)
@@ -136,7 +139,11 @@ class Handler(SimpleHTTPRequestHandler):
136
139
  body = b'window.CASE_FILES = ["example"];\n'; self.send_response(200); self.send_header("Content-Type", "text/javascript"); self.send_header("Content-Length", str(len(body))); self.end_headers(); return self.wfile.write(body)
137
140
  if u.path in ("/visual_extractor", "/feature_explorer", "/bassa_explorer"): # the three pages link to each other by these names
138
141
  to = "/" if u.path == "/bassa_explorer" else gui("/visual" if u.path == "/visual_extractor" else u.path)
142
+ if to is None:
143
+ body = RUN.stop_hint(int(os.environ.get("GUI_PORT", "7432")), "the visual extractor").encode("utf-8")
144
+ self.send_response(503); self.send_header("Content-Type", "text/plain; charset=utf-8"); self.send_header("Content-Length", str(len(body))); self.end_headers(); return self.wfile.write(body)
139
145
  self.send_response(302); self.send_header("Location", to); self.send_header("Content-Length", "0"); return self.end_headers()
146
+ if u.path == "/status": return self.answer(dict(ok=True, app="bassa_explorer", api=RUN.API, sampler=SAMPLER, cases=str(C))) # who is on this port, and which version
140
147
  if u.path == "/handoff": q = HANDOFF.pop(parse_qs(u.query).get("id", [""])[0], None); return self.answer(q or dict(error="this table was already taken, or the server was restarted"), 200 if q else 404)
141
148
  if u.path != "/checks": return super().do_GET()
142
149
  job = JOBS.get(parse_qs(u.query).get("job", [""])[0]); self.answer(job or dict(error="no such job"), 200 if job else 404)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: descripytor
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Molecular feature extraction, alignment, and modeling tools.
5
5
  Author-email: Eden Specktor <edenspec@post.bgu.ac.il>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  """DescriPyTor: chemical-intuition-based molecular descriptors and modeling."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.3.2"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -74,7 +74,7 @@ def get_local_setup_version():
74
74
  from descripytor import __version__ as version
75
75
  return version
76
76
  except Exception:
77
- return "0.3.1"
77
+ return "0.3.2"
78
78
 
79
79
  __version__ = get_local_setup_version()
80
80
 
@@ -342,6 +342,17 @@ def run_bassa_explorer(cases=None, host=None, port=None, open_browser=True, bass
342
342
  url = f"http://{'127.0.0.1' if host in ('0.0.0.0', '') else host}:{port}/"
343
343
  print(f"\n BASSA model explorer\n Open: {url}\n Press Ctrl+C to stop\n", flush=True)
344
344
  python = bassa_python or os.environ.get("DESCRIPYTOR_BASSA_PYTHON") or sys.executable
345
+ _ensure_project_on_path()
346
+ from M3_modeler.bassa_explorer import running
347
+ already = running.state(port)
348
+ if already == "current": # this version's server is up: use it
349
+ print(" It is already running; this process will not start a second server." + chr(10), flush=True)
350
+ if open_browser:
351
+ webbrowser.open(url)
352
+ return 0
353
+ if already == "old": # a server from before an update would give a new page on old routes
354
+ print(" " + running.stop_hint(port, "the BASSA model explorer") + chr(10), flush=True)
355
+ return 1
345
356
 
346
357
  def open_when_up(): # the first import of the sampler can take a while
347
358
  import socket
@@ -628,6 +639,6 @@ def main():
628
639
  parser.print_help()
629
640
 
630
641
  if __name__ == "__main__":
631
- main()
642
+ sys.exit(main()) # as the installed command does: a refused start exits with 1
632
643
 
633
644
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: descripytor
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Molecular feature extraction, alignment, and modeling tools.
5
5
  Author-email: Eden Specktor <edenspec@post.bgu.ac.il>
6
6
  License-Expression: MIT
@@ -150,6 +150,7 @@ M3_modeler/bassa_explorer/example_small.csv
150
150
  M3_modeler/bassa_explorer/index.html
151
151
  M3_modeler/bassa_explorer/influence_check.py
152
152
  M3_modeler/bassa_explorer/read_structures.py
153
+ M3_modeler/bassa_explorer/running.py
153
154
  M3_modeler/bassa_explorer/server.py
154
155
  M3_modeler/bassa_explorer/settings.py
155
156
  M3_modeler/bassa_explorer/test_page.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "descripytor"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Molecular feature extraction, alignment, and modeling tools."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -86,38 +86,92 @@ def test_the_three_pages_have_one_address_each(server):
86
86
  assert 'href="/feature_explorer"' in picker and 'href="/bassa_explorer"' in picker
87
87
  explorer = (ROOT / "M2_data_extractor" / "feature_explorer" / "template.html").read_text(encoding="utf-8")
88
88
  assert 'href="/visual_extractor"' in explorer and 'href="/bassa_explorer"' in explorer
89
-
90
-
91
- def test_a_table_left_by_another_page_is_taken_once(server):
92
- """The picker's one-click hand-off: POST /handoff leaves a table, the page opened with #handoff=id takes it."""
93
- url, _ = server
94
- body = dict(csv=(TOOL / "example_small.csv").read_text(), response="ddG", structures="", name="extracted")
95
- q = urllib.request.Request(url + "handoff", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})
96
- token = json.loads(urllib.request.urlopen(q, timeout=30).read())["id"]
97
- got = json.loads(_get(url + "handoff?id=" + token))
98
- assert got["response"] == "ddG" and got["csv"] == body["csv"]
99
- with pytest.raises(urllib.error.HTTPError) as e:
100
- _get(url + "handoff?id=" + token)
101
- assert e.value.code == 404
102
- bad = urllib.request.Request(url + "handoff", data=json.dumps(dict(csv="name,a,y\nm1,1,2\n", response="y")).encode(), headers={"Content-Type": "application/json"})
103
- with pytest.raises(urllib.error.HTTPError) as e: # a table a run would refuse is refused at the door
104
- urllib.request.urlopen(bad, timeout=30)
105
- assert "at least 6 compounds" in json.loads(e.value.read())["error"]
106
-
107
-
108
- def test_the_extractor_s_own_column_names_are_drawn():
109
- """A table straight from `descripytor extractor` / the visual extractor: its dipole, Sterimol and vibration columns map to drawings."""
110
- code = (
111
- "import build_case as B\n"
112
- "want = {'dipole_x_1-2-19': 'dipole', 'total_dipole_1-2-19': 'dipole_total', 'loc_B1_1-4': 'sterimol', 'B1_B5_azimuth_4-1': 'sterimol',\n"
113
- " 'B1_B5_acute_1-4': 'sterimol', 'Frequency_Stretch_1_2': 'mark', 'Frequency_Bending_5-6': 'mark', 'Cross_mag_Bending_5-6': 'mark',\n"
114
- " 'cross_9_0': 'mark', 'cross_angle_9_0': 'mark', 'para_9_0': 'mark', 'para_angle_9_0': 'mark', 'B1_1-4': 'sterimol',\n"
115
- " '0_bond_length_1-2': 'bond_length', 'nbo_atom_3': 'charge', 'nbo_diff_3-5': 'charge_diff'}\n"
116
- "for c, k in want.items():\n"
117
- " w = B.drawing(c); assert w and w['kind'] == k, (c, w)\n"
118
- " assert B.label(c, w) != c and B.describe(c, w)\n"
119
- "d = B.drawing('dipole_y_20-19-27'); assert (d['origin'], d['y'], d['plane'], d['comp']) == (20, 19, 27, 'u')\n"
120
- "assert B.drawing('cross_9_0')['atoms'] == [9] and B.drawing('aniso') is None and B.label('aniso', None) != 'aniso'\n"
121
- )
122
- r = subprocess.run([sys.executable, "-c", code], cwd=str(TOOL), capture_output=True, text=True)
123
- assert r.returncode == 0, r.stderr[-600:]
89
+
90
+
91
+ def test_a_table_left_by_another_page_is_taken_once(server):
92
+ """The picker's one-click hand-off: POST /handoff leaves a table, the page opened with #handoff=id takes it."""
93
+ url, _ = server
94
+ body = dict(csv=(TOOL / "example_small.csv").read_text(), response="ddG", structures="", name="extracted")
95
+ q = urllib.request.Request(url + "handoff", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})
96
+ token = json.loads(urllib.request.urlopen(q, timeout=30).read())["id"]
97
+ got = json.loads(_get(url + "handoff?id=" + token))
98
+ assert got["response"] == "ddG" and got["csv"] == body["csv"]
99
+ with pytest.raises(urllib.error.HTTPError) as e:
100
+ _get(url + "handoff?id=" + token)
101
+ assert e.value.code == 404
102
+ bad = urllib.request.Request(url + "handoff", data=json.dumps(dict(csv="name,a,y\nm1,1,2\n", response="y")).encode(), headers={"Content-Type": "application/json"})
103
+ with pytest.raises(urllib.error.HTTPError) as e: # a table a run would refuse is refused at the door
104
+ urllib.request.urlopen(bad, timeout=30)
105
+ assert "at least 6 compounds" in json.loads(e.value.read())["error"]
106
+
107
+
108
+ def test_the_extractor_s_own_column_names_are_drawn():
109
+ """A table straight from `descripytor extractor` / the visual extractor: its dipole, Sterimol and vibration columns map to drawings."""
110
+ code = (
111
+ "import build_case as B\n"
112
+ "want = {'dipole_x_1-2-19': 'dipole', 'total_dipole_1-2-19': 'dipole_total', 'loc_B1_1-4': 'sterimol', 'B1_B5_azimuth_4-1': 'sterimol',\n"
113
+ " 'B1_B5_acute_1-4': 'sterimol', 'Frequency_Stretch_1_2': 'mark', 'Frequency_Bending_5-6': 'mark', 'Cross_mag_Bending_5-6': 'mark',\n"
114
+ " 'cross_9_0': 'mark', 'cross_angle_9_0': 'mark', 'para_9_0': 'mark', 'para_angle_9_0': 'mark', 'B1_1-4': 'sterimol',\n"
115
+ " '0_bond_length_1-2': 'bond_length', 'nbo_atom_3': 'charge', 'nbo_diff_3-5': 'charge_diff'}\n"
116
+ "for c, k in want.items():\n"
117
+ " w = B.drawing(c); assert w and w['kind'] == k, (c, w)\n"
118
+ " assert B.label(c, w) != c and B.describe(c, w)\n"
119
+ "d = B.drawing('dipole_y_20-19-27'); assert (d['origin'], d['y'], d['plane'], d['comp']) == (20, 19, 27, 'u')\n"
120
+ "assert B.drawing('cross_9_0')['atoms'] == [9] and B.drawing('aniso') is None and B.label('aniso', None) != 'aniso'\n"
121
+ )
122
+ r = subprocess.run([sys.executable, "-c", code], cwd=str(TOOL), capture_output=True, text=True)
123
+ assert r.returncode == 0, r.stderr[-600:]
124
+
125
+
126
+ @pytest.fixture()
127
+ def other_server():
128
+ """Something else on a port: no /status of ours, as a BASSA explorer from before the check, or any other program."""
129
+ with socket.socket() as s:
130
+ s.bind(("127.0.0.1", 0)); port = s.getsockname()[1]
131
+ p = subprocess.Popen([sys.executable, "-m", "http.server", str(port), "--bind", "127.0.0.1"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
132
+ try:
133
+ for _ in range(60):
134
+ try: urllib.request.urlopen(f"http://127.0.0.1:{port}/", timeout=2); break
135
+ except Exception: time.sleep(0.25)
136
+ yield port
137
+ finally:
138
+ p.kill()
139
+
140
+
141
+ def _running():
142
+ sys.path.insert(0, str(ROOT))
143
+ from M3_modeler.bassa_explorer import running
144
+ return running
145
+
146
+
147
+ def test_a_running_server_says_which_version_it_is(server):
148
+ url, _ = server
149
+ running = _running()
150
+ st = json.loads(_get(url + "status"))
151
+ assert st["app"] == "bassa_explorer" and st["api"] == running.API
152
+ port = int(url.rsplit(":", 1)[1].strip("/"))
153
+ assert running.state(port) == "current"
154
+ with socket.socket() as s:
155
+ s.bind(("127.0.0.1", 0)); free = s.getsockname()[1]
156
+ assert running.state(free) is None
157
+
158
+
159
+ def test_an_older_server_on_the_port_is_not_reused(other_server):
160
+ """A server left from before an update would serve the new page on old routes; every way in refuses it and says how to stop it."""
161
+ running = _running()
162
+ assert running.state(other_server) == "old"
163
+ assert str(other_server) in running.stop_hint(other_server, "the BASSA model explorer")
164
+ # the command
165
+ r = subprocess.run([sys.executable, "-m", "descripytor.cli", "bassa_explorer", "--no-browser", "--port", str(other_server)],
166
+ cwd=str(ROOT), capture_output=True, text=True, timeout=120)
167
+ assert r.returncode == 1 and "older or different process" in r.stdout
168
+ # the visual extractor's link and its one-click hand-off
169
+ env = dict(os.environ, BASSA_EXPLORER_PORT=str(other_server))
170
+ code = ("import sys; sys.path.insert(0, %r)\n"
171
+ "import M2_data_extractor.gui_server as gs\n"
172
+ "c = gs.app.test_client()\n"
173
+ "r = c.get('/bassa_explorer'); assert r.status_code == 503 and b'older or different process' in r.data, r.data\n"
174
+ "csv = 'name,a,b,c,d,y' + chr(10) + chr(10).join('m%%d,%%d,%%d,%%d,%%d,%%d' %% (i, i, i * i %% 7, i * 3 %% 5, i * 5 %% 11, i %% 4) for i in range(8))\n"
175
+ "r = c.post('/bassa_explorer/handoff', json={'csv': csv, 'y_column': 'y'}); assert r.status_code == 503 and 'older' in r.get_json()['error'], r.data\n") % str(ROOT)
176
+ r = subprocess.run([sys.executable, "-c", code], env=env, capture_output=True, text=True, timeout=120)
177
+ assert r.returncode == 0, r.stderr[-800:]
@@ -40,7 +40,8 @@ def test_status(client):
40
40
  assert response.status_code == 200
41
41
  payload = response.get_json()
42
42
  assert payload["ok"] is True
43
- assert payload.get("api") == 2
43
+ from M2_data_extractor.gui_server import GUI_API
44
+ assert payload.get("api") == GUI_API
44
45
  assert "example_feather_dir" in payload
45
46
  assert "example_outcomes" in payload
46
47
  assert "example_presets" in payload
@@ -625,15 +626,11 @@ def test_placeholder_path_ignores_double_underscore_in_real_paths():
625
626
 
626
627
 
627
628
  def test_gui_payload_is_current():
628
- from M2_data_extractor.gui_server import _gui_payload_is_current
629
+ from M2_data_extractor.gui_server import GUI_API, _gui_payload_is_current
629
630
 
630
631
  assert not _gui_payload_is_current({"ok": True, "version": "1.0"})
631
- assert _gui_payload_is_current({"ok": True, "api": 2})
632
- assert _gui_payload_is_current({
633
- "ok": True,
634
- "example_presets": {},
635
- "example_reference_name": "basic",
636
- })
632
+ assert _gui_payload_is_current({"ok": True, "api": GUI_API})
633
+ assert not _gui_payload_is_current({"ok": True, "api": GUI_API - 1}) # a server from before the last update
637
634
  assert not _gui_payload_is_current(None)
638
635
 
639
636
 
@@ -740,23 +737,33 @@ def test_the_old_explorer_folder_still_imports():
740
737
  def test_visual_extractor_address_is_the_picker():
741
738
  r = _load_app().test_client().get("/visual_extractor")
742
739
  assert r.status_code == 302 and r.headers["Location"].endswith("/visual")
743
-
744
-
745
- def test_handoff_needs_a_table_and_a_target():
746
- client = _load_app().test_client()
747
- assert "Extract features first" in client.post("/bassa_explorer/handoff", json={}).get_json()["error"]
748
- r = client.post("/bassa_explorer/handoff", json={"csv": "name,a\nm1,1\nm2,2\n"})
749
- assert r.status_code == 400 and "target" in r.get_json()["error"]
750
-
751
-
752
- def test_the_extraction_engine_is_inside_the_package():
753
- """Extract CSV runs descriptor_extractor; an installed package has no examples folder, so it must sit in M2_data_extractor."""
754
- app = _load_app()
755
- import M2_data_extractor.gui_server as gs
756
- dx = gs._load_descriptor_extractor()
757
- assert Path(dx.__file__).resolve().parent == ROOT / "M2_data_extractor"
758
- assert (ROOT / "M2_data_extractor" / "feature_packages.py").is_file()
759
- import subprocess, sys
760
- old = ROOT / "Getting_started_with_examples" / "descriptor_extraction_toolkit"
761
- code = "import sys; sys.path.insert(0, %r); import descriptor_extractor as d, feature_packages as f; assert d.run_from_config and f" % str(old)
762
- assert subprocess.run([sys.executable, "-c", code]).returncode == 0 # the toolkit's old import path still works
740
+
741
+
742
+ def test_handoff_needs_a_table_and_a_target():
743
+ client = _load_app().test_client()
744
+ assert "Extract features first" in client.post("/bassa_explorer/handoff", json={}).get_json()["error"]
745
+ r = client.post("/bassa_explorer/handoff", json={"csv": "name,a\nm1,1\nm2,2\n"})
746
+ assert r.status_code == 400 and "target" in r.get_json()["error"]
747
+
748
+
749
+ def test_the_extraction_engine_is_inside_the_package():
750
+ """Extract CSV runs descriptor_extractor; an installed package has no examples folder, so it must sit in M2_data_extractor."""
751
+ app = _load_app()
752
+ import M2_data_extractor.gui_server as gs
753
+ dx = gs._load_descriptor_extractor()
754
+ assert Path(dx.__file__).resolve().parent == ROOT / "M2_data_extractor"
755
+ assert (ROOT / "M2_data_extractor" / "feature_packages.py").is_file()
756
+ import subprocess, sys
757
+ old = ROOT / "Getting_started_with_examples" / "descriptor_extraction_toolkit"
758
+ code = "import sys; sys.path.insert(0, %r); import descriptor_extractor as d, feature_packages as f; assert d.run_from_config and f" % str(old)
759
+ assert subprocess.run([sys.executable, "-c", code]).returncode == 0 # the toolkit's old import path still works
760
+
761
+
762
+ def test_a_server_from_before_the_update_is_not_reused():
763
+ """Its pages are read from disk (new) while its routes are old: the header links gave Not Found."""
764
+ _load_app()
765
+ import M2_data_extractor.gui_server as gs
766
+ old = {"ok": True, "api": 2, "example_presets": {}, "example_reference_name": "basic"}
767
+ assert not gs._gui_payload_is_current(old)
768
+ assert gs._gui_payload_is_current({"ok": True, "api": gs.GUI_API})
769
+ assert _load_app().test_client().get("/status").get_json()["api"] == gs.GUI_API
File without changes