igs-slm 0.1.0b0__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 (447) hide show
  1. igs_slm-0.1.0b0.dist-info/LICENSE +21 -0
  2. igs_slm-0.1.0b0.dist-info/METADATA +151 -0
  3. igs_slm-0.1.0b0.dist-info/RECORD +447 -0
  4. igs_slm-0.1.0b0.dist-info/WHEEL +4 -0
  5. igs_slm-0.1.0b0.dist-info/entry_points.txt +3 -0
  6. igs_tools/__init__.py +0 -0
  7. igs_tools/connection.py +88 -0
  8. igs_tools/defines/__init__.py +8 -0
  9. igs_tools/defines/constellation.py +21 -0
  10. igs_tools/defines/data_center.py +75 -0
  11. igs_tools/defines/rinex.py +49 -0
  12. igs_tools/directory.py +247 -0
  13. igs_tools/utils.py +66 -0
  14. slm/__init__.py +21 -0
  15. slm/admin.py +674 -0
  16. slm/api/edit/__init__.py +0 -0
  17. slm/api/edit/serializers.py +316 -0
  18. slm/api/edit/views.py +1632 -0
  19. slm/api/fields.py +89 -0
  20. slm/api/filter.py +504 -0
  21. slm/api/pagination.py +55 -0
  22. slm/api/permissions.py +65 -0
  23. slm/api/public/__init__.py +0 -0
  24. slm/api/public/serializers.py +249 -0
  25. slm/api/public/views.py +606 -0
  26. slm/api/serializers.py +132 -0
  27. slm/api/views.py +148 -0
  28. slm/apps.py +323 -0
  29. slm/authentication.py +198 -0
  30. slm/bin/__init__.py +0 -0
  31. slm/bin/startproject.py +262 -0
  32. slm/bin/templates/{{ project_dir }}/pyproject.toml +35 -0
  33. slm/bin/templates/{{ project_dir }}/sites/__init__.py +0 -0
  34. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/__init__.py +0 -0
  35. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/base.py +15 -0
  36. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/__init__.py +56 -0
  37. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/local.py +4 -0
  38. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/wsgi.py +16 -0
  39. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/manage.py +34 -0
  40. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/production/__init__.py +61 -0
  41. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/production/wsgi.py +16 -0
  42. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/urls.py +7 -0
  43. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/validation.py +11 -0
  44. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/__init__.py +0 -0
  45. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/admin.py +5 -0
  46. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/apps.py +14 -0
  47. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/__init__.py +0 -0
  48. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/commands/__init__.py +0 -0
  49. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/commands/import_archive.py +64 -0
  50. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/migrations/__init__.py +0 -0
  51. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/models.py +6 -0
  52. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/templates/slm/base.html +8 -0
  53. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/urls.py +10 -0
  54. slm/bin/templates/{{ project_dir }}/{{ extension_app }}/views.py +5 -0
  55. slm/defines/AlertLevel.py +24 -0
  56. slm/defines/AntennaCalibration.py +25 -0
  57. slm/defines/AntennaFeatures.py +27 -0
  58. slm/defines/AntennaReferencePoint.py +22 -0
  59. slm/defines/Aspiration.py +13 -0
  60. slm/defines/CardinalDirection.py +19 -0
  61. slm/defines/CollocationStatus.py +12 -0
  62. slm/defines/EquipmentState.py +22 -0
  63. slm/defines/FlagSeverity.py +14 -0
  64. slm/defines/FractureSpacing.py +15 -0
  65. slm/defines/FrequencyStandardType.py +15 -0
  66. slm/defines/GeodesyMLVersion.py +48 -0
  67. slm/defines/ISOCountry.py +1194 -0
  68. slm/defines/Instrumentation.py +19 -0
  69. slm/defines/LogEntryType.py +30 -0
  70. slm/defines/SLMFileType.py +18 -0
  71. slm/defines/SiteFileUploadStatus.py +61 -0
  72. slm/defines/SiteLogFormat.py +49 -0
  73. slm/defines/SiteLogStatus.py +78 -0
  74. slm/defines/TectonicPlates.py +28 -0
  75. slm/defines/__init__.py +46 -0
  76. slm/forms.py +1126 -0
  77. slm/jinja2/slm/sitelog/ascii_9char.log +346 -0
  78. slm/jinja2/slm/sitelog/legacy.log +346 -0
  79. slm/jinja2/slm/sitelog/xsd/0.4/collocationInformation.xml +12 -0
  80. slm/jinja2/slm/sitelog/xsd/0.4/condition.xml +12 -0
  81. slm/jinja2/slm/sitelog/xsd/0.4/contact.xml +52 -0
  82. slm/jinja2/slm/sitelog/xsd/0.4/formInformation.xml +5 -0
  83. slm/jinja2/slm/sitelog/xsd/0.4/frequencyStandard.xml +12 -0
  84. slm/jinja2/slm/sitelog/xsd/0.4/gnssAntenna.xml +16 -0
  85. slm/jinja2/slm/sitelog/xsd/0.4/gnssReceiver.xml +11 -0
  86. slm/jinja2/slm/sitelog/xsd/0.4/humiditySensor.xml +13 -0
  87. slm/jinja2/slm/sitelog/xsd/0.4/localEpisodicEffect.xml +10 -0
  88. slm/jinja2/slm/sitelog/xsd/0.4/moreInformation.xml +22 -0
  89. slm/jinja2/slm/sitelog/xsd/0.4/multipathSource.xml +10 -0
  90. slm/jinja2/slm/sitelog/xsd/0.4/otherInstrumentation.xml +5 -0
  91. slm/jinja2/slm/sitelog/xsd/0.4/pressureSensor.xml +12 -0
  92. slm/jinja2/slm/sitelog/xsd/0.4/radioInterference.xml +11 -0
  93. slm/jinja2/slm/sitelog/xsd/0.4/sensor.xml +16 -0
  94. slm/jinja2/slm/sitelog/xsd/0.4/signalObstruction.xml +10 -0
  95. slm/jinja2/slm/sitelog/xsd/0.4/siteIdentification.xml +22 -0
  96. slm/jinja2/slm/sitelog/xsd/0.4/siteLocation.xml +21 -0
  97. slm/jinja2/slm/sitelog/xsd/0.4/surveyedLocalTie.xml +20 -0
  98. slm/jinja2/slm/sitelog/xsd/0.4/temperatureSensor.xml +13 -0
  99. slm/jinja2/slm/sitelog/xsd/0.4/waterVaporSensor.xml +11 -0
  100. slm/jinja2/slm/sitelog/xsd/0.5/document.xml +10 -0
  101. slm/jinja2/slm/sitelog/xsd/geodesyml_0.4.xml +99 -0
  102. slm/jinja2/slm/sitelog/xsd/geodesyml_0.5.xml +112 -0
  103. slm/management/__init__.py +0 -0
  104. slm/management/commands/__init__.py +53 -0
  105. slm/management/commands/build_index.py +96 -0
  106. slm/management/commands/generate_sinex.py +675 -0
  107. slm/management/commands/head_from_index.py +541 -0
  108. slm/management/commands/import_archive.py +908 -0
  109. slm/management/commands/import_equipment.py +351 -0
  110. slm/management/commands/set_site.py +56 -0
  111. slm/management/commands/sitelog.py +144 -0
  112. slm/management/commands/synchronize.py +60 -0
  113. slm/management/commands/update_data_availability.py +167 -0
  114. slm/management/commands/validate_db.py +186 -0
  115. slm/management/commands/validate_gml.py +73 -0
  116. slm/map/__init__.py +1 -0
  117. slm/map/admin.py +5 -0
  118. slm/map/api/__init__.py +0 -0
  119. slm/map/api/edit/__init__.py +0 -0
  120. slm/map/api/edit/serializers.py +28 -0
  121. slm/map/api/edit/views.py +46 -0
  122. slm/map/api/public/__init__.py +0 -0
  123. slm/map/api/public/serializers.py +29 -0
  124. slm/map/api/public/views.py +64 -0
  125. slm/map/apps.py +7 -0
  126. slm/map/defines.py +53 -0
  127. slm/map/migrations/0001_initial.py +115 -0
  128. slm/map/migrations/__init__.py +0 -0
  129. slm/map/models.py +63 -0
  130. slm/map/static/slm/css/map.css +86 -0
  131. slm/map/static/slm/js/map.js +159 -0
  132. slm/map/templates/slm/map.html +374 -0
  133. slm/map/templates/slm/station/base.html +11 -0
  134. slm/map/templates/slm/station/edit.html +10 -0
  135. slm/map/templates/slm/top_nav.html +17 -0
  136. slm/map/templatetags/__init__.py +0 -0
  137. slm/map/templatetags/slm_map.py +18 -0
  138. slm/map/urls.py +25 -0
  139. slm/map/views.py +36 -0
  140. slm/middleware.py +29 -0
  141. slm/migrations/0001_alter_siteantenna_marker_enu_alter_sitelocation_llh_and_more.py +47 -0
  142. slm/migrations/0001_initial.py +4826 -0
  143. slm/migrations/0002_alter_dataavailability_site.py +22 -0
  144. slm/migrations/0003_remove_logentry_slm_logentr_site_lo_7a2af7_idx_and_more.py +80 -0
  145. slm/migrations/0004_alter_logentry_timestamp_and_more.py +25 -0
  146. slm/migrations/0005_alter_logentry_options_alter_logentry_section_and_more.py +46 -0
  147. slm/migrations/0006_alter_logentry_options_alter_logentry_index_together.py +24 -0
  148. slm/migrations/0007_alter_dataavailability_rate.py +23 -0
  149. slm/migrations/0008_alter_archiveindex_options_and_more.py +64 -0
  150. slm/migrations/0009_alter_archiveindex_end.py +21 -0
  151. slm/migrations/0010_alter_dataavailability_rinex_version_and_more.py +844 -0
  152. slm/migrations/0011_alter_siteidentification_fracture_spacing.py +33 -0
  153. slm/migrations/0012_alter_logentry_type.py +36 -0
  154. slm/migrations/0013_unpublishedfilesalert.py +48 -0
  155. slm/migrations/0014_sitelogpublished.py +48 -0
  156. slm/migrations/0015_alter_siteantenna_options_and_more.py +181 -0
  157. slm/migrations/0016_alter_antenna_description_alter_radome_description_and_more.py +42 -0
  158. slm/migrations/0017_alter_logentry_unique_together_and_more.py +54 -0
  159. slm/migrations/0018_afix_deleted.py +34 -0
  160. slm/migrations/0018_alter_siteantenna_options_and_more.py +244 -0
  161. slm/migrations/0019_remove_siteantenna_marker_enu_siteantenna_marker_une_and_more.py +101 -0
  162. slm/migrations/0020_alter_manufacturer_options.py +16 -0
  163. slm/migrations/0021_alter_siteform_report_type.py +23 -0
  164. slm/migrations/0022_rename_antcal_antenna_radome_slm_antcal_antenna_20827a_idx_and_more.py +297 -0
  165. slm/migrations/0023_archivedsitelog_gml_version_and_more.py +55 -0
  166. slm/migrations/0024_alter_agency_name_alter_agency_shortname.py +24 -0
  167. slm/migrations/0025_alter_archivedsitelog_log_format_and_more.py +61 -0
  168. slm/migrations/0026_alter_archivedsitelog_log_format_and_more.py +61 -0
  169. slm/migrations/0027_importalert_file_contents_importalert_findings_and_more.py +41 -0
  170. slm/migrations/0028_antenna_replaced_manufacturer_url_radome_replaced_and_more.py +46 -0
  171. slm/migrations/0029_manufacturer_full_name.py +17 -0
  172. slm/migrations/0030_alter_antenna_state_alter_radome_state_and_more.py +43 -0
  173. slm/migrations/__init__.py +0 -0
  174. slm/migrations/load_satellitesystems.py +27 -0
  175. slm/models/__init__.py +118 -0
  176. slm/models/about.py +14 -0
  177. slm/models/alerts.py +1204 -0
  178. slm/models/data.py +58 -0
  179. slm/models/equipment.py +229 -0
  180. slm/models/help.py +14 -0
  181. slm/models/index.py +428 -0
  182. slm/models/sitelog.py +4279 -0
  183. slm/models/system.py +723 -0
  184. slm/models/user.py +304 -0
  185. slm/parsing/__init__.py +786 -0
  186. slm/parsing/legacy/__init__.py +4 -0
  187. slm/parsing/legacy/binding.py +817 -0
  188. slm/parsing/legacy/parser.py +377 -0
  189. slm/parsing/xsd/__init__.py +34 -0
  190. slm/parsing/xsd/binding.py +86 -0
  191. slm/parsing/xsd/geodesyml/0.4/commonTypes.xsd +133 -0
  192. slm/parsing/xsd/geodesyml/0.4/contact.xsd +29 -0
  193. slm/parsing/xsd/geodesyml/0.4/dataStreams.xsd +129 -0
  194. slm/parsing/xsd/geodesyml/0.4/document.xsd +64 -0
  195. slm/parsing/xsd/geodesyml/0.4/equipment.xsd +427 -0
  196. slm/parsing/xsd/geodesyml/0.4/fieldMeasurement.xsd +170 -0
  197. slm/parsing/xsd/geodesyml/0.4/geodesyML.xsd +71 -0
  198. slm/parsing/xsd/geodesyml/0.4/geodeticEquipment.xsd +343 -0
  199. slm/parsing/xsd/geodesyml/0.4/geodeticMonument.xsd +147 -0
  200. slm/parsing/xsd/geodesyml/0.4/lineage.xsd +614 -0
  201. slm/parsing/xsd/geodesyml/0.4/localInterferences.xsd +131 -0
  202. slm/parsing/xsd/geodesyml/0.4/measurement.xsd +473 -0
  203. slm/parsing/xsd/geodesyml/0.4/monumentInfo.xsd +251 -0
  204. slm/parsing/xsd/geodesyml/0.4/observationSystem.xsd +429 -0
  205. slm/parsing/xsd/geodesyml/0.4/project.xsd +38 -0
  206. slm/parsing/xsd/geodesyml/0.4/quality.xsd +176 -0
  207. slm/parsing/xsd/geodesyml/0.4/referenceFrame.xsd +194 -0
  208. slm/parsing/xsd/geodesyml/0.4/siteLog.xsd +71 -0
  209. slm/parsing/xsd/geodesyml/0.5/commonTypes.xsd +133 -0
  210. slm/parsing/xsd/geodesyml/0.5/contact.xsd +29 -0
  211. slm/parsing/xsd/geodesyml/0.5/dataStreams.xsd +129 -0
  212. slm/parsing/xsd/geodesyml/0.5/document.xsd +64 -0
  213. slm/parsing/xsd/geodesyml/0.5/equipment.xsd +427 -0
  214. slm/parsing/xsd/geodesyml/0.5/fieldMeasurement.xsd +170 -0
  215. slm/parsing/xsd/geodesyml/0.5/geodesyML.xsd +71 -0
  216. slm/parsing/xsd/geodesyml/0.5/geodeticEquipment.xsd +343 -0
  217. slm/parsing/xsd/geodesyml/0.5/geodeticMonument.xsd +147 -0
  218. slm/parsing/xsd/geodesyml/0.5/lineage.xsd +614 -0
  219. slm/parsing/xsd/geodesyml/0.5/localInterferences.xsd +131 -0
  220. slm/parsing/xsd/geodesyml/0.5/measurement.xsd +473 -0
  221. slm/parsing/xsd/geodesyml/0.5/monumentInfo.xsd +306 -0
  222. slm/parsing/xsd/geodesyml/0.5/observationSystem.xsd +429 -0
  223. slm/parsing/xsd/geodesyml/0.5/project.xsd +38 -0
  224. slm/parsing/xsd/geodesyml/0.5/quality.xsd +176 -0
  225. slm/parsing/xsd/geodesyml/0.5/referenceFrame.xsd +194 -0
  226. slm/parsing/xsd/geodesyml/0.5/siteLog.xsd +73 -0
  227. slm/parsing/xsd/parser.py +116 -0
  228. slm/parsing/xsd/resolver.py +28 -0
  229. slm/receivers/__init__.py +11 -0
  230. slm/receivers/alerts.py +87 -0
  231. slm/receivers/cleanup.py +41 -0
  232. slm/receivers/event_loggers.py +175 -0
  233. slm/receivers/index.py +67 -0
  234. slm/settings/__init__.py +55 -0
  235. slm/settings/auth.py +15 -0
  236. slm/settings/ckeditor.py +14 -0
  237. slm/settings/debug.py +47 -0
  238. slm/settings/internationalization.py +12 -0
  239. slm/settings/logging.py +113 -0
  240. slm/settings/platform/__init__.py +0 -0
  241. slm/settings/platform/darwin.py +10 -0
  242. slm/settings/rest.py +21 -0
  243. slm/settings/root.py +152 -0
  244. slm/settings/routines.py +43 -0
  245. slm/settings/secrets.py +37 -0
  246. slm/settings/security.py +5 -0
  247. slm/settings/slm.py +188 -0
  248. slm/settings/static_templates.py +53 -0
  249. slm/settings/templates.py +29 -0
  250. slm/settings/uploads.py +8 -0
  251. slm/settings/urls.py +126 -0
  252. slm/settings/validation.py +196 -0
  253. slm/signals.py +250 -0
  254. slm/singleton.py +49 -0
  255. slm/static/rest_framework/css/bootstrap-tweaks.css +204 -0
  256. slm/static/rest_framework/css/bootstrap.min.css +7 -0
  257. slm/static/rest_framework/css/bootstrap.min.css.map +1 -0
  258. slm/static/rest_framework/css/default.css +82 -0
  259. slm/static/rest_framework/css/prettify.css +30 -0
  260. slm/static/rest_framework/docs/css/base.css +344 -0
  261. slm/static/rest_framework/docs/css/highlight.css +125 -0
  262. slm/static/rest_framework/docs/css/jquery.json-view.min.css +11 -0
  263. slm/static/rest_framework/docs/img/favicon.ico +0 -0
  264. slm/static/rest_framework/docs/img/grid.png +0 -0
  265. slm/static/rest_framework/docs/js/api.js +321 -0
  266. slm/static/rest_framework/docs/js/highlight.pack.js +2 -0
  267. slm/static/rest_framework/docs/js/jquery.json-view.min.js +7 -0
  268. slm/static/rest_framework/img/grid.png +0 -0
  269. slm/static/rest_framework/js/ajax-form.js +127 -0
  270. slm/static/rest_framework/js/bootstrap.bundle.min.js +7 -0
  271. slm/static/rest_framework/js/bootstrap.bundle.min.js.map +1 -0
  272. slm/static/rest_framework/js/bootstrap.min.js.map +1 -0
  273. slm/static/rest_framework/js/coreapi-0.1.1.js +2043 -0
  274. slm/static/rest_framework/js/csrf.js +52 -0
  275. slm/static/rest_framework/js/default.js +47 -0
  276. slm/static/rest_framework/js/jquery-3.5.1.min.js +2 -0
  277. slm/static/rest_framework/js/prettify-min.js +28 -0
  278. slm/static/slm/css/admin.css +3 -0
  279. slm/static/slm/css/defines.css +82 -0
  280. slm/static/slm/css/forms.css +1 -0
  281. slm/static/slm/css/style.css +1004 -0
  282. slm/static/slm/img/email-branding.png +0 -0
  283. slm/static/slm/img/favicon.ico +0 -0
  284. slm/static/slm/img/login-bg.jpg +0 -0
  285. slm/static/slm/img/slm-logo.svg +4 -0
  286. slm/static/slm/js/autocomplete.js +341 -0
  287. slm/static/slm/js/enums.js +322 -0
  288. slm/static/slm/js/fileIcons.js +30 -0
  289. slm/static/slm/js/form.js +404 -0
  290. slm/static/slm/js/formWidget.js +23 -0
  291. slm/static/slm/js/persistable.js +33 -0
  292. slm/static/slm/js/slm.js +1028 -0
  293. slm/static/slm/js/time24.js +212 -0
  294. slm/static_templates/slm/css/defines.css +26 -0
  295. slm/static_templates/slm/js/enums.js +28 -0
  296. slm/static_templates/slm/js/fileIcons.js +16 -0
  297. slm/static_templates/slm/js/urls.js +5 -0
  298. slm/templates/account/base.html +20 -0
  299. slm/templates/account/email/base.html +43 -0
  300. slm/templates/account/email/base_message.txt +7 -0
  301. slm/templates/account/email/email_confirmation_message.html +16 -0
  302. slm/templates/account/email/email_confirmation_message.txt +7 -0
  303. slm/templates/account/email/email_confirmation_signup_message.html +1 -0
  304. slm/templates/account/email/email_confirmation_signup_message.txt +1 -0
  305. slm/templates/account/email/email_confirmation_signup_subject.txt +1 -0
  306. slm/templates/account/email/email_confirmation_subject.txt +4 -0
  307. slm/templates/account/email/password_reset_key_message.html +28 -0
  308. slm/templates/account/email/password_reset_key_message.txt +9 -0
  309. slm/templates/account/email/password_reset_key_subject.txt +4 -0
  310. slm/templates/account/email/unknown_account_message.html +25 -0
  311. slm/templates/account/email/unknown_account_message.txt +12 -0
  312. slm/templates/account/email/unknown_account_subject.txt +4 -0
  313. slm/templates/account/login.html +67 -0
  314. slm/templates/account/logout.html +38 -0
  315. slm/templates/account/password_change.html +48 -0
  316. slm/templates/account/password_reset.html +51 -0
  317. slm/templates/account/password_reset_done.html +20 -0
  318. slm/templates/account/password_reset_from_key.html +52 -0
  319. slm/templates/account/password_reset_from_key_done.html +17 -0
  320. slm/templates/admin/base.html +7 -0
  321. slm/templates/messages.html +8 -0
  322. slm/templates/rest_framework/README +16 -0
  323. slm/templates/rest_framework/admin/detail.html +10 -0
  324. slm/templates/rest_framework/admin/dict_value.html +11 -0
  325. slm/templates/rest_framework/admin/list.html +32 -0
  326. slm/templates/rest_framework/admin/list_value.html +11 -0
  327. slm/templates/rest_framework/admin/simple_list_value.html +2 -0
  328. slm/templates/rest_framework/admin.html +282 -0
  329. slm/templates/rest_framework/api.html +3 -0
  330. slm/templates/rest_framework/base.html +334 -0
  331. slm/templates/rest_framework/docs/auth/basic.html +42 -0
  332. slm/templates/rest_framework/docs/auth/session.html +40 -0
  333. slm/templates/rest_framework/docs/auth/token.html +41 -0
  334. slm/templates/rest_framework/docs/document.html +37 -0
  335. slm/templates/rest_framework/docs/error.html +71 -0
  336. slm/templates/rest_framework/docs/index.html +55 -0
  337. slm/templates/rest_framework/docs/interact.html +57 -0
  338. slm/templates/rest_framework/docs/langs/javascript-intro.html +5 -0
  339. slm/templates/rest_framework/docs/langs/javascript.html +15 -0
  340. slm/templates/rest_framework/docs/langs/python-intro.html +3 -0
  341. slm/templates/rest_framework/docs/langs/python.html +13 -0
  342. slm/templates/rest_framework/docs/langs/shell-intro.html +3 -0
  343. slm/templates/rest_framework/docs/langs/shell.html +6 -0
  344. slm/templates/rest_framework/docs/link.html +113 -0
  345. slm/templates/rest_framework/docs/sidebar.html +78 -0
  346. slm/templates/rest_framework/filters/base.html +16 -0
  347. slm/templates/rest_framework/filters/ordering.html +17 -0
  348. slm/templates/rest_framework/filters/search.html +13 -0
  349. slm/templates/rest_framework/horizontal/checkbox.html +23 -0
  350. slm/templates/rest_framework/horizontal/checkbox_multiple.html +32 -0
  351. slm/templates/rest_framework/horizontal/dict_field.html +11 -0
  352. slm/templates/rest_framework/horizontal/fieldset.html +16 -0
  353. slm/templates/rest_framework/horizontal/form.html +6 -0
  354. slm/templates/rest_framework/horizontal/input.html +21 -0
  355. slm/templates/rest_framework/horizontal/list_field.html +11 -0
  356. slm/templates/rest_framework/horizontal/list_fieldset.html +13 -0
  357. slm/templates/rest_framework/horizontal/radio.html +42 -0
  358. slm/templates/rest_framework/horizontal/select.html +36 -0
  359. slm/templates/rest_framework/horizontal/select_multiple.html +38 -0
  360. slm/templates/rest_framework/horizontal/textarea.html +21 -0
  361. slm/templates/rest_framework/inline/checkbox.html +8 -0
  362. slm/templates/rest_framework/inline/checkbox_multiple.html +14 -0
  363. slm/templates/rest_framework/inline/dict_field.html +9 -0
  364. slm/templates/rest_framework/inline/fieldset.html +6 -0
  365. slm/templates/rest_framework/inline/form.html +8 -0
  366. slm/templates/rest_framework/inline/input.html +9 -0
  367. slm/templates/rest_framework/inline/list_field.html +9 -0
  368. slm/templates/rest_framework/inline/list_fieldset.html +3 -0
  369. slm/templates/rest_framework/inline/radio.html +25 -0
  370. slm/templates/rest_framework/inline/select.html +24 -0
  371. slm/templates/rest_framework/inline/select_multiple.html +25 -0
  372. slm/templates/rest_framework/inline/textarea.html +9 -0
  373. slm/templates/rest_framework/login.html +3 -0
  374. slm/templates/rest_framework/login_base.html +65 -0
  375. slm/templates/rest_framework/pagination/numbers.html +47 -0
  376. slm/templates/rest_framework/pagination/previous_and_next.html +21 -0
  377. slm/templates/rest_framework/raw_data_form.html +11 -0
  378. slm/templates/rest_framework/schema.js +3 -0
  379. slm/templates/rest_framework/vertical/checkbox.html +16 -0
  380. slm/templates/rest_framework/vertical/checkbox_multiple.html +30 -0
  381. slm/templates/rest_framework/vertical/dict_field.html +7 -0
  382. slm/templates/rest_framework/vertical/fieldset.html +13 -0
  383. slm/templates/rest_framework/vertical/form.html +6 -0
  384. slm/templates/rest_framework/vertical/input.html +17 -0
  385. slm/templates/rest_framework/vertical/list_field.html +7 -0
  386. slm/templates/rest_framework/vertical/list_fieldset.html +7 -0
  387. slm/templates/rest_framework/vertical/radio.html +40 -0
  388. slm/templates/rest_framework/vertical/select.html +34 -0
  389. slm/templates/rest_framework/vertical/select_multiple.html +31 -0
  390. slm/templates/rest_framework/vertical/textarea.html +17 -0
  391. slm/templates/slm/about.html +21 -0
  392. slm/templates/slm/alerts/alert.html +15 -0
  393. slm/templates/slm/alerts/geodesymlinvalid.html +8 -0
  394. slm/templates/slm/alerts/importalert.html +10 -0
  395. slm/templates/slm/alerts.html +18 -0
  396. slm/templates/slm/auth_menu.html +41 -0
  397. slm/templates/slm/base.html +195 -0
  398. slm/templates/slm/emails/alert_issued.html +31 -0
  399. slm/templates/slm/emails/alert_issued.txt +9 -0
  400. slm/templates/slm/emails/base.html +6 -0
  401. slm/templates/slm/emails/changes_rejected.txt +7 -0
  402. slm/templates/slm/emails/review_requested.txt +7 -0
  403. slm/templates/slm/forms/widgets/auto_complete.html +21 -0
  404. slm/templates/slm/forms/widgets/auto_complete_multiple.html +18 -0
  405. slm/templates/slm/forms/widgets/checkbox_multiple.html +6 -0
  406. slm/templates/slm/forms/widgets/inline_multi.html +1 -0
  407. slm/templates/slm/forms/widgets/splitdatetime.html +14 -0
  408. slm/templates/slm/forms/widgets/time24.html +37 -0
  409. slm/templates/slm/help.html +54 -0
  410. slm/templates/slm/messages.html +13 -0
  411. slm/templates/slm/new_site.html +88 -0
  412. slm/templates/slm/profile.html +57 -0
  413. slm/templates/slm/register.html +40 -0
  414. slm/templates/slm/reports/file_log.html +43 -0
  415. slm/templates/slm/reports/head_log.html +23 -0
  416. slm/templates/slm/reports/head_report.html +55 -0
  417. slm/templates/slm/reports/index_log.html +23 -0
  418. slm/templates/slm/reports/index_report.html +71 -0
  419. slm/templates/slm/station/alert.html +8 -0
  420. slm/templates/slm/station/alerts.html +19 -0
  421. slm/templates/slm/station/base.html +104 -0
  422. slm/templates/slm/station/download.html +87 -0
  423. slm/templates/slm/station/edit.html +283 -0
  424. slm/templates/slm/station/form.html +110 -0
  425. slm/templates/slm/station/log.html +18 -0
  426. slm/templates/slm/station/review.html +461 -0
  427. slm/templates/slm/station/upload.html +295 -0
  428. slm/templates/slm/station/uploads/attachment.html +20 -0
  429. slm/templates/slm/station/uploads/geodesyml.html +1 -0
  430. slm/templates/slm/station/uploads/image.html +27 -0
  431. slm/templates/slm/station/uploads/json.html +0 -0
  432. slm/templates/slm/station/uploads/legacy.html +77 -0
  433. slm/templates/slm/top_nav.html +14 -0
  434. slm/templates/slm/user_activity.html +16 -0
  435. slm/templates/slm/widgets/alert_scroll.html +135 -0
  436. slm/templates/slm/widgets/filelist.html +258 -0
  437. slm/templates/slm/widgets/legend.html +12 -0
  438. slm/templates/slm/widgets/log_scroll.html +88 -0
  439. slm/templates/slm/widgets/stationlist.html +233 -0
  440. slm/templatetags/__init__.py +0 -0
  441. slm/templatetags/jinja2.py +9 -0
  442. slm/templatetags/slm.py +459 -0
  443. slm/urls.py +148 -0
  444. slm/utils.py +299 -0
  445. slm/validators.py +297 -0
  446. slm/views.py +654 -0
  447. slm/widgets.py +134 -0
