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
@@ -0,0 +1,167 @@
1
+ """
2
+ Update the data availability information for each station.
3
+ """
4
+
5
+ import typing as t
6
+ from datetime import datetime, timedelta
7
+
8
+ from django.utils.timezone import now
9
+ from django.utils.translation import gettext as _
10
+ from django_typer.management import TyperCommand, model_parser_completer
11
+ from typer import Argument, Option
12
+ from typing_extensions import Annotated
13
+
14
+ from igs_tools.defines import DataCenter
15
+ from igs_tools.directory import DirectoryListing
16
+ from slm.defines import DataRate, RinexVersion
17
+ from slm.management.commands import EnumParser
18
+ from slm.models import DataAvailability, Site
19
+
20
+
21
+ class Command(TyperCommand):
22
+ help = _("Update the data availability information for each station.")
23
+
24
+ LOOKBACK = 60 # 60 days
25
+
26
+ UNHEALTHY = (now() - timedelta(days=30)).date()
27
+ STALE = (now() - timedelta(days=10)).date()
28
+
29
+ suppressed_base_arguments = {
30
+ *TyperCommand.suppressed_base_arguments,
31
+ "version",
32
+ "pythonpath",
33
+ "settings",
34
+ "skip-checks",
35
+ }
36
+ requires_migrations_checks = False
37
+ requires_system_checks = []
38
+
39
+ def handle(
40
+ self,
41
+ sites: Annotated[
42
+ t.Optional[t.List[Site]],
43
+ Argument(
44
+ **model_parser_completer(
45
+ Site, lookup_field="name", case_insensitive=True
46
+ ),
47
+ help=_(
48
+ "The station(s) to update, if unspecified, update data "
49
+ "availability information for all of them."
50
+ ),
51
+ ),
52
+ ] = None,
53
+ username: Annotated[str, Option(help=_("Username to login to ftp."))] = "",
54
+ password: Annotated[
55
+ str,
56
+ Option(
57
+ prompt=True,
58
+ prompt_required=False,
59
+ hide_input=True,
60
+ help=_("Password to login to ftp."),
61
+ ),
62
+ ] = "",
63
+ lookback: Annotated[
64
+ int,
65
+ Option(
66
+ "-l", "--lookback", help=_("The number of days into the past to look.")
67
+ ),
68
+ ] = LOOKBACK,
69
+ data_rates: Annotated[
70
+ t.Optional[t.List[DataRate]],
71
+ Option(
72
+ "--rate",
73
+ parser=EnumParser(DataRate, field="slug"),
74
+ help=_(
75
+ "Restrict the data rates to update availability for. {rates}"
76
+ ).format(rates=", ".join([rate.slug for rate in DataRate])),
77
+ ),
78
+ ] = [rate.slug for rate in DirectoryListing.data_rates],
79
+ rinex_versions: Annotated[
80
+ t.Optional[t.List[RinexVersion]],
81
+ Option(
82
+ "--rinex",
83
+ parser=EnumParser(RinexVersion, field="slug"),
84
+ help=_(
85
+ "Restrict rinex versions to update availability for ({versions})."
86
+ ).format(
87
+ versions=", ".join(
88
+ [ver.slug for ver in RinexVersion.major_versions()]
89
+ )
90
+ ),
91
+ ),
92
+ ] = [ver.slug for ver in DirectoryListing.rinex_versions],
93
+ data_centers: Annotated[
94
+ t.Optional[t.List[DataCenter]],
95
+ Option(
96
+ "--center",
97
+ parser=EnumParser(DataCenter, field="label"),
98
+ help=_(
99
+ "Restrict data centers to update availability for ({centers})."
100
+ ).format(centers=", ".join([str(dc) for dc in DataCenter])),
101
+ ),
102
+ ] = [dc for dc in DirectoryListing.data_centers],
103
+ ):
104
+ availability = {}
105
+ unrecognized = set()
106
+ for listing in DirectoryListing(
107
+ stations=[site.name for site in sites or Site.objects.active()],
108
+ start=now(),
109
+ end=now() - timedelta(days=lookback),
110
+ username=username,
111
+ password=password,
112
+ data_rates=data_rates or [],
113
+ rinex_versions=rinex_versions or [],
114
+ data_centers=data_centers or [],
115
+ ):
116
+ if listing.file_type not in {"d", "O"}:
117
+ continue
118
+ try:
119
+ avail, created = DataAvailability.objects.get_or_create(
120
+ site=Site.objects.get(name__istartswith=listing.station),
121
+ rinex_version=listing.rinex_version,
122
+ rate=listing.data_rate,
123
+ last=(
124
+ listing.date.date()
125
+ if isinstance(listing.date, datetime)
126
+ else listing.date
127
+ ),
128
+ )
129
+ except Site.DoesNotExist:
130
+ unrecognized.add(listing.station)
131
+ continue
132
+ availability.setdefault(avail.site.name, avail.last)
133
+ if availability[avail.site.name] < avail.last:
134
+ availability[avail.site.name] = avail.last
135
+
136
+ unhealthy = []
137
+ stale = []
138
+ healthy = []
139
+ for station, last in availability.items():
140
+ if last < self.UNHEALTHY:
141
+ unhealthy.append((station, last))
142
+ elif last < self.STALE:
143
+ stale.append((station, last))
144
+ else:
145
+ healthy.append((station, last))
146
+
147
+ for station, last in healthy:
148
+ self.stdout.write(self.style.SUCCESS(f"{station} {last}"))
149
+ for station, last in stale:
150
+ self.stdout.write(self.style.WARNING(f"{station} {last}"))
151
+ for station, last in unhealthy:
152
+ self.stdout.write(self.style.ERROR(f"{station} {last}"))
153
+
154
+ self.stdout.write(
155
+ self.style.WARNING(
156
+ _("Ignored {unrecognized} stations.").format(
157
+ unrecognized=len(unrecognized)
158
+ )
159
+ )
160
+ )
161
+ self.stdout.write(
162
+ self.style.SUCCESS(
163
+ _("Updated {availability} stations.").format(
164
+ availability=len(availability)
165
+ )
166
+ )
167
+ )
@@ -0,0 +1,186 @@
1
+ """
2
+ The SLM supports pluggable validation logic for all site log sections and fields. Since
3
+ this logic is configuraton specific, it may change, diverging from the validation flags
4
+ recorded against the previous validation configuration in the database.
5
+
6
+ Run this command to run all validation routines against all current sitelog fields
7
+ in the database.
8
+ """
9
+
10
+ import typing as t
11
+
12
+ from django.core.exceptions import ValidationError
13
+ from django.db import transaction
14
+ from django.db.models import Q, Sum
15
+ from django.utils.translation import gettext as _
16
+ from django_typer.management import TyperCommand, model_parser_completer
17
+ from tqdm import tqdm
18
+ from typer import Argument, Option
19
+ from typing_extensions import Annotated
20
+
21
+ from slm.defines import SiteLogStatus
22
+ from slm.models import GeodesyMLInvalid, Site
23
+
24
+
25
+ class Command(TyperCommand):
26
+ help = _(
27
+ "Validate and update status of head state of all existing site logs. "
28
+ "This might be necessary to run if the validation configuration is "
29
+ "updated."
30
+ )
31
+
32
+ suppressed_base_arguments = {
33
+ *TyperCommand.suppressed_base_arguments,
34
+ "version",
35
+ "pythonpath",
36
+ "settings",
37
+ }
38
+
39
+ def handle(
40
+ self,
41
+ sites: Annotated[
42
+ t.Optional[t.List[Site]],
43
+ Argument(
44
+ **model_parser_completer(
45
+ Site, lookup_field="name", case_insensitive=True
46
+ ),
47
+ help=_("The name of the site(s). Default - all sites."),
48
+ ),
49
+ ] = None,
50
+ clear: Annotated[
51
+ bool, Option("--clear", help=_("Clear existing validation flags."))
52
+ ] = False,
53
+ schema: Annotated[
54
+ bool,
55
+ Option(
56
+ "--schema",
57
+ help=_(
58
+ "Also perform validation of generated GeodesyML files "
59
+ "against the latest schema."
60
+ ),
61
+ ),
62
+ ] = False,
63
+ all: Annotated[
64
+ bool,
65
+ Option(
66
+ "--all",
67
+ help=_("Validate all sites, not just the currently public sites."),
68
+ ),
69
+ ] = False,
70
+ ):
71
+ from slm.validators import set_bypass
72
+
73
+ set_bypass(True)
74
+ invalid = 0
75
+ valid = 0
76
+ critical = 0
77
+ old_flag_count = Site.objects.aggregate(Sum("num_flags"))["num_flags__sum"]
78
+ with transaction.atomic():
79
+ sites = sites or Site.objects.all()
80
+ if not all:
81
+ sites = Site.objects.filter(
82
+ Q(pk__in=[site.pk for site in sites])
83
+ & Q(status__in=[SiteLogStatus.PUBLISHED, SiteLogStatus.UPDATED])
84
+ )
85
+
86
+ with tqdm(
87
+ total=sites.count(),
88
+ desc=_("Validating"),
89
+ unit="sites",
90
+ postfix={"site": ""},
91
+ ) as p_bar:
92
+ for site in sites:
93
+ p_bar.set_postfix({"site": site.name})
94
+ for section in Site.sections():
95
+ head = getattr(site, section.accessor).head()
96
+ if not hasattr(head, "__iter__"):
97
+ if head:
98
+ head = [head]
99
+ else:
100
+ head = []
101
+ for obj in head:
102
+ if clear:
103
+ obj._flags = {}
104
+ try:
105
+ obj.clean()
106
+ obj.save()
107
+ except ValidationError as verr:
108
+ self.stderr.write(
109
+ self.style.ERROR(
110
+ _(
111
+ "Section {} of site {} has "
112
+ "critical error(s): {}"
113
+ ).format(
114
+ obj.__class__.__name__.lstrip("Site"),
115
+ site.name,
116
+ str(verr),
117
+ )
118
+ )
119
+ )
120
+ critical += 1
121
+ # this type of error would normally block the
122
+ # section from being saved - but we let this
123
+ # go through here and record the error as a
124
+ # normal flag
125
+ for field, errors in verr.error_dict.items():
126
+ obj._flags[field] = "\n".join(
127
+ err.message for err in errors
128
+ )
129
+
130
+ if schema:
131
+ alert = GeodesyMLInvalid.objects.check_site(site=site)
132
+ if alert:
133
+ invalid += 1
134
+ else:
135
+ valid += 1
136
+
137
+ # site.update_status(save=True)
138
+ p_bar.update(n=1)
139
+
140
+ Site.objects.synchronize_denormalized_state(skip_form_updates=True)
141
+
142
+ if schema:
143
+ if valid:
144
+ self.stdout.write(
145
+ self.style.SUCCESS(
146
+ _("{valid} sites had valid GeodesyML documents.").format(
147
+ valid=valid
148
+ )
149
+ )
150
+ )
151
+ if invalid:
152
+ self.stdout.write(
153
+ self.style.ERROR(
154
+ _(
155
+ "{invalid} sites do not have valid GeodesyML documents."
156
+ ).format(invalid=invalid)
157
+ )
158
+ )
159
+
160
+ new_flags = Site.objects.aggregate(Sum("num_flags"))["num_flags__sum"]
161
+
162
+ delta = new_flags - old_flag_count
163
+
164
+ if delta >= 0:
165
+ change = "added"
166
+ else:
167
+ change = "removed"
168
+
169
+ self.stdout.write(
170
+ self.style.NOTICE(
171
+ _("{delta} flags were {change}.").format(
172
+ delta=abs(delta), change=change
173
+ )
174
+ )
175
+ )
176
+
177
+ self.stdout.write(
178
+ self.style.NOTICE(
179
+ _(
180
+ "There are a total of {new_flags} validation flags across "
181
+ "{site_count} sites. {critical} are critical."
182
+ ).format(
183
+ new_flags=new_flags, site_count=sites.count(), critical=critical
184
+ )
185
+ )
186
+ )
@@ -0,0 +1,73 @@
1
+ """
2
+ Update the data availability information for each station.
3
+ """
4
+
5
+ import typing as t
6
+ from pathlib import Path
7
+
8
+ from django.core.management import CommandError
9
+ from django.utils.translation import gettext as _
10
+ from django_typer.management import TyperCommand
11
+ from lxml import etree
12
+ from typer import Argument, Option
13
+ from typing_extensions import Annotated
14
+
15
+ from slm.defines import GeodesyMLVersion
16
+ from slm.management.commands import EnumParser
17
+
18
+
19
+ class Command(TyperCommand):
20
+ help = _("Validate the given xml document against the GeodesyML schema.")
21
+
22
+ suppressed_base_arguments = {
23
+ *TyperCommand.suppressed_base_arguments,
24
+ # "version",
25
+ "pythonpath",
26
+ "settings",
27
+ "skip-checks",
28
+ }
29
+ requires_migrations_checks = False
30
+ requires_system_checks = []
31
+
32
+ def handle(
33
+ self,
34
+ file: Annotated[Path, Argument(help=_("Path to the GML file to validate."))],
35
+ version: Annotated[
36
+ t.Optional[GeodesyMLVersion],
37
+ Option(
38
+ "--version",
39
+ metavar="VERSION",
40
+ parser=EnumParser(GeodesyMLVersion, "version"),
41
+ help=_("The Geodesy ML version. ({versions})").format(
42
+ versions=", ".join([str(gml.version) for gml in GeodesyMLVersion])
43
+ ),
44
+ ),
45
+ ] = None,
46
+ ):
47
+ if not file.exists():
48
+ raise CommandError(_("{file} does not exist!").format(file=file))
49
+ doc = etree.parse(file)
50
+ root = doc.getroot()
51
+
52
+ if not version:
53
+ version = GeodesyMLVersion(
54
+ root.nsmap.get(root.prefix, GeodesyMLVersion.latest())
55
+ )
56
+
57
+ self.stdout.write(_("Validating against: {version}").format(version=version))
58
+
59
+ result = version.schema.validate(doc)
60
+
61
+ if not result:
62
+ for error in version.schema.error_log:
63
+ self.stdout.write(
64
+ self.style.ERROR(
65
+ _("[{line}] {message}").format(
66
+ line=error.line, message=error.message
67
+ )
68
+ )
69
+ )
70
+ else:
71
+ self.stdout.write(
72
+ self.style.SUCCESS(_("{file} is valid!").format(file=file))
73
+ )
slm/map/__init__.py ADDED
@@ -0,0 +1 @@
1
+ default_app_config = "slm.map.apps.MapConfig"
slm/map/admin.py ADDED
@@ -0,0 +1,5 @@
1
+ from django.contrib import admin
2
+
3
+ from slm.map.models import MapSettings
4
+
5
+ admin.site.register(MapSettings)
File without changes
File without changes
@@ -0,0 +1,28 @@
1
+ from rest_framework import serializers
2
+
3
+ from slm.api.edit import serializers as slm_serializers
4
+
5
+
6
+ class StationSerializer(slm_serializers.StationSerializer):
7
+ llh = serializers.SerializerMethodField(read_only=True)
8
+
9
+ def get_llh(self, obj):
10
+ if hasattr(obj, "llh") and obj.llh:
11
+ return obj.llh[0], obj.llh[1], obj.llh[2]
12
+ return None
13
+
14
+ class Meta(slm_serializers.StationSerializer.Meta):
15
+ fields = (*slm_serializers.StationSerializer.Meta.fields, "llh")
16
+
17
+
18
+ class StationMapSerializer(serializers.Serializer):
19
+ def to_representation(self, instance):
20
+ return {
21
+ "id": instance.pk,
22
+ "type": "Feature",
23
+ "geometry": {
24
+ "type": "Point",
25
+ "coordinates": [instance.llh[1], instance.llh[0]],
26
+ },
27
+ "properties": {"name": instance.name, "status": instance.status},
28
+ }
@@ -0,0 +1,46 @@
1
+ from django.db.models import Q
2
+ from rest_framework import status
3
+ from rest_framework.response import Response
4
+
5
+ from slm.api.edit import views as slm_views
6
+ from slm.map.api.edit.serializers import StationMapSerializer, StationSerializer
7
+
8
+
9
+ class StationListViewSet(slm_views.StationListViewSet):
10
+ serializer_class = StationSerializer
11
+
12
+ ordering_fields = (
13
+ slm_views.StationListViewSet.ordering_fields
14
+ ) # + ['latitude', 'longitude']
15
+
16
+ def get_queryset(self):
17
+ return super().get_queryset().with_location_fields("llh")
18
+
19
+
20
+ # todo we could use geodjango and gis drf extensions to do this automatically - but that produces a large dependency
21
+ # overhead for a pretty basic task - revisit this if polygonal queries are deemed useful or other reasons to integrate
22
+ # GIS features arise
23
+ class StationMapViewSet(StationListViewSet):
24
+ """
25
+ A view for returning a site list as a geojson set of point features. We
26
+ inherit from our normal StationListViewSet so all filtering parameters are
27
+ the same and we can pair requests to the map view with requests to the
28
+ station list view.
29
+ """
30
+
31
+ serializer_class = StationMapSerializer
32
+ pagination_class = None
33
+
34
+ def list(self, request, **kwargs):
35
+ return Response(
36
+ {
37
+ "type": "FeatureCollection",
38
+ "features": self.get_serializer(
39
+ self.filter_queryset(self.get_queryset()), many=True
40
+ ).data,
41
+ },
42
+ status=status.HTTP_200_OK,
43
+ )
44
+
45
+ def get_queryset(self):
46
+ return super().get_queryset().filter(Q(llh__isnull=False))
File without changes
@@ -0,0 +1,29 @@
1
+ from rest_framework import serializers
2
+
3
+ from slm.api.public import serializers as slm_serializers
4
+
5
+
6
+ class StationListSerializer(slm_serializers.StationListSerializer):
7
+ class Meta(slm_serializers.StationListSerializer.Meta):
8
+ # could add additional fields here to extend the data exposed by this api
9
+ fields = slm_serializers.StationListSerializer.Meta.fields
10
+
11
+
12
+ class StationMapSerializer(serializers.Serializer):
13
+ def to_representation(self, instance):
14
+ return {
15
+ "type": "Feature",
16
+ "geometry": {
17
+ "type": "Point",
18
+ "coordinates": (
19
+ [instance.llh[1], instance.llh[0]] if instance.llh else [None, None]
20
+ ),
21
+ },
22
+ "properties": {
23
+ "name": instance.name,
24
+ "status": instance.status,
25
+ "last_data": (
26
+ max(0, instance.last_data.days) if instance.last_data else None
27
+ ),
28
+ },
29
+ }
@@ -0,0 +1,64 @@
1
+ from rest_framework import pagination
2
+ from rest_framework.response import Response
3
+
4
+ from slm.api.public import views as slm_views
5
+ from slm.map.api.public.serializers import StationListSerializer, StationMapSerializer
6
+ from slm.models import Site
7
+
8
+
9
+ class StationListViewSet(slm_views.StationListViewSet):
10
+ serializer_class = StationListSerializer
11
+
12
+ # could add fields here to extend ordering options
13
+ ordering_fields = slm_views.StationListViewSet.ordering_fields
14
+
15
+ def get_queryset(self):
16
+ # could add additional filter parameters or annotations here to extend the api
17
+ return super().get_queryset()
18
+
19
+
20
+ class FeatureCollectionPagination(pagination.BasePagination):
21
+ """
22
+ We implement geojson wrapper as a special type of pagination even though
23
+ no paging is done. This method fits neatly into how the base classes
24
+ generate and process response objects. Doing this directly in list()
25
+ on the view breaks filter form rendering in the browsable renderers.
26
+ """
27
+
28
+ def paginate_queryset(self, queryset, request, view=None):
29
+ return queryset
30
+
31
+ def to_html(self):
32
+ return None
33
+
34
+ def get_results(self, data):
35
+ return data["features"]
36
+
37
+ def get_paginated_response(self, data):
38
+ return Response({"type": "FeatureCollection", "features": data})
39
+
40
+ def get_paginated_response_schema(self, schema):
41
+ return {
42
+ "type": "object",
43
+ "properties": {
44
+ "type": {
45
+ "type": "string",
46
+ "example": "FeatureCollection",
47
+ },
48
+ "features": schema,
49
+ },
50
+ }
51
+
52
+
53
+ class StationMapViewSet(StationListViewSet):
54
+ """
55
+ A view for returning a site list as a geojson set of point features.
56
+ """
57
+
58
+ serializer_class = StationMapSerializer
59
+ pagination_class = FeatureCollectionPagination
60
+
61
+ def get_queryset(self):
62
+ return (
63
+ Site.objects.with_location_fields("llh").public().availability().distinct()
64
+ )
slm/map/apps.py ADDED
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class MapConfig(AppConfig):
5
+ name = "slm.map"
6
+ label = name.replace(".", "_")
7
+ verbose_name = " SLM Map"
slm/map/defines.py ADDED
@@ -0,0 +1,53 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from django_enum import IntegerChoices
3
+ from enum_properties import p, s
4
+
5
+
6
+ class MapBoxStyle(IntegerChoices, s("slug", case_fold=True), p("version")):
7
+ """
8
+ https://docs.mapbox.com/api/maps/styles/
9
+ """
10
+
11
+ _symmetric_builtins_ = ["name", s("label", case_fold=True), "uri"]
12
+
13
+ # name value label slug version
14
+ STREETS = 1, _("Streets"), "streets", 12
15
+ OUTDOORS = 2, _("Outdoors"), "outdoors", 12
16
+ LIGHT = 3, _("Light"), "light", 11
17
+ DARK = 4, _("Dark"), "dark", 11
18
+ SATELLITE = 5, _("Satellite"), "satellite", 9
19
+ SATELLITE_STREETS = 6, _("Satellite Streets"), "satellite-streets", 12
20
+ NAVIGATION_DAY = 7, _("Navigation Day"), "navigation-day", 1
21
+ NAVIGATION_NIGHT = 8, _("Navigation Night"), "navigation-night", 1
22
+
23
+ @property
24
+ def uri(self):
25
+ return f"mapbox://styles/mapbox/{self.version_slug}"
26
+
27
+ @property
28
+ def version_slug(self):
29
+ return f"{self.slug}-v{self.version}"
30
+
31
+ def __str__(self):
32
+ return self.uri
33
+
34
+
35
+ class MapBoxProjection(IntegerChoices, s("slug", case_fold=True)):
36
+ """
37
+ https://docs.mapbox.com/mapbox-gl-js/style-spec/projection/
38
+ """
39
+
40
+ _symmetric_builtins_ = ["name", "label"]
41
+
42
+ # name value label slug
43
+ ALBERS = 0, _("Albers"), "albers"
44
+ EQUAL_EARTH = 1, _("Equal Earth"), "equalEarth"
45
+ EQUI_RECTANGULAR = 2, _("Equi-Rectangular"), "equirectangular"
46
+ LAMBERT_CONFORMAL_CONIC = 3, _("Lambert Conformal Conic"), "lambertConformalConic"
47
+ MERCATOR = 4, _("Mercator"), "mercator"
48
+ NATURAL_EARTH = 5, _("Natural Earth"), "naturalEarth"
49
+ WINKEL_TRIPEL = 6, _("Winkel Tripel"), "winkelTripel"
50
+ GLOBE = 7, _("Globe"), "globe"
51
+
52
+ def __str__(self):
53
+ return self.slug