setiastrosuitepro 1.6.7__py3-none-any.whl

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 setiastrosuitepro might be problematic. Click here for more details.

Files changed (394) hide show
  1. setiastro/__init__.py +2 -0
  2. setiastro/data/SASP_data.fits +0 -0
  3. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  4. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  5. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  6. setiastro/data/catalogs/cali2.csv +63 -0
  7. setiastro/data/catalogs/cali2color.csv +65 -0
  8. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  9. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  10. setiastro/data/catalogs/detected_stars.csv +24784 -0
  11. setiastro/data/catalogs/fits_header_data.csv +46 -0
  12. setiastro/data/catalogs/test.csv +8 -0
  13. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  14. setiastro/images/Astro_Spikes.png +0 -0
  15. setiastro/images/Background_startup.jpg +0 -0
  16. setiastro/images/HRDiagram.png +0 -0
  17. setiastro/images/LExtract.png +0 -0
  18. setiastro/images/LInsert.png +0 -0
  19. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  20. setiastro/images/RGB080604.png +0 -0
  21. setiastro/images/abeicon.png +0 -0
  22. setiastro/images/aberration.png +0 -0
  23. setiastro/images/acv_icon.png +0 -0
  24. setiastro/images/andromedatry.png +0 -0
  25. setiastro/images/andromedatry_satellited.png +0 -0
  26. setiastro/images/annotated.png +0 -0
  27. setiastro/images/aperture.png +0 -0
  28. setiastro/images/astrosuite.ico +0 -0
  29. setiastro/images/astrosuite.png +0 -0
  30. setiastro/images/astrosuitepro.icns +0 -0
  31. setiastro/images/astrosuitepro.ico +0 -0
  32. setiastro/images/astrosuitepro.png +0 -0
  33. setiastro/images/background.png +0 -0
  34. setiastro/images/background2.png +0 -0
  35. setiastro/images/benchmark.png +0 -0
  36. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  37. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  38. setiastro/images/blaster.png +0 -0
  39. setiastro/images/blink.png +0 -0
  40. setiastro/images/clahe.png +0 -0
  41. setiastro/images/collage.png +0 -0
  42. setiastro/images/colorwheel.png +0 -0
  43. setiastro/images/contsub.png +0 -0
  44. setiastro/images/convo.png +0 -0
  45. setiastro/images/copyslot.png +0 -0
  46. setiastro/images/cosmic.png +0 -0
  47. setiastro/images/cosmicsat.png +0 -0
  48. setiastro/images/crop1.png +0 -0
  49. setiastro/images/cropicon.png +0 -0
  50. setiastro/images/curves.png +0 -0
  51. setiastro/images/cvs.png +0 -0
  52. setiastro/images/debayer.png +0 -0
  53. setiastro/images/denoise_cnn_custom.png +0 -0
  54. setiastro/images/denoise_cnn_graph.png +0 -0
  55. setiastro/images/disk.png +0 -0
  56. setiastro/images/dse.png +0 -0
  57. setiastro/images/exoicon.png +0 -0
  58. setiastro/images/eye.png +0 -0
  59. setiastro/images/first_quarter.png +0 -0
  60. setiastro/images/fliphorizontal.png +0 -0
  61. setiastro/images/flipvertical.png +0 -0
  62. setiastro/images/font.png +0 -0
  63. setiastro/images/freqsep.png +0 -0
  64. setiastro/images/full_moon.png +0 -0
  65. setiastro/images/functionbundle.png +0 -0
  66. setiastro/images/graxpert.png +0 -0
  67. setiastro/images/green.png +0 -0
  68. setiastro/images/gridicon.png +0 -0
  69. setiastro/images/halo.png +0 -0
  70. setiastro/images/hdr.png +0 -0
  71. setiastro/images/histogram.png +0 -0
  72. setiastro/images/hubble.png +0 -0
  73. setiastro/images/imagecombine.png +0 -0
  74. setiastro/images/invert.png +0 -0
  75. setiastro/images/isophote.png +0 -0
  76. setiastro/images/isophote_demo_figure.png +0 -0
  77. setiastro/images/isophote_demo_image.png +0 -0
  78. setiastro/images/isophote_demo_model.png +0 -0
  79. setiastro/images/isophote_demo_residual.png +0 -0
  80. setiastro/images/jwstpupil.png +0 -0
  81. setiastro/images/last_quarter.png +0 -0
  82. setiastro/images/linearfit.png +0 -0
  83. setiastro/images/livestacking.png +0 -0
  84. setiastro/images/mask.png +0 -0
  85. setiastro/images/maskapply.png +0 -0
  86. setiastro/images/maskcreate.png +0 -0
  87. setiastro/images/maskremove.png +0 -0
  88. setiastro/images/morpho.png +0 -0
  89. setiastro/images/mosaic.png +0 -0
  90. setiastro/images/multiscale_decomp.png +0 -0
  91. setiastro/images/nbtorgb.png +0 -0
  92. setiastro/images/neutral.png +0 -0
  93. setiastro/images/new_moon.png +0 -0
  94. setiastro/images/nuke.png +0 -0
  95. setiastro/images/openfile.png +0 -0
  96. setiastro/images/pedestal.png +0 -0
  97. setiastro/images/pen.png +0 -0
  98. setiastro/images/pixelmath.png +0 -0
  99. setiastro/images/platesolve.png +0 -0
  100. setiastro/images/ppp.png +0 -0
  101. setiastro/images/pro.png +0 -0
  102. setiastro/images/project.png +0 -0
  103. setiastro/images/psf.png +0 -0
  104. setiastro/images/redo.png +0 -0
  105. setiastro/images/redoicon.png +0 -0
  106. setiastro/images/rescale.png +0 -0
  107. setiastro/images/rgbalign.png +0 -0
  108. setiastro/images/rgbcombo.png +0 -0
  109. setiastro/images/rgbextract.png +0 -0
  110. setiastro/images/rotate180.png +0 -0
  111. setiastro/images/rotatearbitrary.png +0 -0
  112. setiastro/images/rotateclockwise.png +0 -0
  113. setiastro/images/rotatecounterclockwise.png +0 -0
  114. setiastro/images/satellite.png +0 -0
  115. setiastro/images/script.png +0 -0
  116. setiastro/images/selectivecolor.png +0 -0
  117. setiastro/images/simbad.png +0 -0
  118. setiastro/images/slot0.png +0 -0
  119. setiastro/images/slot1.png +0 -0
  120. setiastro/images/slot2.png +0 -0
  121. setiastro/images/slot3.png +0 -0
  122. setiastro/images/slot4.png +0 -0
  123. setiastro/images/slot5.png +0 -0
  124. setiastro/images/slot6.png +0 -0
  125. setiastro/images/slot7.png +0 -0
  126. setiastro/images/slot8.png +0 -0
  127. setiastro/images/slot9.png +0 -0
  128. setiastro/images/spcc.png +0 -0
  129. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  130. setiastro/images/spinner.gif +0 -0
  131. setiastro/images/stacking.png +0 -0
  132. setiastro/images/staradd.png +0 -0
  133. setiastro/images/staralign.png +0 -0
  134. setiastro/images/starnet.png +0 -0
  135. setiastro/images/starregistration.png +0 -0
  136. setiastro/images/starspike.png +0 -0
  137. setiastro/images/starstretch.png +0 -0
  138. setiastro/images/statstretch.png +0 -0
  139. setiastro/images/supernova.png +0 -0
  140. setiastro/images/uhs.png +0 -0
  141. setiastro/images/undoicon.png +0 -0
  142. setiastro/images/upscale.png +0 -0
  143. setiastro/images/viewbundle.png +0 -0
  144. setiastro/images/waning_crescent_1.png +0 -0
  145. setiastro/images/waning_crescent_2.png +0 -0
  146. setiastro/images/waning_crescent_3.png +0 -0
  147. setiastro/images/waning_crescent_4.png +0 -0
  148. setiastro/images/waning_crescent_5.png +0 -0
  149. setiastro/images/waning_gibbous_1.png +0 -0
  150. setiastro/images/waning_gibbous_2.png +0 -0
  151. setiastro/images/waning_gibbous_3.png +0 -0
  152. setiastro/images/waning_gibbous_4.png +0 -0
  153. setiastro/images/waning_gibbous_5.png +0 -0
  154. setiastro/images/waxing_crescent_1.png +0 -0
  155. setiastro/images/waxing_crescent_2.png +0 -0
  156. setiastro/images/waxing_crescent_3.png +0 -0
  157. setiastro/images/waxing_crescent_4.png +0 -0
  158. setiastro/images/waxing_crescent_5.png +0 -0
  159. setiastro/images/waxing_gibbous_1.png +0 -0
  160. setiastro/images/waxing_gibbous_2.png +0 -0
  161. setiastro/images/waxing_gibbous_3.png +0 -0
  162. setiastro/images/waxing_gibbous_4.png +0 -0
  163. setiastro/images/waxing_gibbous_5.png +0 -0
  164. setiastro/images/whitebalance.png +0 -0
  165. setiastro/images/wimi_icon_256x256.png +0 -0
  166. setiastro/images/wimilogo.png +0 -0
  167. setiastro/images/wims.png +0 -0
  168. setiastro/images/wrench_icon.png +0 -0
  169. setiastro/images/xisfliberator.png +0 -0
  170. setiastro/qml/ResourceMonitor.qml +128 -0
  171. setiastro/saspro/__init__.py +20 -0
  172. setiastro/saspro/__main__.py +964 -0
  173. setiastro/saspro/_generated/__init__.py +7 -0
  174. setiastro/saspro/_generated/build_info.py +3 -0
  175. setiastro/saspro/abe.py +1379 -0
  176. setiastro/saspro/abe_preset.py +196 -0
  177. setiastro/saspro/aberration_ai.py +910 -0
  178. setiastro/saspro/aberration_ai_preset.py +224 -0
  179. setiastro/saspro/accel_installer.py +218 -0
  180. setiastro/saspro/accel_workers.py +30 -0
  181. setiastro/saspro/acv_exporter.py +379 -0
  182. setiastro/saspro/add_stars.py +627 -0
  183. setiastro/saspro/astrobin_exporter.py +1010 -0
  184. setiastro/saspro/astrospike.py +153 -0
  185. setiastro/saspro/astrospike_python.py +1841 -0
  186. setiastro/saspro/autostretch.py +198 -0
  187. setiastro/saspro/backgroundneutral.py +639 -0
  188. setiastro/saspro/batch_convert.py +328 -0
  189. setiastro/saspro/batch_renamer.py +522 -0
  190. setiastro/saspro/blemish_blaster.py +494 -0
  191. setiastro/saspro/blink_comparator_pro.py +3149 -0
  192. setiastro/saspro/bundles.py +61 -0
  193. setiastro/saspro/bundles_dock.py +114 -0
  194. setiastro/saspro/cheat_sheet.py +213 -0
  195. setiastro/saspro/clahe.py +371 -0
  196. setiastro/saspro/comet_stacking.py +1442 -0
  197. setiastro/saspro/common_tr.py +107 -0
  198. setiastro/saspro/config.py +38 -0
  199. setiastro/saspro/config_bootstrap.py +40 -0
  200. setiastro/saspro/config_manager.py +316 -0
  201. setiastro/saspro/continuum_subtract.py +1620 -0
  202. setiastro/saspro/convo.py +1403 -0
  203. setiastro/saspro/convo_preset.py +414 -0
  204. setiastro/saspro/copyastro.py +190 -0
  205. setiastro/saspro/cosmicclarity.py +1593 -0
  206. setiastro/saspro/cosmicclarity_preset.py +407 -0
  207. setiastro/saspro/crop_dialog_pro.py +1005 -0
  208. setiastro/saspro/crop_preset.py +189 -0
  209. setiastro/saspro/curve_editor_pro.py +2608 -0
  210. setiastro/saspro/curves_preset.py +375 -0
  211. setiastro/saspro/debayer.py +673 -0
  212. setiastro/saspro/debug_utils.py +29 -0
  213. setiastro/saspro/dnd_mime.py +35 -0
  214. setiastro/saspro/doc_manager.py +2727 -0
  215. setiastro/saspro/exoplanet_detector.py +2258 -0
  216. setiastro/saspro/file_utils.py +284 -0
  217. setiastro/saspro/fitsmodifier.py +748 -0
  218. setiastro/saspro/fix_bom.py +32 -0
  219. setiastro/saspro/free_torch_memory.py +48 -0
  220. setiastro/saspro/frequency_separation.py +1352 -0
  221. setiastro/saspro/function_bundle.py +1596 -0
  222. setiastro/saspro/generate_translations.py +3092 -0
  223. setiastro/saspro/ghs_dialog_pro.py +728 -0
  224. setiastro/saspro/ghs_preset.py +284 -0
  225. setiastro/saspro/graxpert.py +638 -0
  226. setiastro/saspro/graxpert_preset.py +287 -0
  227. setiastro/saspro/gui/__init__.py +0 -0
  228. setiastro/saspro/gui/main_window.py +8928 -0
  229. setiastro/saspro/gui/mixins/__init__.py +33 -0
  230. setiastro/saspro/gui/mixins/dock_mixin.py +375 -0
  231. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  232. setiastro/saspro/gui/mixins/geometry_mixin.py +503 -0
  233. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  234. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  235. setiastro/saspro/gui/mixins/menu_mixin.py +391 -0
  236. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  237. setiastro/saspro/gui/mixins/toolbar_mixin.py +1824 -0
  238. setiastro/saspro/gui/mixins/update_mixin.py +323 -0
  239. setiastro/saspro/gui/mixins/view_mixin.py +477 -0
  240. setiastro/saspro/gui/statistics_dialog.py +47 -0
  241. setiastro/saspro/halobgon.py +492 -0
  242. setiastro/saspro/header_viewer.py +448 -0
  243. setiastro/saspro/headless_utils.py +88 -0
  244. setiastro/saspro/histogram.py +760 -0
  245. setiastro/saspro/history_explorer.py +941 -0
  246. setiastro/saspro/i18n.py +168 -0
  247. setiastro/saspro/image_combine.py +421 -0
  248. setiastro/saspro/image_peeker_pro.py +1608 -0
  249. setiastro/saspro/imageops/__init__.py +37 -0
  250. setiastro/saspro/imageops/mdi_snap.py +292 -0
  251. setiastro/saspro/imageops/scnr.py +36 -0
  252. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  253. setiastro/saspro/imageops/stretch.py +236 -0
  254. setiastro/saspro/isophote.py +1186 -0
  255. setiastro/saspro/layers.py +208 -0
  256. setiastro/saspro/layers_dock.py +714 -0
  257. setiastro/saspro/lazy_imports.py +193 -0
  258. setiastro/saspro/legacy/__init__.py +2 -0
  259. setiastro/saspro/legacy/image_manager.py +2360 -0
  260. setiastro/saspro/legacy/numba_utils.py +3676 -0
  261. setiastro/saspro/legacy/xisf.py +1213 -0
  262. setiastro/saspro/linear_fit.py +537 -0
  263. setiastro/saspro/live_stacking.py +1854 -0
  264. setiastro/saspro/log_bus.py +5 -0
  265. setiastro/saspro/logging_config.py +460 -0
  266. setiastro/saspro/luminancerecombine.py +510 -0
  267. setiastro/saspro/main_helpers.py +201 -0
  268. setiastro/saspro/mask_creation.py +1090 -0
  269. setiastro/saspro/masks_core.py +56 -0
  270. setiastro/saspro/mdi_widgets.py +353 -0
  271. setiastro/saspro/memory_utils.py +666 -0
  272. setiastro/saspro/metadata_patcher.py +75 -0
  273. setiastro/saspro/mfdeconv.py +3909 -0
  274. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  275. setiastro/saspro/mfdeconvcudnn.py +3312 -0
  276. setiastro/saspro/mfdeconvsport.py +2459 -0
  277. setiastro/saspro/minorbodycatalog.py +567 -0
  278. setiastro/saspro/morphology.py +411 -0
  279. setiastro/saspro/multiscale_decomp.py +1751 -0
  280. setiastro/saspro/nbtorgb_stars.py +541 -0
  281. setiastro/saspro/numba_utils.py +3145 -0
  282. setiastro/saspro/numba_warmup.py +141 -0
  283. setiastro/saspro/ops/__init__.py +9 -0
  284. setiastro/saspro/ops/command_help_dialog.py +623 -0
  285. setiastro/saspro/ops/command_runner.py +217 -0
  286. setiastro/saspro/ops/commands.py +1594 -0
  287. setiastro/saspro/ops/script_editor.py +1105 -0
  288. setiastro/saspro/ops/scripts.py +1476 -0
  289. setiastro/saspro/ops/settings.py +637 -0
  290. setiastro/saspro/parallel_utils.py +554 -0
  291. setiastro/saspro/pedestal.py +121 -0
  292. setiastro/saspro/perfect_palette_picker.py +1105 -0
  293. setiastro/saspro/pipeline.py +110 -0
  294. setiastro/saspro/pixelmath.py +1604 -0
  295. setiastro/saspro/plate_solver.py +2480 -0
  296. setiastro/saspro/project_io.py +797 -0
  297. setiastro/saspro/psf_utils.py +136 -0
  298. setiastro/saspro/psf_viewer.py +631 -0
  299. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  300. setiastro/saspro/remove_green.py +331 -0
  301. setiastro/saspro/remove_stars.py +1599 -0
  302. setiastro/saspro/remove_stars_preset.py +446 -0
  303. setiastro/saspro/resources.py +570 -0
  304. setiastro/saspro/rgb_combination.py +208 -0
  305. setiastro/saspro/rgb_extract.py +19 -0
  306. setiastro/saspro/rgbalign.py +727 -0
  307. setiastro/saspro/runtime_imports.py +7 -0
  308. setiastro/saspro/runtime_torch.py +754 -0
  309. setiastro/saspro/save_options.py +73 -0
  310. setiastro/saspro/selective_color.py +1614 -0
  311. setiastro/saspro/sfcc.py +1530 -0
  312. setiastro/saspro/shortcuts.py +3125 -0
  313. setiastro/saspro/signature_insert.py +1106 -0
  314. setiastro/saspro/stacking_suite.py +19069 -0
  315. setiastro/saspro/star_alignment.py +7383 -0
  316. setiastro/saspro/star_alignment_preset.py +329 -0
  317. setiastro/saspro/star_metrics.py +49 -0
  318. setiastro/saspro/star_spikes.py +769 -0
  319. setiastro/saspro/star_stretch.py +542 -0
  320. setiastro/saspro/stat_stretch.py +554 -0
  321. setiastro/saspro/status_log_dock.py +78 -0
  322. setiastro/saspro/subwindow.py +3523 -0
  323. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  324. setiastro/saspro/swap_manager.py +134 -0
  325. setiastro/saspro/torch_backend.py +89 -0
  326. setiastro/saspro/torch_rejection.py +434 -0
  327. setiastro/saspro/translations/all_source_strings.json +4726 -0
  328. setiastro/saspro/translations/ar_translations.py +4096 -0
  329. setiastro/saspro/translations/de_translations.py +3728 -0
  330. setiastro/saspro/translations/es_translations.py +4169 -0
  331. setiastro/saspro/translations/fr_translations.py +4090 -0
  332. setiastro/saspro/translations/hi_translations.py +3803 -0
  333. setiastro/saspro/translations/integrate_translations.py +271 -0
  334. setiastro/saspro/translations/it_translations.py +4728 -0
  335. setiastro/saspro/translations/ja_translations.py +3834 -0
  336. setiastro/saspro/translations/pt_translations.py +3847 -0
  337. setiastro/saspro/translations/ru_translations.py +3082 -0
  338. setiastro/saspro/translations/saspro_ar.qm +0 -0
  339. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  340. setiastro/saspro/translations/saspro_de.qm +0 -0
  341. setiastro/saspro/translations/saspro_de.ts +14548 -0
  342. setiastro/saspro/translations/saspro_es.qm +0 -0
  343. setiastro/saspro/translations/saspro_es.ts +16202 -0
  344. setiastro/saspro/translations/saspro_fr.qm +0 -0
  345. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  346. setiastro/saspro/translations/saspro_hi.qm +0 -0
  347. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  348. setiastro/saspro/translations/saspro_it.qm +0 -0
  349. setiastro/saspro/translations/saspro_it.ts +19046 -0
  350. setiastro/saspro/translations/saspro_ja.qm +0 -0
  351. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  352. setiastro/saspro/translations/saspro_pt.qm +0 -0
  353. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  354. setiastro/saspro/translations/saspro_ru.qm +0 -0
  355. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  356. setiastro/saspro/translations/saspro_sw.qm +0 -0
  357. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  358. setiastro/saspro/translations/saspro_uk.qm +0 -0
  359. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  360. setiastro/saspro/translations/saspro_zh.qm +0 -0
  361. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  362. setiastro/saspro/translations/sw_translations.py +3897 -0
  363. setiastro/saspro/translations/uk_translations.py +3929 -0
  364. setiastro/saspro/translations/zh_translations.py +3910 -0
  365. setiastro/saspro/versioning.py +77 -0
  366. setiastro/saspro/view_bundle.py +1558 -0
  367. setiastro/saspro/wavescale_hdr.py +648 -0
  368. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  369. setiastro/saspro/wavescalede.py +683 -0
  370. setiastro/saspro/wavescalede_preset.py +230 -0
  371. setiastro/saspro/wcs_update.py +374 -0
  372. setiastro/saspro/whitebalance.py +540 -0
  373. setiastro/saspro/widgets/__init__.py +48 -0
  374. setiastro/saspro/widgets/common_utilities.py +306 -0
  375. setiastro/saspro/widgets/graphics_views.py +122 -0
  376. setiastro/saspro/widgets/image_utils.py +518 -0
  377. setiastro/saspro/widgets/minigame/game.js +991 -0
  378. setiastro/saspro/widgets/minigame/index.html +53 -0
  379. setiastro/saspro/widgets/minigame/style.css +241 -0
  380. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  381. setiastro/saspro/widgets/resource_monitor.py +313 -0
  382. setiastro/saspro/widgets/spinboxes.py +290 -0
  383. setiastro/saspro/widgets/themed_buttons.py +13 -0
  384. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  385. setiastro/saspro/wimi.py +7367 -0
  386. setiastro/saspro/wims.py +588 -0
  387. setiastro/saspro/window_shelf.py +185 -0
  388. setiastro/saspro/xisf.py +1213 -0
  389. setiastrosuitepro-1.6.7.dist-info/METADATA +279 -0
  390. setiastrosuitepro-1.6.7.dist-info/RECORD +394 -0
  391. setiastrosuitepro-1.6.7.dist-info/WHEEL +4 -0
  392. setiastrosuitepro-1.6.7.dist-info/entry_points.txt +6 -0
  393. setiastrosuitepro-1.6.7.dist-info/licenses/LICENSE +674 -0
  394. setiastrosuitepro-1.6.7.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,323 @@
