tomtoolkit 2.30.0a0__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.
Files changed (483) hide show
  1. tom_alerts/__init__.py +0 -0
  2. tom_alerts/admin.py +7 -0
  3. tom_alerts/alerts.py +275 -0
  4. tom_alerts/apps.py +12 -0
  5. tom_alerts/brokers/__init__.py +0 -0
  6. tom_alerts/brokers/alerce.py +636 -0
  7. tom_alerts/brokers/antares.py +50 -0
  8. tom_alerts/brokers/fink.py +44 -0
  9. tom_alerts/brokers/gaia.py +197 -0
  10. tom_alerts/brokers/hermes.py +44 -0
  11. tom_alerts/brokers/lasair.py +229 -0
  12. tom_alerts/brokers/scout.py +96 -0
  13. tom_alerts/brokers/tns.py +240 -0
  14. tom_alerts/exceptions.py +4 -0
  15. tom_alerts/management/__init__.py +0 -0
  16. tom_alerts/management/commands/__init__.py +0 -0
  17. tom_alerts/management/commands/runbrokerquery.py +33 -0
  18. tom_alerts/migrations/0001_initial.py +25 -0
  19. tom_alerts/migrations/0002_auto_20190626_2135.py +23 -0
  20. tom_alerts/migrations/0003_auto_20190701_2226.py +18 -0
  21. tom_alerts/migrations/0004_auto_20210204_2300.py +18 -0
  22. tom_alerts/migrations/0005_alertstreammessage.py +23 -0
  23. tom_alerts/migrations/0006_alter_alertstreammessage_exchange_status.py +18 -0
  24. tom_alerts/migrations/0007_alter_alertstreammessage_message_id.py +18 -0
  25. tom_alerts/migrations/__init__.py +0 -0
  26. tom_alerts/models.py +83 -0
  27. tom_alerts/templates/tom_alerts/brokerquery_confirm_delete.html +9 -0
  28. tom_alerts/templates/tom_alerts/brokerquery_list.html +49 -0
  29. tom_alerts/templates/tom_alerts/partials/navbar_link.html +3 -0
  30. tom_alerts/templates/tom_alerts/partials/submit_upstream_form.html +2 -0
  31. tom_alerts/templates/tom_alerts/query_form.html +7 -0
  32. tom_alerts/templates/tom_alerts/query_result.html +70 -0
  33. tom_alerts/templatetags/alerts_extras.py +36 -0
  34. tom_alerts/tests/__init__.py +0 -0
  35. tom_alerts/tests/brokers/__init__.py +0 -0
  36. tom_alerts/tests/brokers/test_alerce.py +569 -0
  37. tom_alerts/tests/brokers/test_gaia.py +188 -0
  38. tom_alerts/tests/brokers/test_lasair.py +96 -0
  39. tom_alerts/tests/tests.py +343 -0
  40. tom_alerts/urls.py +16 -0
  41. tom_alerts/views.py +394 -0
  42. tom_base/__init__.py +2 -0
  43. tom_base/settings.py +352 -0
  44. tom_base/wsgi.py +16 -0
  45. tom_catalogs/__init__.py +0 -0
  46. tom_catalogs/admin.py +1 -0
  47. tom_catalogs/apps.py +18 -0
  48. tom_catalogs/forms.py +41 -0
  49. tom_catalogs/harvester.py +86 -0
  50. tom_catalogs/harvesters/__init__.py +0 -0
  51. tom_catalogs/harvesters/jplhorizons.py +64 -0
  52. tom_catalogs/harvesters/mpc.py +200 -0
  53. tom_catalogs/harvesters/ned.py +27 -0
  54. tom_catalogs/harvesters/simbad.py +52 -0
  55. tom_catalogs/harvesters/tns.py +78 -0
  56. tom_catalogs/templates/tom_catalogs/partials/catalog_query_menu.html +4 -0
  57. tom_catalogs/templates/tom_catalogs/query_form.html +13 -0
  58. tom_catalogs/templatetags/__init__.py +0 -0
  59. tom_catalogs/templatetags/dataservices_extras.py +17 -0
  60. tom_catalogs/tests/__init__.py +0 -0
  61. tom_catalogs/tests/harvesters/__init__.py +0 -0
  62. tom_catalogs/tests/harvesters/data/__init__.py +0 -0
  63. tom_catalogs/tests/harvesters/data/test_65803_mpc_orb.json +284 -0
  64. tom_catalogs/tests/harvesters/test_jplhorizons.py +72 -0
  65. tom_catalogs/tests/harvesters/test_mpc.py +421 -0
  66. tom_catalogs/tests/harvesters/test_simbad.py +61 -0
  67. tom_catalogs/tests/tests.py +51 -0
  68. tom_catalogs/urls.py +9 -0
  69. tom_catalogs/views.py +39 -0
  70. tom_common/__init__.py +0 -0
  71. tom_common/admin.py +23 -0
  72. tom_common/api_router.py +54 -0
  73. tom_common/api_views.py +15 -0
  74. tom_common/apps.py +34 -0
  75. tom_common/exceptions.py +5 -0
  76. tom_common/forms.py +119 -0
  77. tom_common/hints.py +14 -0
  78. tom_common/hooks.py +33 -0
  79. tom_common/middleware.py +60 -0
  80. tom_common/migrations/0001_initial.py +25 -0
  81. tom_common/migrations/0002_usersession.py +25 -0
  82. tom_common/migrations/__init__.py +0 -0
  83. tom_common/mixins.py +13 -0
  84. tom_common/models.py +188 -0
  85. tom_common/serializers.py +11 -0
  86. tom_common/session_utils.py +351 -0
  87. tom_common/signals.py +168 -0
  88. tom_common/static/font-awesome-4.7.0/HELP-US-OUT.txt +7 -0
  89. tom_common/static/font-awesome-4.7.0/css/font-awesome.css +2337 -0
  90. tom_common/static/font-awesome-4.7.0/css/font-awesome.min.css +4 -0
  91. tom_common/static/font-awesome-4.7.0/fonts/FontAwesome.otf +0 -0
  92. tom_common/static/font-awesome-4.7.0/fonts/fontawesome-webfont.eot +0 -0
  93. tom_common/static/font-awesome-4.7.0/fonts/fontawesome-webfont.svg +2671 -0
  94. tom_common/static/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf +0 -0
  95. tom_common/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff +0 -0
  96. tom_common/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 +0 -0
  97. tom_common/static/font-awesome-4.7.0/less/animated.less +34 -0
  98. tom_common/static/font-awesome-4.7.0/less/bordered-pulled.less +25 -0
  99. tom_common/static/font-awesome-4.7.0/less/core.less +12 -0
  100. tom_common/static/font-awesome-4.7.0/less/fixed-width.less +6 -0
  101. tom_common/static/font-awesome-4.7.0/less/font-awesome.less +18 -0
  102. tom_common/static/font-awesome-4.7.0/less/icons.less +789 -0
  103. tom_common/static/font-awesome-4.7.0/less/larger.less +13 -0
  104. tom_common/static/font-awesome-4.7.0/less/list.less +19 -0
  105. tom_common/static/font-awesome-4.7.0/less/mixins.less +60 -0
  106. tom_common/static/font-awesome-4.7.0/less/path.less +15 -0
  107. tom_common/static/font-awesome-4.7.0/less/rotated-flipped.less +20 -0
  108. tom_common/static/font-awesome-4.7.0/less/screen-reader.less +5 -0
  109. tom_common/static/font-awesome-4.7.0/less/stacked.less +20 -0
  110. tom_common/static/font-awesome-4.7.0/less/variables.less +800 -0
  111. tom_common/static/font-awesome-4.7.0/scss/_animated.scss +34 -0
  112. tom_common/static/font-awesome-4.7.0/scss/_bordered-pulled.scss +25 -0
  113. tom_common/static/font-awesome-4.7.0/scss/_core.scss +12 -0
  114. tom_common/static/font-awesome-4.7.0/scss/_fixed-width.scss +6 -0
  115. tom_common/static/font-awesome-4.7.0/scss/_icons.scss +789 -0
  116. tom_common/static/font-awesome-4.7.0/scss/_larger.scss +13 -0
  117. tom_common/static/font-awesome-4.7.0/scss/_list.scss +19 -0
  118. tom_common/static/font-awesome-4.7.0/scss/_mixins.scss +60 -0
  119. tom_common/static/font-awesome-4.7.0/scss/_path.scss +15 -0
  120. tom_common/static/font-awesome-4.7.0/scss/_rotated-flipped.scss +20 -0
  121. tom_common/static/font-awesome-4.7.0/scss/_screen-reader.scss +5 -0
  122. tom_common/static/font-awesome-4.7.0/scss/_stacked.scss +20 -0
  123. tom_common/static/font-awesome-4.7.0/scss/_variables.scss +800 -0
  124. tom_common/static/font-awesome-4.7.0/scss/font-awesome.scss +18 -0
  125. tom_common/static/tom_common/css/dark.css +22 -0
  126. tom_common/static/tom_common/css/main.css +214 -0
  127. tom_common/static/tom_common/img/bluespinner.gif +0 -0
  128. tom_common/static/tom_common/img/favicon-16.ico +0 -0
  129. tom_common/static/tom_common/img/favicon-16x16.png +0 -0
  130. tom_common/static/tom_common/img/favicon-32.ico +0 -0
  131. tom_common/static/tom_common/img/favicon-32x32.png +0 -0
  132. tom_common/static/tom_common/img/favicon.ico +0 -0
  133. tom_common/static/tom_common/img/icon.png +0 -0
  134. tom_common/static/tom_common/img/logo-color-cropped.png +0 -0
  135. tom_common/static/tom_common/img/logo-color.png +0 -0
  136. tom_common/static/tom_common/img/logo-text.png +0 -0
  137. tom_common/static/tom_common/js/htmx.min.js +1 -0
  138. tom_common/templates/auth/group_confirm_delete.html +9 -0
  139. tom_common/templates/auth/group_form.html +17 -0
  140. tom_common/templates/auth/partials/app_user_lists.html +5 -0
  141. tom_common/templates/auth/partials/group_list.html +23 -0
  142. tom_common/templates/auth/partials/user_list.html +33 -0
  143. tom_common/templates/auth/user_confirm_change_password.html +11 -0
  144. tom_common/templates/auth/user_confirm_delete.html +10 -0
  145. tom_common/templates/auth/user_list.html +9 -0
  146. tom_common/templates/bootstrap4_overrides/pagination.html +42 -0
  147. tom_common/templates/comments/form.html +16 -0
  148. tom_common/templates/comments/list.html +23 -0
  149. tom_common/templates/django_comments/comment_confirm_delete.html +11 -0
  150. tom_common/templates/django_filters/widgets/multiwidget.html +10 -0
  151. tom_common/templates/registration/login.html +16 -0
  152. tom_common/templates/tom_common/base.html +76 -0
  153. tom_common/templates/tom_common/change_user_password.html +14 -0
  154. tom_common/templates/tom_common/create_user.html +27 -0
  155. tom_common/templates/tom_common/index.html +50 -0
  156. tom_common/templates/tom_common/navbar_content.html +41 -0
  157. tom_common/templates/tom_common/partials/app_profiles.html +23 -0
  158. tom_common/templates/tom_common/partials/confirm_user_delete.html +24 -0
  159. tom_common/templates/tom_common/partials/copy_button.html +19 -0
  160. tom_common/templates/tom_common/partials/include_app_partial.html +7 -0
  161. tom_common/templates/tom_common/partials/navbar_app_addons.html +4 -0
  162. tom_common/templates/tom_common/partials/navbar_login.html +18 -0
  163. tom_common/templates/tom_common/partials/user_data.html +32 -0
  164. tom_common/templates/tom_common/user_profile.html +16 -0
  165. tom_common/templatetags/__init__.py +0 -0
  166. tom_common/templatetags/bootstrap4_overrides.py +36 -0
  167. tom_common/templatetags/tom_common_extras.py +197 -0
  168. tom_common/templatetags/user_extras.py +119 -0
  169. tom_common/tests.py +620 -0
  170. tom_common/urls.py +74 -0
  171. tom_common/views.py +335 -0
  172. tom_dataproducts/__init__.py +0 -0
  173. tom_dataproducts/admin.py +7 -0
  174. tom_dataproducts/alertstreams/__init__.py +0 -0
  175. tom_dataproducts/alertstreams/hermes.py +413 -0
  176. tom_dataproducts/api_views.py +94 -0
  177. tom_dataproducts/apps.py +5 -0
  178. tom_dataproducts/data_processor.py +124 -0
  179. tom_dataproducts/exceptions.py +2 -0
  180. tom_dataproducts/filters.py +52 -0
  181. tom_dataproducts/forms.py +66 -0
  182. tom_dataproducts/hooks.py +24 -0
  183. tom_dataproducts/management/__init__.py +0 -0
  184. tom_dataproducts/management/commands/__init__.py +0 -0
  185. tom_dataproducts/management/commands/downloaddata.py +19 -0
  186. tom_dataproducts/management/commands/updatereduceddata.py +49 -0
  187. tom_dataproducts/migrations/0001_initial.py +77 -0
  188. tom_dataproducts/migrations/0004_auto_20190626_0904.py +23 -0
  189. tom_dataproducts/migrations/0005_auto_20190704_1010.py +29 -0
  190. tom_dataproducts/migrations/0006_auto_20190912_2013.py +18 -0
  191. tom_dataproducts/migrations/0007_manual_20191016_rename_type.py +37 -0
  192. tom_dataproducts/migrations/0008_auto_20191205_1952.py +27 -0
  193. tom_dataproducts/migrations/0009_auto_20210204_2221.py +18 -0
  194. tom_dataproducts/migrations/0010_manual_20210305_fix_spectroscopy.py +43 -0
  195. tom_dataproducts/migrations/0011_reduceddatum_message.py +19 -0
  196. tom_dataproducts/migrations/0012_alter_reduceddatum_data_product_and_more.py +29 -0
  197. tom_dataproducts/migrations/0013_alter_reduceddatum_message.py +19 -0
  198. tom_dataproducts/migrations/0014_alter_reduceddatum_timestamp.py +19 -0
  199. tom_dataproducts/migrations/__init__.py +0 -0
  200. tom_dataproducts/models.py +420 -0
  201. tom_dataproducts/processors/__init__.py +0 -0
  202. tom_dataproducts/processors/atlas_processor.py +92 -0
  203. tom_dataproducts/processors/data_serializers.py +38 -0
  204. tom_dataproducts/processors/panstarrs_processor.py +98 -0
  205. tom_dataproducts/processors/photometry_processor.py +65 -0
  206. tom_dataproducts/processors/spectroscopy_processor.py +137 -0
  207. tom_dataproducts/serializers.py +132 -0
  208. tom_dataproducts/sharing.py +379 -0
  209. tom_dataproducts/single_target_data_service/__init__.py +0 -0
  210. tom_dataproducts/single_target_data_service/atlas.py +173 -0
  211. tom_dataproducts/single_target_data_service/panstarrs_service/__init__.py +0 -0
  212. tom_dataproducts/single_target_data_service/panstarrs_service/panstarrs.py +235 -0
  213. tom_dataproducts/single_target_data_service/panstarrs_service/panstarrs_api.py +170 -0
  214. tom_dataproducts/single_target_data_service/panstarrs_service/tests.py +9 -0
  215. tom_dataproducts/single_target_data_service/single_target_data_service.py +145 -0
  216. tom_dataproducts/static/tom_dataproducts/img/placeholder.png +0 -0
  217. tom_dataproducts/static/tom_dataproducts/photometry_sample.csv +5 -0
  218. tom_dataproducts/static/tom_dataproducts/spectrum_sample.csv +7 -0
  219. tom_dataproducts/tasks.py +103 -0
  220. tom_dataproducts/templates/tom_dataproducts/add_product_to_group.html +12 -0
  221. tom_dataproducts/templates/tom_dataproducts/dataproduct_confirm_delete.html +12 -0
  222. tom_dataproducts/templates/tom_dataproducts/dataproduct_list.html +94 -0
  223. tom_dataproducts/templates/tom_dataproducts/dataproductgroup_confirm_delete.html +11 -0
  224. tom_dataproducts/templates/tom_dataproducts/dataproductgroup_detail.html +20 -0
  225. tom_dataproducts/templates/tom_dataproducts/dataproductgroup_form.html +12 -0
  226. tom_dataproducts/templates/tom_dataproducts/dataproductgroup_list.html +18 -0
  227. tom_dataproducts/templates/tom_dataproducts/partials/dataproduct_list_for_target.html +75 -0
  228. tom_dataproducts/templates/tom_dataproducts/partials/js9_button.html +1 -0
  229. tom_dataproducts/templates/tom_dataproducts/partials/js9_scripts.html +21 -0
  230. tom_dataproducts/templates/tom_dataproducts/partials/photometry_datalist_for_target.html +125 -0
  231. tom_dataproducts/templates/tom_dataproducts/partials/photometry_for_target.html +11 -0
  232. tom_dataproducts/templates/tom_dataproducts/partials/query_single_target_data_service.html +12 -0
  233. tom_dataproducts/templates/tom_dataproducts/partials/recent_photometry.html +29 -0
  234. tom_dataproducts/templates/tom_dataproducts/partials/reduceddatum_sparkline.html +3 -0
  235. tom_dataproducts/templates/tom_dataproducts/partials/saved_dataproduct_list_for_observation.html +37 -0
  236. tom_dataproducts/templates/tom_dataproducts/partials/spectroscopy_for_target.html +4 -0
  237. tom_dataproducts/templates/tom_dataproducts/partials/unsaved_dataproduct_list_for_observation.html +27 -0
  238. tom_dataproducts/templates/tom_dataproducts/partials/update_broker_data_button.html +1 -0
  239. tom_dataproducts/templates/tom_dataproducts/partials/upload_dataproduct.html +12 -0
  240. tom_dataproducts/templates/tom_dataproducts/single_target_data_service_form.html +24 -0
  241. tom_dataproducts/templates/tom_dataproducts/upload_reduced_data.html +12 -0
  242. tom_dataproducts/templatetags/__init__.py +0 -0
  243. tom_dataproducts/templatetags/dataproduct_extras.py +520 -0
  244. tom_dataproducts/tests/__init__.py +0 -0
  245. tom_dataproducts/tests/test_api.py +197 -0
  246. tom_dataproducts/tests/test_atlas.py +52 -0
  247. tom_dataproducts/tests/test_data/test_atlas_fp.csv +33 -0
  248. tom_dataproducts/tests/test_data/test_lightcurve.csv +4 -0
  249. tom_dataproducts/tests/test_data/test_spectrum.csv +3 -0
  250. tom_dataproducts/tests/test_data/test_spectrum.fits +231 -1
  251. tom_dataproducts/tests/test_sharing.py +173 -0
  252. tom_dataproducts/tests/tests.py +944 -0
  253. tom_dataproducts/urls.py +34 -0
  254. tom_dataproducts/utils.py +34 -0
  255. tom_dataproducts/views.py +536 -0
  256. tom_dataservices/__init__.py +0 -0
  257. tom_dataservices/admin.py +3 -0
  258. tom_dataservices/apps.py +33 -0
  259. tom_dataservices/data_services/lsst.py +133 -0
  260. tom_dataservices/data_services/tns.py +209 -0
  261. tom_dataservices/dataservices.py +286 -0
  262. tom_dataservices/forms.py +50 -0
  263. tom_dataservices/migrations/0001_initial.py +26 -0
  264. tom_dataservices/migrations/__init__.py +0 -0
  265. tom_dataservices/models.py +24 -0
  266. tom_dataservices/templates/tom_dataservices/dataservicequery_confirm_delete.html +9 -0
  267. tom_dataservices/templates/tom_dataservices/partials/catalog_query_menu.html +11 -0
  268. tom_dataservices/templates/tom_dataservices/partials/navbar_list.html +14 -0
  269. tom_dataservices/templates/tom_dataservices/partials/query_results_table.html +21 -0
  270. tom_dataservices/templates/tom_dataservices/query_form.html +58 -0
  271. tom_dataservices/templates/tom_dataservices/query_list.html +49 -0
  272. tom_dataservices/templates/tom_dataservices/query_result.html +45 -0
  273. tom_dataservices/templates/tom_dataservices/tns/partials/tns_query_results_table.html +24 -0
  274. tom_dataservices/templates/tom_dataservices/tns/partials/tns_simple_form.html +2 -0
  275. tom_dataservices/templatetags/dataservices_extras.py +22 -0
  276. tom_dataservices/tests/__init__.py +0 -0
  277. tom_dataservices/tests/test_api.py +5 -0
  278. tom_dataservices/tests/tests.py +113 -0
  279. tom_dataservices/urls.py +17 -0
  280. tom_dataservices/views.py +351 -0
  281. tom_observations/__init__.py +0 -0
  282. tom_observations/admin.py +26 -0
  283. tom_observations/api_views.py +186 -0
  284. tom_observations/apps.py +5 -0
  285. tom_observations/cadence.py +82 -0
  286. tom_observations/cadences/__init__.py +0 -0
  287. tom_observations/cadences/resume_cadence_after_failure.py +115 -0
  288. tom_observations/cadences/retry_failed_observations.py +66 -0
  289. tom_observations/facilities/__init__.py +0 -0
  290. tom_observations/facilities/blanco.py +233 -0
  291. tom_observations/facilities/gemini.py +523 -0
  292. tom_observations/facilities/lco.py +1140 -0
  293. tom_observations/facilities/lco_redirect.py +50 -0
  294. tom_observations/facilities/lt.py +70 -0
  295. tom_observations/facilities/manual.py +125 -0
  296. tom_observations/facilities/ocs.py +1630 -0
  297. tom_observations/facilities/soar.py +265 -0
  298. tom_observations/facility.py +635 -0
  299. tom_observations/forms.py +70 -0
  300. tom_observations/management/__init__.py +0 -0
  301. tom_observations/management/commands/__init__.py +0 -0
  302. tom_observations/management/commands/runcadencestrategies.py +51 -0
  303. tom_observations/management/commands/updatestatus.py +55 -0
  304. tom_observations/migrations/0001_initial.py +32 -0
  305. tom_observations/migrations/0002_auto_20190306_2343.py +23 -0
  306. tom_observations/migrations/0003_auto_20190503_2318.py +18 -0
  307. tom_observations/migrations/0004_observationgroup.py +23 -0
  308. tom_observations/migrations/0005_auto_20191210_0007.py +17 -0
  309. tom_observations/migrations/0006_observationgroup_cadence_strategy.py +18 -0
  310. tom_observations/migrations/0007_observationstrategy.py +24 -0
  311. tom_observations/migrations/0008_observationgroup_cadence_parameters.py +18 -0
  312. tom_observations/migrations/0009_observationrecord_user.py +21 -0
  313. tom_observations/migrations/0010_manual_create_dynamic_cadence.py +67 -0
  314. tom_observations/migrations/0011_auto_20200917_0306.py +26 -0
  315. tom_observations/migrations/0012_auto_20210205_1819.py +23 -0
  316. tom_observations/migrations/0013_facility.py +34 -0
  317. tom_observations/migrations/0014_alter_facility_orbit.py +18 -0
  318. tom_observations/migrations/0015_remove_facility_mpc_site_code_and_more.py +73 -0
  319. tom_observations/migrations/0016_alter_facility_options.py +17 -0
  320. tom_observations/migrations/__init__.py +0 -0
  321. tom_observations/models.py +335 -0
  322. tom_observations/observation_template.py +56 -0
  323. tom_observations/serializers.py +84 -0
  324. tom_observations/static/tom_observations/css/main.css +24 -0
  325. tom_observations/templates/tom_observations/existing_observation_confirm.html +7 -0
  326. tom_observations/templates/tom_observations/facility_status.html +10 -0
  327. tom_observations/templates/tom_observations/observation_form.html +64 -0
  328. tom_observations/templates/tom_observations/observation_list.html +89 -0
  329. tom_observations/templates/tom_observations/observationgroup_confirm_delete.html +12 -0
  330. tom_observations/templates/tom_observations/observationgroup_form.html +9 -0
  331. tom_observations/templates/tom_observations/observationgroup_list.html +48 -0
  332. tom_observations/templates/tom_observations/observationrecord_detail.html +90 -0
  333. tom_observations/templates/tom_observations/observationtemplate_confirm_delete.html +9 -0
  334. tom_observations/templates/tom_observations/observationtemplate_form.html +10 -0
  335. tom_observations/templates/tom_observations/observationtemplate_list.html +50 -0
  336. tom_observations/templates/tom_observations/observationupdate_form.html +7 -0
  337. tom_observations/templates/tom_observations/partials/existing_observation_form.html +3 -0
  338. tom_observations/templates/tom_observations/partials/facility_map.html +1 -0
  339. tom_observations/templates/tom_observations/partials/facility_observation_form.html +4 -0
  340. tom_observations/templates/tom_observations/partials/facility_status.html +33 -0
  341. tom_observations/templates/tom_observations/partials/observation_distribution.html +1 -0
  342. tom_observations/templates/tom_observations/partials/observation_list.html +25 -0
  343. tom_observations/templates/tom_observations/partials/observation_plan.html +4 -0
  344. tom_observations/templates/tom_observations/partials/observation_type_tabs.html +10 -0
  345. tom_observations/templates/tom_observations/partials/observationtemplate_from_record.html +1 -0
  346. tom_observations/templates/tom_observations/partials/observationtemplate_run.html +6 -0
  347. tom_observations/templates/tom_observations/partials/observing_buttons.html +7 -0
  348. tom_observations/templates/tom_observations/partials/update_observation_id_form.html +2 -0
  349. tom_observations/templates/tom_observations/partials/update_status_button.html +1 -0
  350. tom_observations/templatetags/__init__.py +0 -0
  351. tom_observations/templatetags/observation_extras.py +344 -0
  352. tom_observations/tests/__init__.py +0 -0
  353. tom_observations/tests/facilities/__init__.py +0 -0
  354. tom_observations/tests/facilities/test_gemini.py +32 -0
  355. tom_observations/tests/facilities/test_lco.py +717 -0
  356. tom_observations/tests/facilities/test_ocs.py +266 -0
  357. tom_observations/tests/facilities/test_soar.py +195 -0
  358. tom_observations/tests/factories.py +92 -0
  359. tom_observations/tests/test_api.py +295 -0
  360. tom_observations/tests/test_cadence.py +141 -0
  361. tom_observations/tests/test_credentials.py +164 -0
  362. tom_observations/tests/tests.py +493 -0
  363. tom_observations/tests/utils.py +161 -0
  364. tom_observations/urls.py +36 -0
  365. tom_observations/utils.py +186 -0
  366. tom_observations/views.py +815 -0
  367. tom_observations/widgets.py +50 -0
  368. tom_setup/__init__.py +0 -0
  369. tom_setup/apps.py +5 -0
  370. tom_setup/management/__init__.py +0 -0
  371. tom_setup/management/commands/__init__.py +0 -0
  372. tom_setup/management/commands/tom_setup.py +296 -0
  373. tom_setup/templates/tom_setup/css.tmpl +26 -0
  374. tom_setup/templates/tom_setup/models.tmpl +18 -0
  375. tom_setup/templates/tom_setup/settings.tmpl +383 -0
  376. tom_setup/templates/tom_setup/urls.tmpl +20 -0
  377. tom_targets/__init__.py +1 -0
  378. tom_targets/admin.py +34 -0
  379. tom_targets/api_views.py +127 -0
  380. tom_targets/apps.py +9 -0
  381. tom_targets/base_models.py +609 -0
  382. tom_targets/fields.py +14 -0
  383. tom_targets/filters.py +142 -0
  384. tom_targets/forms.py +307 -0
  385. tom_targets/groups.py +260 -0
  386. tom_targets/management/__init__.py +0 -0
  387. tom_targets/management/commands/__init__.py +0 -0
  388. tom_targets/management/commands/converttargetextras.py +179 -0
  389. tom_targets/management/commands/setdefaultextras.py +55 -0
  390. tom_targets/merge.py +90 -0
  391. tom_targets/migrations/0001_initial.py +68 -0
  392. tom_targets/migrations/0002_auto_20190115_2102.py +23 -0
  393. tom_targets/migrations/0002_auto_20190117_2248.py +23 -0
  394. tom_targets/migrations/0003_merge_20190123_1754.py +14 -0
  395. tom_targets/migrations/0004_auto_20190123_2010.py +31 -0
  396. tom_targets/migrations/0005_auto_20190214_1722.py +28 -0
  397. tom_targets/migrations/0006_auto_20190403_1659.py +23 -0
  398. tom_targets/migrations/0007_auto_20190423_1455.py +18 -0
  399. tom_targets/migrations/0007_auto_20190503_2318.py +18 -0
  400. tom_targets/migrations/0007_auto_20190515_0047.py +18 -0
  401. tom_targets/migrations/0008_merge_20190515_0914.py +14 -0
  402. tom_targets/migrations/0008_merge_20190520_1645.py +14 -0
  403. tom_targets/migrations/0009_auto_20190605_1656.py +17 -0
  404. tom_targets/migrations/0010_auto_20190715_1657.py +17 -0
  405. tom_targets/migrations/0010_merge_20190612_1227.py +14 -0
  406. tom_targets/migrations/0011_merge_20190731_1821.py +14 -0
  407. tom_targets/migrations/0012_target_perihdist.py +18 -0
  408. tom_targets/migrations/0013_manual_20190916_multiple_names.py +74 -0
  409. tom_targets/migrations/0014_auto_20190923_1827.py +33 -0
  410. tom_targets/migrations/0015_auto_20190923_2233.py +23 -0
  411. tom_targets/migrations/0016_auto_20191211_1707.py +18 -0
  412. tom_targets/migrations/0017_auto_20200130_2350.py +23 -0
  413. tom_targets/migrations/0018_auto_20200714_1832.py +18 -0
  414. tom_targets/migrations/0019_auto_20210811_0018.py +17 -0
  415. tom_targets/migrations/0020_alter_targetname_created_alter_targetname_modified.py +23 -0
  416. tom_targets/migrations/0021_rename_target_basetarget_alter_basetarget_options.py +23 -0
  417. tom_targets/migrations/0022_persistentshare.py +30 -0
  418. tom_targets/migrations/0023_alter_basetarget_created.py +18 -0
  419. tom_targets/migrations/0024_basetarget_permissions.py +18 -0
  420. tom_targets/migrations/0025_auto_20250206_2017.py +53 -0
  421. tom_targets/migrations/0026_alter_basetarget_permissions.py +18 -0
  422. tom_targets/migrations/0027_alter_basetarget_epoch_of_elements_and_more.py +33 -0
  423. tom_targets/migrations/0028_basetarget_abs_mag_basetarget_slope_and_more.py +23 -0
  424. tom_targets/migrations/0029_alter_basetarget_permissions.py +19 -0
  425. tom_targets/migrations/0030_alter_basetarget_slope.py +18 -0
  426. tom_targets/migrations/__init__.py +0 -0
  427. tom_targets/models.py +226 -0
  428. tom_targets/permissions.py +37 -0
  429. tom_targets/persistent_sharing_serializers.py +28 -0
  430. tom_targets/seed.py +123 -0
  431. tom_targets/serializers.py +181 -0
  432. tom_targets/sharing.py +123 -0
  433. tom_targets/signals/__init__.py +0 -0
  434. tom_targets/signals/handlers.py +23 -0
  435. tom_targets/static/tom_targets/css/main.css +17 -0
  436. tom_targets/static/tom_targets/target_import.csv +3 -0
  437. tom_targets/templates/tom_targets/partials/aladin_finderchart.html +178 -0
  438. tom_targets/templates/tom_targets/partials/aladin_skymap.html +127 -0
  439. tom_targets/templates/tom_targets/partials/app_tab_divs.html +6 -0
  440. tom_targets/templates/tom_targets/partials/app_tabs.html +6 -0
  441. tom_targets/templates/tom_targets/partials/create_persistent_share.html +90 -0
  442. tom_targets/templates/tom_targets/partials/module_buttons.html +4 -0
  443. tom_targets/templates/tom_targets/partials/moon_distance.html +3 -0
  444. tom_targets/templates/tom_targets/partials/persistent_share_table.html +59 -0
  445. tom_targets/templates/tom_targets/partials/recent_targets.html +38 -0
  446. tom_targets/templates/tom_targets/partials/recently_updated_targets.html +23 -0
  447. tom_targets/templates/tom_targets/partials/target_buttons.html +9 -0
  448. tom_targets/templates/tom_targets/partials/target_data.html +44 -0
  449. tom_targets/templates/tom_targets/partials/target_feature.html +17 -0
  450. tom_targets/templates/tom_targets/partials/target_groups.html +37 -0
  451. tom_targets/templates/tom_targets/partials/target_merge_fields.html +38 -0
  452. tom_targets/templates/tom_targets/partials/target_plan.html +11 -0
  453. tom_targets/templates/tom_targets/partials/target_table.html +61 -0
  454. tom_targets/templates/tom_targets/partials/target_unknown_statuses.html +5 -0
  455. tom_targets/templates/tom_targets/partials/targetlist_select.html +55 -0
  456. tom_targets/templates/tom_targets/target_confirm_delete.html +12 -0
  457. tom_targets/templates/tom_targets/target_detail.html +133 -0
  458. tom_targets/templates/tom_targets/target_facility_selection.html +68 -0
  459. tom_targets/templates/tom_targets/target_form.html +78 -0
  460. tom_targets/templates/tom_targets/target_group_share.html +70 -0
  461. tom_targets/templates/tom_targets/target_grouping.html +48 -0
  462. tom_targets/templates/tom_targets/target_import.html +26 -0
  463. tom_targets/templates/tom_targets/target_list.html +68 -0
  464. tom_targets/templates/tom_targets/target_manage_persistent_shares.html +20 -0
  465. tom_targets/templates/tom_targets/target_merge.html +24 -0
  466. tom_targets/templates/tom_targets/target_share.html +89 -0
  467. tom_targets/templates/tom_targets/targetlist_confirm_delete.html +11 -0
  468. tom_targets/templates/tom_targets/targetlist_form.html +9 -0
  469. tom_targets/templatetags/__init__.py +0 -0
  470. tom_targets/templatetags/targets_extras.py +569 -0
  471. tom_targets/tests/__init__.py +0 -0
  472. tom_targets/tests/factories.py +63 -0
  473. tom_targets/tests/test_api.py +303 -0
  474. tom_targets/tests/tests.py +2066 -0
  475. tom_targets/urls.py +59 -0
  476. tom_targets/utils.py +156 -0
  477. tom_targets/validators.py +23 -0
  478. tom_targets/views.py +1124 -0
  479. tom_targets/viewsets.py +49 -0
  480. tomtoolkit-2.30.0a0.dist-info/METADATA +164 -0
  481. tomtoolkit-2.30.0a0.dist-info/RECORD +483 -0
  482. tomtoolkit-2.30.0a0.dist-info/WHEEL +4 -0
  483. tomtoolkit-2.30.0a0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,636 @@