slm/api/fields.py ADDED
@@ -0,0 +1,89 @@
1
+ from datetime import date, datetime, time, timezone
2
+
3
+ from dateutil import parser
4
+ from django.conf import settings
5
+ from django.contrib.gis.geos import Point
6
+ from django.utils.translation import gettext as _
7
+ from rest_framework.serializers import DateTimeField, Field
8
+
9
+
10
+ class SLMDateTimeField(DateTimeField):
11
+ default_error_messages = {
12
+ "invalid": _("Unable to interpret datetime, please use format: {format}."),
13
+ "parse": _("Please use format: {format}: {error}"),
14
+ }
15
+
16
+ """
17
+ A much more lenient datetime field that uses dateutil to parse. This field
18
+ differs from the vanilla DRF DateTimeField in several ways:
19
+
20
+ 1) dateutil.parser is used to parse incoming strings. This is very lenient.
21
+ 2) Values that are just dates default to default_time if it is set, and
22
+ fail otherwise.
23
+ 3) The timezone is set to UTC unless otherwise given.
24
+
25
+ :param default_time: Use this time for incoming values that are just dates.
26
+ defaults to midnight.
27
+ :param default_timezone: This is the output timezone - defaults to UTC.
28
+ :param kwargs: kwargs for DRF base classes.
29
+ """
30
+
31
+ default_time = time(hour=0, minute=0, second=0)
32
+
33
+ def __init__(
34
+ self, default_time=default_time, default_timezone=timezone.utc, **kwargs
35
+ ):
36
+ self.default_time = default_time
37
+ super().__init__(default_timezone=default_timezone, **kwargs)
38
+
39
+ def default_timezone(self):
40
+ return timezone.utc if settings.USE_TZ else None
41
+
42
+ def to_internal_value(self, value):
43
+ if isinstance(value, date) and not isinstance(value, datetime):
44
+ # assume midnight
45
+ if not self.default_time:
46
+ self.fail("date")
47
+ value = datetime.combine(value, self.default_time)
48
+
49
+ if isinstance(value, datetime):
50
+ return self.enforce_timezone(value)
51
+
52
+ try:
53
+ parsed = parser.parse(value)
54
+ if parsed is not None:
55
+ return self.enforce_timezone(parsed)
56
+ except parser.ParserError as pe:
57
+ self.fail("parse", format="CCYY-MM-DDThh:mmZ", error=str(pe))
58
+ except (ValueError, TypeError):
59
+ pass
60
+
61
+ self.fail("invalid", format="CCYY-MM-DDThh:mmZ")
62
+
63
+
64
+ class SLMPointField(Field):
65
+ default_error_messages = {
66
+ "invalid": _(
67
+ "Unable to interpret point ({data}), please use format: {format}."
68
+ ),
69
+ "missing": _("Must provide all {num} values, received: {received}."),
70
+ }
71
+
72
+ def to_representation(self, value):
73
+ return value.coords
74
+
75
+ def to_internal_value(self, data):
76
+ try:
77
+ coords = [None if coord in ["", None] else float(coord) for coord in data]
78
+ # all values must be null or a number
79
+ if any([coord is None for coord in coords]) and not all(
80
+ [coord is None for coord in coords]
81
+ ):
82
+ self.fail(
83
+ "missing",
84
+ num=len(coords),
85
+ received=len([coord for coord in coords if coord is not None]),
86
+ )
87
+ return Point(*coords) or None
88
+ except (TypeError, ValueError):
89
+ self.fail("invalid", format="[float, float, float]", data=data)
slm/api/filter.py ADDED
@@ -0,0 +1,504 @@
1
+ from collections import namedtuple
2
+
3
+ import django_filters
4
+ from dateutil import parser
5
+ from django.core.exceptions import ValidationError
6
+ from django.db.models import Q
7
+ from django.forms import DateTimeField
8
+ from django.forms.utils import from_current_timezone
9
+ from django.http import QueryDict
10
+ from django.utils.functional import cached_property
11
+ from django.utils.timezone import now
12
+ from django.utils.translation import gettext as _
13
+ from django_filters import (
14
+ BaseInFilter,
15
+ BooleanFilter,
16
+ DateTimeFilter,
17
+ FilterSet,
18
+ NumberFilter,
19
+ compat,
20
+ )
21
+ from django_filters.filterset import BaseFilterSet
22
+ from rest_framework.filters import SearchFilter
23
+
24
+ from slm.defines import AlertLevel, FrequencyStandardType, ISOCountry, SiteLogStatus
25
+ from slm.forms import SLMBooleanField
26
+ from slm.models import (
27
+ Agency,
28
+ Alert,
29
+ Antenna,
30
+ Network,
31
+ Radome,
32
+ Receiver,
33
+ SatelliteSystem,
34
+ Site,
35
+ SiteAntenna,
36
+ SiteIdentification,
37
+ SiteMoreInformation,
38
+ SiteReceiver,
39
+ )
40
+
41
+
42
+ class SiteSearchFilter(SearchFilter):
43
+ """
44
+ For large data textual search will get very slow. The database indexes on
45
+ these fields are not utilized for icontains lookups. PostgreSQL has a
46
+ built in textual search index that can be utilized with __search lookups
47
+ in Django. Alternatively an external solution would have to be implemented
48
+ for mysql.
49
+
50
+ On the current size of the IGS data, icontains does not introduce too large
51
+ of a performance penalty.
52
+ """
53
+
54
+ def filter_queryset(self, request, queryset, view):
55
+ """
56
+ This search was broken out manually because the query generated by
57
+ the ORM using the default DRF SearchFilter did not perform well enough.
58
+
59
+ The basic strategy is to do table text searches separately then pull
60
+ the relevant primary keys for matching sites into a set that the main
61
+ query with annotations and ordering simply requires a pk lookup. The
62
+ order_by() statements below remove unnecessary default ORDER BY
63
+ statements.
64
+
65
+ This strategy could be sped up more by doing the individual table
66
+ searches asynchronously.
67
+ """
68
+ terms = [
69
+ term.lstrip(",").rstrip(",").strip()
70
+ for term in self.get_search_terms(request)
71
+ ]
72
+ searched = Q()
73
+ if terms:
74
+ try:
75
+ epoch = parser.parse(request.GET.get("epoch", None)) or now()
76
+ except (parser.ParserError, TypeError, ValueError):
77
+ epoch = now()
78
+
79
+ for search_term in terms:
80
+ site_ids = set(
81
+ Site.objects.filter(name__icontains=search_term)
82
+ .order_by()
83
+ .values_list("pk", flat=True)
84
+ )
85
+
86
+ site_ids.update(
87
+ Site.objects.filter(
88
+ networks__in=Network.objects.filter(name__icontains=search_term)
89
+ )
90
+ .order_by()
91
+ .distinct()
92
+ .values_list("pk", flat=True)
93
+ )
94
+
95
+ site_ids.update(
96
+ Site.objects.filter(
97
+ agencies__in=Agency.objects.filter(
98
+ Q(name__icontains=search_term)
99
+ | Q(shortname__icontains=search_term)
100
+ )
101
+ )
102
+ .order_by()
103
+ .distinct()
104
+ .values_list("pk", flat=True)
105
+ )
106
+
107
+ site_receivers = SiteReceiver.objects.filter(
108
+ Q(published=True)
109
+ & (
110
+ Q(
111
+ receiver_type__in=Receiver.objects.filter(
112
+ model__icontains=search_term
113
+ )
114
+ )
115
+ | Q(firmware__icontains=search_term)
116
+ | Q(serial_number__icontains=search_term)
117
+ & (
118
+ (Q(installed__lte=epoch) | Q(installed__isnull=True))
119
+ & (Q(removed__gt=epoch) | Q(removed__isnull=True))
120
+ )
121
+ )
122
+ ).order_by()
123
+ site_antennas = SiteAntenna.objects.filter(
124
+ Q(published=True)
125
+ & (
126
+ Q(
127
+ antenna_type__in=Antenna.objects.filter(
128
+ model__icontains=search_term
129
+ )
130
+ )
131
+ | Q(
132
+ radome_type__in=Radome.objects.filter(
133
+ model__icontains=search_term
134
+ )
135
+ )
136
+ & (
137
+ (Q(installed__lte=epoch) | Q(installed__isnull=True))
138
+ & (Q(removed__gt=epoch) | Q(removed__isnull=True))
139
+ )
140
+ )
141
+ ).order_by()
142
+
143
+ site_ids.update(
144
+ Site.objects.filter(siteantenna__in=site_antennas)
145
+ .order_by()
146
+ .distinct()
147
+ .values_list("pk", flat=True)
148
+ )
149
+
150
+ site_ids.update(
151
+ Site.objects.filter(
152
+ siteidentification__in=SiteIdentification.objects.filter(
153
+ Q(published=True)
154
+ & Q(iers_domes_number__icontains=search_term)
155
+ ).order_by()
156
+ )
157
+ .order_by()
158
+ .values_list("pk", flat=True)
159
+ )
160
+
161
+ site_ids.update(
162
+ Site.objects.filter(
163
+ sitemoreinformation__in=SiteMoreInformation.objects.filter(
164
+ Q(published=True) & Q(primary__icontains=search_term)
165
+ ).order_by()
166
+ )
167
+ .order_by()
168
+ .values_list("pk", flat=True)
169
+ )
170
+
171
+ site_ids.update(
172
+ Site.objects.filter(sitereceiver__in=site_receivers)
173
+ .order_by()
174
+ .distinct()
175
+ .values_list("pk", flat=True)
176
+ )
177
+
178
+ searched &= Q(pk__in=site_ids)
179
+
180
+ return queryset.filter(searched)
181
+
182
+
183
+ class SLMBooleanFilter(BooleanFilter):
184
+ field_class = SLMBooleanField
185
+
186
+
187
+ class CrispyFormCompat:
188
+ """
189
+ Ensure the given form as a submit button and correct method set!
190
+ """
191
+
192
+ @property
193
+ def form(self):
194
+ form = BaseFilterSet.form.fget(self)
195
+
196
+ if compat.is_crispy():
197
+ from crispy_forms.helper import FormHelper
198
+ from crispy_forms.layout import Layout, Submit
199
+
200
+ helper = getattr(form, "helper", None)
201
+ if helper:
202
+ # add on a submit button if one does not exist on the form
203
+ def has_submit(fields):
204
+ for field in fields:
205
+ if isinstance(field, Submit) or has_submit(
206
+ getattr(field, "fields", [])
207
+ ):
208
+ return True
209
+ return False
210
+
211
+ if not has_submit(form.helper.layout.fields):
212
+ helper.layout = Layout(
213
+ *helper.layout.fields,
214
+ Submit("", _("Submit"), css_class="btn btn-primary"),
215
+ )
216
+ else:
217
+ form.helper = FormHelper()
218
+ form.helper.form_method = "GET"
219
+ form.helper.layout = Layout(
220
+ *form.fields.keys(),
221
+ Submit("", _("Submit"), css_class="btn btn-primary"),
222
+ )
223
+ return form
224
+
225
+
226
+ class AcceptListArguments:
227
+ """
228
+ Automatic conversion of lists to GET parameters in AJAX seems to add pesky
229
+ [] to the end of list arguments - there doesn't seem to be an easy way
230
+ to handle this in FilterSets so we strip the brackets out with this mixin.
231
+
232
+ It seems really really stupid that this is necessary...
233
+ """
234
+
235
+ def __init__(self, data=None, *args, **kwargs):
236
+ if data:
237
+ stripped = QueryDict(mutable=True)
238
+ for key in data.keys():
239
+ if key.endswith("[]"):
240
+ stripped.setlist(key[0:-2], data.getlist(key))
241
+ else:
242
+ stripped.setlist(key, data.getlist(key))
243
+ if len(stripped.getlist(key)) == 1:
244
+ stripped[key] = stripped.getlist(key)[0]
245
+ data = stripped
246
+ super().__init__(data=data, *args, **kwargs)
247
+
248
+
249
+ class MustIncludeThese(BaseInFilter, NumberFilter):
250
+ def __init__(self, field_name="pk", *args, **kwargs):
251
+ super().__init__(field_name=field_name, *args, **kwargs)
252
+
253
+ def filter(self, qs, value):
254
+ if value:
255
+ qs |= super().filter(qs.model.objects.all(), value).distinct()
256
+ return qs
257
+
258
+
259
+ class SLMDateTimeField(DateTimeField):
260
+ """
261
+ A DateTimeField that uses dateutil to parse datetimes. Much more lenient
262
+ than the default parsers.
263
+ """
264
+
265
+ def to_python(self, value):
266
+ try:
267
+ return super().to_python(value)
268
+ except ValidationError:
269
+ try:
270
+ return from_current_timezone(parser.parse(value))
271
+ except parser.ParserError as pe:
272
+ raise ValidationError(
273
+ _("Invalid date/time: {error}").format(str(pe)), code="invalid"
274
+ )
275
+
276
+
277
+ class SLMDateTimeFilter(DateTimeFilter):
278
+ field_class = SLMDateTimeField
279
+
280
+
281
+ class InitialValueFilterSet(FilterSet):
282
+ """
283
+ This allows you to specify initial values that can also be callables on
284
+ your field filters.
285
+ """
286
+
287
+ def __init__(self, data=None, *args, **kwargs):
288
+ # if filterset is bound, use initial values as defaults
289
+ if data is not None:
290
+ # get a mutable copy of the QueryDict
291
+ data = data.copy()
292
+ for name, f in self.base_filters.items():
293
+ initial = f.extra.get("initial")
294
+ # filter param is either missing or empty, use initial as
295
+ # default
296
+ if not data.get(name) and initial:
297
+ data[name] = initial() if callable(initial) else initial
298
+
299
+ super().__init__(data, *args, **kwargs)
300
+
301
+
302
+ class BaseStationFilter(CrispyFormCompat, AcceptListArguments, FilterSet):
303
+ EQLookup = namedtuple("EQLookup", "relation field start end")
304
+ EQUIPMENT_TABLE = {
305
+ "satellite_system": EQLookup(
306
+ "sitereceiver", "satellite_system", "installed", "removed"
307
+ ),
308
+ "receiver": EQLookup("sitereceiver", "receiver_type", "installed", "removed"),
309
+ "antenna": EQLookup("siteantenna", "antenna_type", "installed", "removed"),
310
+ "radome": EQLookup("siteantenna", "radome_type", "installed", "removed"),
311
+ "frequency_standard": EQLookup(
312
+ "sitefrequencystandard", "standard_type", "effective_start", "effective_end"
313
+ ),
314
+ }
315
+
316
+ SITE_FILTER = Site.objects.all()
317
+
318
+ @property
319
+ def restrict_published(self):
320
+ return False
321
+
322
+ def published_q(self, lookup):
323
+ if self.restrict_published:
324
+ if lookup:
325
+ lookup += "__"
326
+ return Q(**{f"{lookup}published": True})
327
+ return Q()
328
+
329
+ @property
330
+ def current_equipment(self):
331
+ return self.form.cleaned_data.get("current", None)
332
+
333
+ @cached_property
334
+ def alert_fields(self):
335
+ """
336
+ Fetch the mapping of alert names to related fields.
337
+ """
338
+
339
+ def get_related_field(alert):
340
+ for obj in Site._meta.related_objects:
341
+ if obj.related_model is alert:
342
+ return obj.name
343
+
344
+ return {
345
+ alert.__name__.lower(): get_related_field(alert)
346
+ for alert in Alert.objects.site_alerts()
347
+ }
348
+
349
+ @property
350
+ def query_epoch(self):
351
+ return self.form.cleaned_data.get("epoch", now())
352
+
353
+ epoch = django_filters.DateTimeFilter(field_name="epoch", method="at_epoch")
354
+
355
+ def at_epoch(self, queryset, name, value):
356
+ return queryset.at_epoch(epoch=value)
357
+
358
+ name = django_filters.CharFilter(
359
+ field_name="name", lookup_expr="icontains", distinct=True
360
+ )
361
+
362
+ station = django_filters.ModelMultipleChoiceFilter(
363
+ field_name="name",
364
+ to_field_name="name",
365
+ queryset=SITE_FILTER,
366
+ # method='filter_stations',
367
+ null_value="",
368
+ null_label="",
369
+ )
370
+
371
+ id = MustIncludeThese()
372
+
373
+ status = django_filters.MultipleChoiceFilter(
374
+ choices=SiteLogStatus.choices, distinct=True
375
+ )
376
+
377
+ alert = django_filters.MultipleChoiceFilter(
378
+ choices=[
379
+ (alert.__name__, alert._meta.verbose_name.title())
380
+ for alert in Alert.objects.site_alerts()
381
+ ],
382
+ method="filter_alerts",
383
+ distinct=True,
384
+ )
385
+
386
+ alert_level = django_filters.MultipleChoiceFilter(
387
+ choices=[(level.value, level.label) for level in AlertLevel],
388
+ method="filter_alert_level",
389
+ )
390
+
391
+ agency = django_filters.ModelMultipleChoiceFilter(
392
+ field_name="agencies", queryset=Agency.objects.all(), distinct=True
393
+ )
394
+
395
+ network = django_filters.ModelMultipleChoiceFilter(
396
+ field_name="networks", queryset=Network.objects.all(), distinct=True
397
+ )
398
+
399
+ receiver = django_filters.ModelMultipleChoiceFilter(
400
+ method="filter_equipment", queryset=Receiver.objects.all(), distinct=True
401
+ )
402
+
403
+ antenna = django_filters.ModelMultipleChoiceFilter(
404
+ method="filter_equipment", queryset=Antenna.objects.all(), distinct=True
405
+ )
406
+
407
+ radome = django_filters.ModelMultipleChoiceFilter(
408
+ method="filter_equipment", queryset=Radome.objects.all(), distinct=True
409
+ )
410
+
411
+ satellite_system = django_filters.ModelMultipleChoiceFilter(
412
+ method="filter_equipment", queryset=SatelliteSystem.objects.all()
413
+ )
414
+
415
+ frequency_standard = django_filters.MultipleChoiceFilter(
416
+ choices=FrequencyStandardType.choices, method="filter_equipment"
417
+ )
418
+
419
+ country = django_filters.MultipleChoiceFilter(
420
+ choices=ISOCountry.choices, method="filter_country"
421
+ )
422
+
423
+ current = SLMBooleanFilter(method="noop", distinct=True, field_name="current")
424
+
425
+ geography = django_filters.CharFilter(method="filter_geography")
426
+
427
+ def filter_geography(self, queryset, name, value):
428
+ qry = Q()
429
+ for poly in value:
430
+ qry |= Q(**{"sitelocation__llh__within": poly})
431
+ qry &= Q(sitelocation__published=True)
432
+ return queryset.filter(qry)
433
+
434
+ def noop(self, queryset, name, value):
435
+ return queryset
436
+
437
+ def filter_country(self, queryset, name, value):
438
+ return queryset.filter(
439
+ Q(sitelocation__country__in=value) & Q(self.published_q("sitelocation"))
440
+ )
441
+
442
+ def filter_alerts(self, queryset, name, value):
443
+ if value:
444
+ alert_q = Q()
445
+ for alert in value:
446
+ alert_q |= Q(**{f"{self.alert_fields[alert.lower()]}__isnull": False})
447
+ return queryset.filter(alert_q)
448
+ return queryset
449
+
450
+ def filter_alert_level(self, queryset, name, value):
451
+ if value:
452
+ level_q = Q()
453
+ for alerts in Site.alert_fields:
454
+ level_q |= Q(**{f"{alerts}__level__in": value})
455
+ return queryset.filter(level_q)
456
+ return queryset
457
+
458
+ def filter_equipment(self, queryset, name, value):
459
+ if value:
460
+ lookup = self.EQUIPMENT_TABLE[name]
461
+ if self.current_equipment:
462
+ return queryset.filter(
463
+ Q(**{f"{lookup.relation}__{lookup.field}__in": value})
464
+ & self.published_q(lookup.relation)
465
+ & (
466
+ Q(
467
+ **{
468
+ f"{lookup.relation}__{lookup.start}__lte": self.query_epoch
469
+ }
470
+ )
471
+ | Q(**{f"{lookup.relation}__{lookup.start}__isnull": True})
472
+ )
473
+ & (
474
+ Q(**{f"{lookup.relation}__{lookup.end}__gt": self.query_epoch})
475
+ | Q(**{f"{lookup.relation}__{lookup.end}__isnull": True})
476
+ )
477
+ ).distinct()
478
+ else:
479
+ return queryset.filter(
480
+ Q(**{f"{lookup.relation}__{lookup.field}__in": value})
481
+ & self.published_q(lookup.relation)
482
+ ).distinct()
483
+ return queryset
484
+
485
+ class Meta:
486
+ model = Site
487
+ fields = (
488
+ "id",
489
+ "status",
490
+ "alert_level",
491
+ "station",
492
+ "name",
493
+ "agency",
494
+ "network",
495
+ "receiver",
496
+ "antenna",
497
+ "radome",
498
+ "country",
499
+ "current",
500
+ "epoch",
501
+ "satellite_system",
502
+ "frequency_standard",
503
+ )
504
+ distinct = True
slm/api/pagination.py ADDED
@@ -0,0 +1,55 @@
1
+ from rest_framework.pagination import LimitOffsetPagination
2
+ from rest_framework.response import Response
3
+
4
+
5
+ class BrowsablePagination(LimitOffsetPagination):
6
+ default_limit = 100
7
+
8
+
9
+ class ClientPagination(LimitOffsetPagination):
10
+ default_limit = 10
11
+
12
+
13
+ class DataTablesPagination(LimitOffsetPagination):
14
+ default_limit = 20
15
+
16
+ # datatables naming
17
+ limit_query_param = "length"
18
+ offset_query_param = "start"
19
+
20
+ queryset = None
21
+ draw = None
22
+
23
+ def paginate_queryset(self, queryset, request, view=None):
24
+ self.queryset = queryset
25
+ self.draw = request.query_params.get("draw", None)
26
+ return super().paginate_queryset(queryset, request, view=view)
27
+
28
+ def get_paginated_response(self, data):
29
+ resp = {
30
+ "data": data,
31
+ "recordsTotal": self.queryset.model.objects.count(),
32
+ "recordsFiltered": self.count,
33
+ "next": self.get_next_link(),
34
+ "previous": self.get_previous_link(),
35
+ }
36
+ if self.draw is not None:
37
+ resp["draw"] = self.draw
38
+ return Response(resp)
39
+
40
+ def get_paginated_response_schema(self, schema):
41
+ return {
42
+ "type": "object",
43
+ "properties": {
44
+ "recordsTotal": {
45
+ "type": "integer",
46
+ "example": 123,
47
+ },
48
+ "recordsFiltered": {"type": "integer", "example": 123},
49
+ "draw": {"type": "integer", "nullable": True, "example": 1},
50
+ "data": schema,
51
+ },
52
+ }
53
+
54
+ def get_results(self, data):
55
+ return data["data"]