1
+ # pro/gui/mixins/update_mixin.py
2
+ """
3
+ Update check mixin for AstroSuiteProMainWindow.
4
+
5
+ This mixin contains all functionality for checking for application updates,
6
+ downloading updates, and handling the update installation process.
7
+ """
8
+ from __future__ import annotations
9
+ import json
10
+ import sys
11
+ import webbrowser
12
+ from typing import TYPE_CHECKING
13
+
14
+ from PyQt6.QtCore import QUrl
15
+ from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
16
+ from PyQt6.QtWidgets import QMessageBox, QApplication
17
+
18
+ from PyQt6.QtNetwork import QSslSocket
19
+
20
+ if TYPE_CHECKING:
21
+ pass
22
+
23
+
24
+ class UpdateMixin:
25
+ """
26
+ Mixin for application update functionality.
27
+
28
+ Provides methods for checking for updates, downloading updates,
29
+ and managing the update installation process.
30
+ """
31
+
32
+ # Default URL for update checks
33
+ _updates_url = "https://raw.githubusercontent.com/user/repo/main/updates.json"
34
+
35
+ @property
36
+ def _current_version_str(self) -> str:
37
+ """
38
+ Return the current app version as a string.
39
+
40
+ Prefer an attribute set on the main window (self._version),
41
+ fall back to any module-level VERSION if present, then "0.0.0".
42
+ """
43
+ v = getattr(self, "_version", None)
44
+ if not v:
45
+ v = globals().get("VERSION", None)
46
+ return str(v or "0.0.0")
47
+
48
+ def _ensure_network_manager(self):
49
+ from PyQt6.QtNetwork import QNetworkAccessManager
50
+ if getattr(self, "_nam", None) is None:
51
+ self._nam = QNetworkAccessManager(self)
52
+ self._nam.finished.connect(self._on_update_reply)
53
+
54
+ def _kick_update_check(self, *, interactive: bool):
55
+ """
56
+ Start an update check request.
57
+ """
58
+ self._ensure_network_manager()
59
+ url_str = self.settings.value("updates/url", self._updates_url, type=str) or self._updates_url
60
+
61
+ # ---- TLS availability guard (prevents crash on OpenSSL mismatch) ----
62
+ if url_str.lower().startswith("https://"):
63
+ try:
64
+ if not QSslSocket.supportsSsl():
65
+ msg = "TLS unavailable in Qt (QSslSocket.supportsSsl()=False). Skipping update check."
66
+ if self.statusBar():
67
+ self.statusBar().showMessage(self.tr("Update check unavailable (TLS missing)."), 5000)
68
+ if interactive:
69
+ QMessageBox.information(self, self.tr("Update Check"),
70
+ self.tr("Update check is unavailable because TLS is not available on this system."))
71
+ else:
72
+ print(f"[updates] {msg}")
73
+ return
74
+ except Exception as e:
75
+ # If QtNetwork is in a weird state, fail safe (do not attempt TLS)
76
+ print(f"[updates] TLS probe failed ({e}); skipping update check.")
77
+ return
78
+
79
+ req = QNetworkRequest(QUrl(url_str))
80
+ req.setRawHeader(b"User-Agent", f"SASPro/{self._current_version_str}".encode("utf-8"))
81
+
82
+ reply = self._nam.get(req)
83
+ reply.setProperty("interactive", interactive)
84
+
85
+ def check_for_updates_now(self):
86
+ """Check for updates interactively (show result to user)."""
87
+ if self.statusBar():
88
+ self.statusBar().showMessage(self.tr("Checking for updates..."))
89
+ self._kick_update_check(interactive=True)
90
+
91
+ def check_for_updates_startup(self):
92
+ """Check for updates silently at startup."""
93
+ self._kick_update_check(interactive=False)
94
+
95
+ def _parse_version_tuple(self, v: str):
96
+ """
97
+ Parse a version string into a tuple for comparison.
98
+
99
+ Args:
100
+ v: Version string like "1.2.3"
101
+
102
+ Returns:
103
+ Tuple of integers, or None if parsing fails
104
+ """
105
+ try:
106
+ parts = str(v).strip().split(".")
107
+ return tuple(int(p) for p in parts)
108
+ except Exception:
109
+ return None
110
+
111
+ def _on_update_reply(self, reply: QNetworkReply):
112
+ """Handle network reply from update check or download."""
113
+ interactive = bool(reply.property("interactive"))
114
+
115
+ # Was this the second request (the actual installer download)?
116
+ if bool(reply.property("is_update_download")):
117
+ self._on_windows_update_download_finished(reply)
118
+ return
119
+
120
+ try:
121
+ if reply.error() != QNetworkReply.NetworkError.NoError:
122
+ err = reply.errorString()
123
+ if self.statusBar():
124
+ self.statusBar().showMessage("Update check failed.", 5000)
125
+ if interactive:
126
+ QMessageBox.warning(self, self.tr("Update Check Failed"),
127
+ self.tr("Unable to check for updates.\n\n{err}").replace("{err}", err))
128
+ else:
129
+ print(f"[updates] check failed: {err}")
130
+ return
131
+
132
+ raw = bytes(reply.readAll())
133
+ try:
134
+ data = json.loads(raw.decode("utf-8"))
135
+ except Exception as je:
136
+ if self.statusBar():
137
+ self.statusBar().showMessage("Update check failed (bad JSON).", 5000)
138
+ if interactive:
139
+ QMessageBox.warning(self, self.tr("Update Check Failed"),
140
+ self.tr("Update JSON is invalid.\n\n{je}").replace("{je}", str(je)))
141
+ else:
142
+ print(f"[updates] bad JSON: {je}")
143
+ return
144
+
145
+ latest_str = str(data.get("version", "")).strip()
146
+ notes = str(data.get("notes", "") or "")
147
+ downloads = data.get("downloads", {}) or {}
148
+
149
+ if not latest_str:
150
+ if self.statusBar():
151
+ self.statusBar().showMessage("Update check failed (no 'version').", 5000)
152
+ if interactive:
153
+ QMessageBox.warning(self, self.tr("Update Check Failed"),
154
+ self.tr("Update JSON missing the 'version' field."))
155
+ else:
156
+ print("[updates] JSON missing 'version'")
157
+ return
158
+
159
+ cur_tuple = self._parse_version_tuple(self._current_version_str)
160
+ latest_tuple = self._parse_version_tuple(latest_str)
161
+ available = bool(latest_tuple and cur_tuple and latest_tuple > cur_tuple)
162
+
163
+ if available:
164
+ if self.statusBar():
165
+ self.statusBar().showMessage(self.tr("Update available: {0}").format(latest_str), 5000)
166
+ msg_box = QMessageBox(self)
167
+ msg_box.setIcon(QMessageBox.Icon.Information)
168
+ msg_box.setWindowTitle(self.tr("Update Available"))
169
+ msg_box.setText(self.tr("A new version ({0}) is available!").format(latest_str))
170
+ if notes:
171
+ msg_box.setInformativeText(self.tr("Release Notes:\n{0}").format(notes))
172
+ msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
173
+ msg_box.setDefaultButton(QMessageBox.StandardButton.Yes)
174
+
175
+ if downloads:
176
+ details = "\n".join([f"{k}: {v}" for k, v in downloads.items()])
177
+ msg_box.setDetailedText(details)
178
+
179
+ if msg_box.exec() == QMessageBox.StandardButton.Yes:
180
+ plat = sys.platform
181
+ link = downloads.get(
182
+ "Windows" if plat.startswith("win") else
183
+ "macOS" if plat.startswith("darwin") else
184
+ "Linux" if plat.startswith("linux") else "", ""
185
+ )
186
+ if not link:
187
+ QMessageBox.warning(self, self.tr("Download"), self.tr("No download link available for this platform."))
188
+ return
189
+
190
+ if plat.startswith("win"):
191
+ # Use in-app updater for Windows
192
+ self._start_windows_update_download(link)
193
+ else:
194
+ # Open browser for other platforms
195
+ webbrowser.open(link)
196
+ else:
197
+ if self.statusBar():
198
+ self.statusBar().showMessage("You're up to date.", 3000)
199
+ if interactive:
200
+ QMessageBox.information(self, self.tr("Up to Date"),
201
+ self.tr("You're already running the latest version."))
202
+ finally:
203
+ reply.deleteLater()
204
+
205
+ def _is_windows(self) -> bool:
206
+ """Check if running on Windows."""
207
+ return sys.platform.startswith("win")
208
+
209
+ def _start_windows_update_download(self, url: str):
210
+ """
211
+ Download the update file for Windows.
212
+
213
+ Args:
214
+ url: URL to download from
215
+ """
216
+ from PyQt6.QtCore import QStandardPaths
217
+ from pathlib import Path
218
+ import os
219
+
220
+ self._ensure_network_manager()
221
+
222
+ downloads_dir = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DownloadLocation)
223
+ if not downloads_dir:
224
+ import tempfile
225
+ downloads_dir = tempfile.gettempdir()
226
+
227
+ os.makedirs(downloads_dir, exist_ok=True)
228
+
229
+ # filename from URL
230
+ fname = url.split("/")[-1] or "setiastrosuitepro_windows.zip"
231
+ target_path = Path(downloads_dir) / fname
232
+
233
+ req = QNetworkRequest(QUrl(url))
234
+ req.setRawHeader(
235
+ b"User-Agent",
236
+ f"SASPro/{self._current_version_str}".encode("utf-8")
237
+ )
238
+
239
+ reply = self._nam.get(req)
240
+ # mark this reply as "this is the actual installer file, not updates.json"
241
+ reply.setProperty("is_update_download", True)
242
+ reply.setProperty("target_path", str(target_path))
243
+
244
+ reply.downloadProgress.connect(
245
+ lambda rec, tot: self.statusBar().showMessage(
246
+ self.tr("Downloading update... {0:.1f} KB / {1:.1f} KB").format(rec / 1024, tot / 1024) if tot > 0 else self.tr("Downloading update...")
247
+ )
248
+ )
249
+
250
+ def _on_windows_update_download_finished(self, reply: QNetworkReply):
251
+ """Handle completion of Windows update download."""
252
+ from pathlib import Path
253
+ import os
254
+ import zipfile
255
+ import subprocess
256
+ import tempfile
257
+
258
+ target_path = Path(reply.property("target_path"))
259
+
260
+ if reply.error() != QNetworkReply.NetworkError.NoError:
261
+ QMessageBox.warning(self, self.tr("Update Failed"),
262
+ self.tr("Could not download update:\n{0}").format(reply.errorString()))
263
+ return
264
+
265
+ # Write the .zip
266
+ data = bytes(reply.readAll())
267
+ try:
268
+ with open(target_path, "wb") as f:
269
+ f.write(data)
270
+ except Exception as e:
271
+ QMessageBox.warning(self, self.tr("Update Failed"),
272
+ self.tr("Could not save update to disk:\n{0}").format(e))
273
+ return
274
+
275
+ self.statusBar().showMessage(f"Update downloaded to {target_path}", 5000)
276
+
277
+ # Extract zip if needed
278
+ if target_path.suffix.lower() == ".zip":
279
+ extract_dir = Path(tempfile.mkdtemp(prefix="saspro-update-"))
280
+ try:
281
+ with zipfile.ZipFile(target_path, "r") as zf:
282
+ zf.extractall(extract_dir)
283
+ except Exception as e:
284
+ QMessageBox.warning(self, self.tr("Update Failed"),
285
+ self.tr("Could not extract update zip:\n{0}").format(e))
286
+ return
287
+
288
+ # Look recursively for an .exe
289
+ exe_cands = list(extract_dir.rglob("*.exe"))
290
+ if not exe_cands:
291
+ QMessageBox.warning(
292
+ self,
293
+ "Update Failed",
294
+ self.tr("Downloaded ZIP did not contain an .exe installer.\nFolder: {0}").format(extract_dir)
295
+ )
296
+ return
297
+
298
+ installer_path = exe_cands[0]
299
+ else:
300
+ # In case one day Windows points straight to .exe
301
+ installer_path = target_path
302
+
303
+ # Ask to run
304
+ ok = QMessageBox.question(
305
+ self,
306
+ self.tr("Run Installer"),
307
+ self.tr("The update has been downloaded.\n\nRun the installer now? (SAS will close.)"),
308
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
309
+ QMessageBox.StandardButton.Yes,
310
+ )
311
+ if ok != QMessageBox.StandardButton.Yes:
312
+ return
313
+
314
+ # Launch installer
315
+ try:
316
+ subprocess.Popen([str(installer_path)], shell=False)
317
+ except Exception as e:
318
+ QMessageBox.warning(self, self.tr("Update Failed"),
319
+ self.tr("Could not start installer:\n{0}").format(e))
320
+ return
321
+
322
+ # Close app so the installer can overwrite files
323
+ QApplication.instance().quit()