1
+ import logging
2
+ import requests
3
+ from urllib.parse import urlencode
4
+
5
+ from astropy.time import Time, TimezoneInfo
6
+ from crispy_forms.layout import Column, Fieldset, HTML, Layout, Row
7
+ from django import forms
8
+ from django.core.cache import cache
9
+
10
+
11
+ from tom_alerts.alerts import GenericAlert, GenericBroker, GenericQueryForm
12
+ from tom_targets.models import Target
13
+ from tom_dataproducts.models import ReducedDatum
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ ALERCE_URL = 'https://alerce.online'
18
+ ALERCE_SEARCH_URL = 'https://api.alerce.online/ztf/v1'
19
+ ALERCE_CLASSES_URL = f'{ALERCE_SEARCH_URL}/classifiers'
20
+
21
+ SORT_CHOICES = [(None, 'None'),
22
+ ('oid', 'Object ID'),
23
+ ('probability', 'Classifier Probability'),
24
+ ('ndet', 'Number of Detections'),
25
+ ('firstmjd', 'First Detection'),
26
+ ('lastmjd', 'Last Detection'),
27
+ ('deltamjd', 'Delta MJD (days)')
28
+ ]
29
+
30
+ SORT_ORDER = [(None, 'None'),
31
+ ('ASC', 'Ascending'),
32
+ ('DESC', 'Descending')]
33
+ FILTERS = {1: 'g', 2: 'r', 3: 'i'}
34
+
35
+
36
+ class ALeRCEQueryForm(GenericQueryForm):
37
+
38
+ oid = forms.CharField(
39
+ required=False,
40
+ label='Object ID',
41
+ )
42
+ ndet = forms.IntegerField(
43
+ required=False,
44
+ label='Detections Lower',
45
+ widget=forms.TextInput(attrs={'placeholder': 'Min number of detections'})
46
+ )
47
+ ranking = forms.IntegerField(
48
+ required=False,
49
+ label='Ranking',
50
+ widget=forms.TextInput(attrs={'placeholder': 'Class ordering by probability'})
51
+ )
52
+ lc_classifier = forms.ChoiceField(
53
+ required=False,
54
+ label='Light Curve Classifier Class',
55
+ choices=[], # Choices are populated dynamically in the constructor
56
+ )
57
+ p_lc_classifier = forms.FloatField(
58
+ required=False,
59
+ label='Light Curve Classifier Probability'
60
+ )
61
+ lc_classifier_top = forms.ChoiceField(
62
+ required=False,
63
+ label='Light Curve Classifier Top Class',
64
+ choices=[], # Choices are populated dynamically in the constructor
65
+ )
66
+ p_lc_classifier_top = forms.FloatField(
67
+ required=False,
68
+ label='Light Curve Classifier Top Probability'
69
+ )
70
+ lc_classifier_bhrf = forms.ChoiceField(
71
+ required=False,
72
+ label='Light Curve Classifier BHRF Forced Phot Class',
73
+ choices=[], # Choices are populated dynamically in the constructor
74
+ )
75
+ p_lc_classifier_bhrf = forms.FloatField(
76
+ required=False,
77
+ label='Light Curve Classifier BHRF Forced Phot Probability'
78
+ )
79
+ lc_classifier_bhrf_top = forms.ChoiceField(
80
+ required=False,
81
+ label='Light Curve Classifier BHRF Forced Phot Top Class',
82
+ choices=[], # Choices are populated dynamically in the constructor
83
+ )
84
+ p_lc_classifier_bhrf_top = forms.FloatField(
85
+ required=False,
86
+ label='Light Curve Classifier BHRF Forced Phot Top Probability'
87
+ )
88
+ lc_classifier_atat = forms.ChoiceField(
89
+ required=False,
90
+ label='Light Curve Classifier ATAT Forced Phot Beta Class',
91
+ choices=[], # Choices are populated dynamically in the constructor
92
+ )
93
+ p_lc_classifier_atat = forms.FloatField(
94
+ required=False,
95
+ label='Light Curve Classifier ATAT Forced Phot Beta Probability'
96
+ )
97
+ stamp_classifier = forms.ChoiceField(
98
+ required=False,
99
+ label='Stamp Class',
100
+ choices=[], # Choices are populated dynamically in the constructor
101
+ )
102
+ p_stamp_classifier = forms.FloatField(
103
+ required=False,
104
+ label='Stamp Classifier Probability'
105
+ )
106
+ stamp_classifier_beta = forms.ChoiceField(
107
+ required=False,
108
+ label='Stamp Classifier 2025 Beta Class',
109
+ choices=[], # Choices are populated dynamically in the constructor
110
+ )
111
+ p_stamp_classifier_beta = forms.FloatField(
112
+ required=False,
113
+ label='Stamp Classifier 2025 Beta Probability'
114
+ )
115
+ ra = forms.FloatField(
116
+ required=False,
117
+ label='RA',
118
+ widget=forms.TextInput(attrs={'placeholder': 'RA (Degrees)'})
119
+ )
120
+ dec = forms.FloatField(
121
+ required=False,
122
+ label='Dec',
123
+ widget=forms.TextInput(attrs={'placeholder': 'Dec (Degrees)'})
124
+ )
125
+ radius = forms.IntegerField(
126
+ required=False,
127
+ label='Search Radius',
128
+ widget=forms.TextInput(attrs={'placeholder': 'Radius (Arcseconds)'})
129
+ )
130
+ firstmjd__gt = forms.FloatField(
131
+ required=False,
132
+ label='Min date of first detection ',
133
+ widget=forms.TextInput(attrs={'placeholder': 'Date (MJD)'}),
134
+ min_value=0.0
135
+ )
136
+ firstmjd__lt = forms.FloatField(
137
+ required=False,
138
+ label='Max date of first detection ',
139
+ widget=forms.TextInput(attrs={'placeholder': 'Date (MJD)'}),
140
+ min_value=0.0
141
+ )
142
+ lastmjd__gt = forms.FloatField(
143
+ required=False,
144
+ label='Min date of last detection',
145
+ widget=forms.TextInput(attrs={'placeholder': 'Date (MJD)'}),
146
+ min_value=0.0
147
+ )
148
+ lastmjd__lt = forms.FloatField(
149
+ required=False,
150
+ label='Max date of last detection',
151
+ widget=forms.TextInput(attrs={'placeholder': 'Date (MJD)'}),
152
+ min_value=0.0
153
+ )
154
+ order_by = forms.ChoiceField(
155
+ choices=SORT_CHOICES,
156
+ required=False,
157
+ label='Sort By'
158
+ )
159
+ order_mode = forms.ChoiceField(
160
+ choices=SORT_ORDER,
161
+ required=False,
162
+ label='Sort Order'
163
+ )
164
+ max_pages = forms.TypedChoiceField(
165
+ choices=[(1, 1), (5, 5), (10, 10), (20, 20)],
166
+ required=False,
167
+ coerce=int,
168
+ label='Maximum pages to retrieve'
169
+ )
170
+
171
+ def __init__(self, *args, **kwargs):
172
+ super().__init__(*args, **kwargs)
173
+
174
+ self.fields['lc_classifier'].choices = self._get_light_curve_classifier_choices()
175
+ self.fields['lc_classifier_top'].choices = self._get_light_curve_classifier_top_choices()
176
+ self.fields['lc_classifier_bhrf'].choices = self._get_light_curve_classifier_bhrf_choices()
177
+ self.fields['lc_classifier_bhrf_top'].choices = self._get_light_curve_classifier_bhrf_top_choices()
178
+ self.fields['lc_classifier_atat'].choices = self._get_light_curve_classifier_atat_choices()
179
+ self.fields['stamp_classifier'].choices = self._get_stamp_classifier_choices()
180
+ self.fields['stamp_classifier_beta'].choices = self._get_stamp_classifier_beta_choices()
181
+
182
+ self.helper.layout = Layout(
183
+ HTML('''
184
+ <p>
185
+ Please see the <a href="http://alerce.science/" target="_blank">ALeRCE homepage</a> for information
186
+ about the ALeRCE filters.
187
+ '''),
188
+ self.common_layout,
189
+ 'oid',
190
+ Fieldset(
191
+ 'Classification Filters',
192
+ Row(
193
+ Column('lc_classifier'),
194
+ Column('p_lc_classifier')
195
+ ),
196
+ Row(
197
+ Column('lc_classifier_top'),
198
+ Column('p_lc_classifier_top')
199
+ ),
200
+ Row(
201
+ Column('lc_classifier_bhrf'),
202
+ Column('p_lc_classifier_bhrf')
203
+ ),
204
+ Row(
205
+ Column('lc_classifier_bhrf_top'),
206
+ Column('p_lc_classifier_bhrf_top')
207
+ ),
208
+ Row(
209
+ Column('lc_classifier_atat'),
210
+ Column('p_lc_classifier_atat')
211
+ ),
212
+ Row(
213
+ Column('stamp_classifier'),
214
+ Column('p_stamp_classifier')
215
+ ),
216
+ Row(
217
+ Column('stamp_classifier_beta'),
218
+ Column('p_stamp_classifier_beta')
219
+ )
220
+ ),
221
+ Fieldset(
222
+ 'Location Filters',
223
+ Row(
224
+ Column('ra'),
225
+ Column('dec'),
226
+ Column('radius'),
227
+ )
228
+ ),
229
+ Fieldset(
230
+ 'Time Filters',
231
+ Row(
232
+ Column('firstmjd__gt'),
233
+ Column('firstmjd__lt'),
234
+ ),
235
+ Row(
236
+ Column('lastmjd__gt'),
237
+ Column('lastmjd__lt'),
238
+ )
239
+ ),
240
+ Fieldset(
241
+ 'Other Filters',
242
+ Row(
243
+ Column('ranking'),
244
+ Column('ndet')
245
+ )
246
+ ),
247
+ Fieldset(
248
+ 'General Parameters',
249
+ Row(
250
+ Column('order_by'),
251
+ Column('order_mode'),
252
+ ),
253
+ Row(
254
+ Column('max_pages')
255
+ )
256
+ ),
257
+ )
258
+
259
+ @staticmethod
260
+ def _get_classifiers():
261
+ cached_classifiers = cache.get('alerce_classifiers')
262
+
263
+ if not cached_classifiers:
264
+ response = requests.get(ALERCE_CLASSES_URL)
265
+ response.raise_for_status()
266
+ cached_classifiers = response.json()
267
+
268
+ return cached_classifiers
269
+
270
+ @staticmethod
271
+ def _get_light_curve_classifier_choices():
272
+ light_curve_classifiers = []
273
+ version = '1.0.0'
274
+ current_version = version
275
+
276
+ # Grab all classifiers available in ALeRCE
277
+ all_classifiers = ALeRCEQueryForm._get_classifiers()
278
+
279
+ for classifier in all_classifiers:
280
+ if classifier['classifier_name'] == 'lc_classifier':
281
+ new_version = classifier['classifier_version'].split('_')[-1]
282
+ if new_version > version:
283
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
284
+ current_version = new_version
285
+
286
+ if current_version == version:
287
+ for classifier in all_classifiers:
288
+ split_class = classifier['classifier_name'].split('_')
289
+ if len(split_class) == 3 and split_class[0] == 'lc' and split_class[2] != 'top':
290
+ subclass = split_class[2].capitalize()
291
+ light_curve_classifiers += [(c, f'{subclass} - {c}') for c in classifier['classes']]
292
+
293
+ return [(None, '')] + light_curve_classifiers
294
+
295
+ @staticmethod
296
+ def _get_light_curve_classifier_top_choices():
297
+ light_curve_classifiers = []
298
+ version = '1.0.0'
299
+
300
+ # Grab all classifiers available in ALeRCE
301
+ all_classifiers = ALeRCEQueryForm._get_classifiers()
302
+
303
+ for classifier in all_classifiers:
304
+ if classifier['classifier_name'] == 'lc_classifier_top':
305
+ new_version = classifier['classifier_version'].split('_')[-1]
306
+ if new_version > version:
307
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
308
+ else:
309
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
310
+
311
+ return [(None, '')] + light_curve_classifiers
312
+
313
+ @staticmethod
314
+ def _get_light_curve_classifier_bhrf_choices():
315
+ light_curve_classifiers = []
316
+ version = '2.1.0'
317
+ current_version = version
318
+
319
+ # Grab all classifiers available in ALeRCE
320
+ all_classifiers = ALeRCEQueryForm._get_classifiers()
321
+
322
+ for classifier in all_classifiers:
323
+ if classifier['classifier_name'] == 'lc_classifier_BHRF_forced_phot':
324
+ new_version = classifier['classifier_version'].split('_')[-1]
325
+ if new_version > version:
326
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
327
+ current_version = new_version
328
+
329
+ if current_version == version:
330
+ for classifier in all_classifiers:
331
+ split_class = classifier['classifier_name'].split('_')
332
+ if len(split_class) == 3 and split_class[0] == 'lc' and split_class[2] != 'top':
333
+ subclass = split_class[2].capitalize()
334
+ light_curve_classifiers += [(c, f'{subclass} - {c}') for c in classifier['classes']]
335
+
336
+ return [(None, '')] + light_curve_classifiers
337
+
338
+ @staticmethod
339
+ def _get_light_curve_classifier_bhrf_top_choices():
340
+ light_curve_classifiers = []
341
+ version = '1.0.0'
342
+
343
+ # Grab all classifiers available in ALeRCE
344
+ all_classifiers = ALeRCEQueryForm._get_classifiers()
345
+
346
+ for classifier in all_classifiers:
347
+ if classifier['classifier_name'] == 'lc_classifier_BHRF_forced_phot_top':
348
+ new_version = classifier['classifier_version'].split('_')[-1]
349
+ if new_version > version:
350
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
351
+ else:
352
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
353
+
354
+ return [(None, '')] + light_curve_classifiers
355
+
356
+ @staticmethod
357
+ def _get_light_curve_classifier_atat_choices():
358
+ version = 'beta'
359
+ light_curve_classifiers = []
360
+ for classifier in ALeRCEQueryForm._get_classifiers():
361
+ if classifier['classifier_name'] == 'LC_classifier_ATAT_forced_phot(beta)':
362
+ new_version = classifier['classifier_version'].split('_')[-1]
363
+ if new_version > version:
364
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
365
+ else:
366
+ light_curve_classifiers = [(c, c) for c in classifier['classes']]
367
+
368
+ return [(None, '')] + light_curve_classifiers
369
+
370
+ @staticmethod
371
+ def _get_stamp_classifier_choices():
372
+ version = '1.0.4'
373
+ stamp_classifiers = []
374
+
375
+ for classifier in ALeRCEQueryForm._get_classifiers():
376
+ if classifier['classifier_name'] == 'stamp_classifier':
377
+ new_version = classifier['classifier_version'].split('_')[-1]
378
+ if new_version > version:
379
+ stamp_classifiers = [(c, c) for c in classifier['classes']]
380
+ else:
381
+ stamp_classifiers = [(c, c) for c in classifier['classes']]
382
+
383
+ return [(None, '')] + stamp_classifiers
384
+
385
+ @staticmethod
386
+ def _get_stamp_classifier_beta_choices():
387
+ version = 'beta'
388
+ stamp_classifiers = []
389
+
390
+ for classifier in ALeRCEQueryForm._get_classifiers():
391
+ if classifier['classifier_name'] == 'stamp_classifier_2025_beta':
392
+ new_version = classifier['classifier_version'].split('_')[-1]
393
+ if new_version > version:
394
+ stamp_classifiers = [(c, c) for c in classifier['classes']]
395
+ else:
396
+ stamp_classifiers = [(c, c) for c in classifier['classes']]
397
+
398
+ return [(None, '')] + stamp_classifiers
399
+
400
+ def clean_max_pages(self):
401
+ max_pages = self.cleaned_data['max_pages']
402
+ if not max_pages:
403
+ max_pages = 1
404
+ return max_pages
405
+
406
+ def clean(self):
407
+ cleaned_data = super().clean()
408
+
409
+ # Ensure that all cone search fields are present
410
+ if (any(cleaned_data[k] for k in ['ra', 'dec', 'radius'])
411
+ and not all(cleaned_data[k] for k in ['ra', 'dec', 'radius'])):
412
+ raise forms.ValidationError('All of RA, Dec, and Search Radius must be included to execute a cone search.')
413
+
414
+ # Ensure that only one classification set is filled in
415
+ if (any(cleaned_data.get(k) for k in ['lc_classifier', 'p_lc_classifier'])
416
+ and any(cleaned_data.get(k) for k in ['stamp_classifier', 'p_stamp_classifier'])):
417
+ raise forms.ValidationError('Only one of either light curve or stamp classification may be used as a '
418
+ 'filter.')
419
+
420
+ return cleaned_data
421
+
422
+
423
+ class ALeRCEBroker(GenericBroker):
424
+ """
425
+ The ``ALeRCEBroker`` is the interface to the ALeRCE alert broker.
426
+
427
+ To include the ``ALeRCEBroker`` in your TOM, add the broker module location to your `TOM_ALERT_CLASSES` list in
428
+ your ``settings.py``:
429
+
430
+ .. code-block:: python
431
+
432
+ TOM_ALERT_CLASSES = [
433
+ 'tom_alerts.brokers.alerce.ALeRCEBroker',
434
+ ...
435
+ ]
436
+
437
+ For information regarding the ALeRCE objects
438
+ and classification, please see http://alerce.science.
439
+ """
440
+
441
+ name = 'ALeRCE'
442
+ form = ALeRCEQueryForm
443
+
444
+ def _clean_classifier_parameters(self, parameters):
445
+ """
446
+ This method returns a parameter list for a given classifier with a
447
+ cleaned up version of the ALeRCE classifier names.
448
+ The function finds the ALeRCE classifier name (predefined here) and
449
+ matches it to the TOM Toolkit ALeRCE broker classifier name. Then it
450
+ appends the classifier_parameter list with the parameters specified in
451
+ the TOM Toolkit ALeRCE broker query definition.
452
+
453
+ List of available classifiers can be seen here: https://api.alerce.online/ztf/v1/classifiers/
454
+
455
+ :param parameters:
456
+ :return: classifier parameters tuple that holds strings with cleaned up versions of
457
+ available classifiers.
458
+ """
459
+
460
+ classifier_parameters = []
461
+ class_type = ''
462
+
463
+ if parameters['stamp_classifier']:
464
+ class_type = 'stamp_classifier'
465
+ elif parameters.get('stamp_classifier_beta'):
466
+ class_type = 'stamp_classifier_2025_beta'
467
+ elif parameters['lc_classifier']:
468
+ class_type = 'lc_classifier'
469
+ elif parameters.get('lc_classifier_top'):
470
+ class_type = 'lc_classifier_top'
471
+ elif parameters.get('lc_classifier_bhrf'):
472
+ class_type = 'lc_classifier_BHRF_forced_phot'
473
+ elif parameters.get('lc_classifier_bhrf_top'):
474
+ class_type = 'lc_classifier_BHRF_forced_phot_top'
475
+ elif parameters.get('lc_classifier_atat'):
476
+ class_type = 'LC_classifier_ATAT_forced_phot(beta)'
477
+
478
+ if class_type:
479
+ classifier_parameters.append(('classifier', class_type))
480
+ if class_type in parameters and parameters[class_type] is not None:
481
+ classifier_parameters.append(('class', parameters[class_type]))
482
+ if f'p_{class_type}' in parameters and parameters[f'p_{class_type}'] is not None:
483
+ classifier_parameters.append(('probability', parameters[f'p_{class_type}']))
484
+
485
+ return classifier_parameters
486
+
487
+ def _clean_coordinate_parameters(self, parameters):
488
+ if all([parameters['ra'], parameters['dec'], parameters['radius']]):
489
+ return [
490
+ ('ra', parameters['ra']),
491
+ ('dec', parameters['dec']),
492
+ ('radius', parameters['radius'])
493
+ ]
494
+ else:
495
+ return []
496
+
497
+ def _clean_date_parameters(self, parameters):
498
+ dates = []
499
+
500
+ dates += [('firstmjd', v) for k, v in parameters.items() if 'firstmjd' in k and v]
501
+ dates += [('lastmjd', v) for k, v in parameters.items() if 'lastmjd' in k and v]
502
+
503
+ return dates
504
+
505
+ def _clean_parameters(self, parameters):
506
+ payload = [
507
+ (k, v) for k, v in parameters.items() if k in ['oid', 'ndet', 'ranking', 'order_by', 'order_mode'] and v
508
+ ]
509
+
510
+ payload += [
511
+ ('page', parameters.get('page', 1)),
512
+ ('page_size', 20),
513
+ ]
514
+
515
+ payload += self._clean_classifier_parameters(parameters)
516
+
517
+ payload += self._clean_coordinate_parameters(parameters)
518
+
519
+ payload += self._clean_date_parameters(parameters)
520
+
521
+ return payload
522
+
523
+ def _request_alerts(self, parameters):
524
+ payload = self._clean_parameters(parameters)
525
+ logger.log(msg=f'Fetching alerts from ALeRCE with payload {payload}', level=logging.INFO)
526
+ args = urlencode(payload)
527
+ response = requests.get(f'{ALERCE_SEARCH_URL}/objects/?count=false&{args}')
528
+ response.raise_for_status()
529
+ return response.json()
530
+
531
+ def fetch_alerts(self, parameters):
532
+ """
533
+ Loop through pages of ALeRCE alerts until we reach the maximum pages requested.
534
+ This simply concatenates all alerts from n pages into a single iterable to be returned.
535
+ """
536
+ response = self._request_alerts(parameters)
537
+ alerts = response['items']
538
+ broker_feedback = ''
539
+ current_page = parameters.get('page', 1)
540
+ if len(alerts) > 0 and current_page < parameters.get('max_pages', 1):
541
+ # make new request for the next page. (by recursion)
542
+ parameters['page'] = current_page + 1
543
+ alerts += self.fetch_alerts(parameters)[0]
544
+ # Bottom out of recursion and return accumulated alerts
545
+ return iter(alerts), broker_feedback
546
+
547
+ def fetch_alert(self, alert_id):
548
+ """
549
+ The response for a single alert is as follows:
550
+
551
+ .. code-block:: python
552
+
553
+ {
554
+ 'oid':'ZTF20acnsdjd',
555
+ ...
556
+ 'firstmjd':59149.1119328998,
557
+ ...
558
+ }
559
+
560
+ """
561
+ response = requests.get(f'{ALERCE_SEARCH_URL}/objects/{alert_id}')
562
+ response.raise_for_status()
563
+ return response.json()
564
+
565
+ def fetch_lightcurve(self, oid):
566
+ response = requests.get(f'{ALERCE_SEARCH_URL}/objects/{oid}/lightcurve')
567
+ response.raise_for_status()
568
+ return response.json()
569
+
570
+ def process_reduced_data(self, target, alert=None):
571
+ oid = target.name
572
+ lightcurve = self.fetch_lightcurve(oid)
573
+
574
+ for detection in lightcurve['detections']:
575
+ mjd = Time(detection['mjd'], format='mjd', scale='utc')
576
+ value = {
577
+ 'filter': FILTERS[detection['fid']],
578
+ 'magnitude': detection['diffmaglim'],
579
+ 'error': detection['sigmapsf'],
580
+ 'telescope': 'ZTF',
581
+ }
582
+ ReducedDatum.objects.get_or_create(
583
+ timestamp=mjd.to_datetime(TimezoneInfo()),
584
+ value=value,
585
+ source_name=self.name,
586
+ source_location=oid,
587
+ data_type='photometry',
588
+ target=target
589
+ )
590
+
591
+ for non_detection in lightcurve['non_detections']:
592
+ mjd = Time(non_detection['mjd'], format='mjd', scale='utc')
593
+ value = {
594
+ 'filter': FILTERS[non_detection['fid']],
595
+ 'limit': non_detection['diffmaglim'],
596
+ 'telescope': 'ZTF',
597
+ }
598
+ ReducedDatum.objects.get_or_create(
599
+ timestamp=mjd.to_datetime(TimezoneInfo()),
600
+ value=value,
601
+ source_name=self.name,
602
+ source_location=oid,
603
+ data_type='photometry',
604
+ target=target
605
+ )
606
+
607
+ def to_target(self, alert):
608
+ return Target.objects.create(
609
+ name=alert['oid'],
610
+ type='SIDEREAL',
611
+ ra=alert['meanra'],
612
+ dec=alert['meandec']
613
+ )
614
+
615
+ # TODO: the generic alert is clearly not sufficient for ALeRCE's classifications
616
+ def to_generic_alert(self, alert):
617
+ if alert['lastmjd']:
618
+ timestamp = Time(alert['lastmjd'], format='mjd', scale='utc').to_datetime(timezone=TimezoneInfo())
619
+ else:
620
+ timestamp = ''
621
+ url = f'{ALERCE_URL}/object/{alert["oid"]}'
622
+
623
+ mag = None # mag is no longer returned in the object list
624
+
625
+ score = alert['probability']
626
+
627
+ return GenericAlert(
628
+ timestamp=timestamp,
629
+ url=url,
630
+ id=alert['oid'],
631
+ name=alert['oid'],
632
+ ra=alert['meanra'],
633
+ dec=alert['meandec'],
634
+ mag=mag,
635
+ score=score
636
+ )
@@ -0,0 +1,50 @@
1
+ from crispy_forms.layout import Layout, HTML
2
+
3
+ from tom_alerts.alerts import GenericBroker, GenericQueryForm, GenericAlert
4
+
5
+
6
+ class ANTARESQueryForm(GenericQueryForm):
7
+ def __init__(self, *args, **kwargs):
8
+ super().__init__(*args, **kwargs)
9
+ self.helper.inputs.pop()
10
+ self.helper.layout = Layout(
11
+ HTML('''
12
+ <p>
13
+ This plugin is a stub for the ANTARES plugin. In order to install the full plugin, please see the
14
+ instructions <a href="https://github.com/TOMToolkit/tom_antares" target="_blank">here</a>.
15
+ </p>
16
+ '''),
17
+ HTML('''<a class="btn btn-outline-primary" href={% url 'tom_alerts:list' %}>Back</a>''')
18
+ )
19
+
20
+
21
+ class ANTARESBroker(GenericBroker):
22
+ """
23
+ In order to install the Antares Broker plugin, please see
24
+ these instructions at https://github.com/TOMToolkit/tom_antares.
25
+
26
+ There is a known compatibility issue with antares-client required for the TOM_Antares Broker.
27
+ The antares-client requires the librdkafka library to be installed in order to be compatible with Python 3.10.
28
+ You can learn more about this on the
29
+ antares-client website (https://nsf-noirlab.gitlab.io/csdc/antares/client/installation.html) .
30
+ """
31
+ name = 'ANTARES'
32
+ form = ANTARESQueryForm
33
+
34
+ def fetch_alerts(self, parameters):
35
+ return iter([]), ''
36
+
37
+ def process_reduced_data(self, target, alert=None):
38
+ return
39
+
40
+ def to_generic_alert(self, alert):
41
+ return GenericAlert(
42
+ timestamp=None,
43
+ url=None,
44
+ id=None,
45
+ name=None,
46
+ ra=None,
47
+ dec=None,
48
+ mag=None,
49
+ score=None
50
+ )