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/admin.py ADDED
@@ -0,0 +1,674 @@
1
+ """
2
+ Handles admin site for Django project.
3
+
4
+
5
+ NOTES:
6
+ This file essentially controls what is seen on the admin site
7
+ as well as overriding the default methods for adding
8
+ a new admin user.
9
+
10
+ To add a new model group to admin site simply add
11
+ "admin.site.register(NEW MODEL GROUP NAME)"
12
+ to bottom of file.
13
+
14
+ Same process can be applied for unregistering a model
15
+ group (same command with unregister).
16
+
17
+ More info:
18
+ https://docs.djangoproject.com/en/stable/ref/contrib/admin/
19
+ """
20
+
21
+ from django import forms
22
+ from django.conf import settings
23
+ from django.contrib import admin
24
+ from django.contrib.auth import get_user_model
25
+ from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
26
+ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
27
+ from django.contrib.auth.models import Group
28
+ from django.db.models import BooleanField, Count, Q
29
+ from django.db.models.expressions import ExpressionWrapper
30
+ from django.utils.safestring import mark_safe
31
+ from django.utils.translation import gettext_lazy as _
32
+ from polymorphic.admin import (
33
+ PolymorphicChildModelAdmin,
34
+ PolymorphicChildModelFilter,
35
+ PolymorphicParentModelAdmin,
36
+ )
37
+
38
+ from slm.authentication import initiate_password_resets, permissions
39
+ from slm.defines import SiteLogStatus
40
+ from slm.models import (
41
+ About,
42
+ Agency,
43
+ AgencyAlert,
44
+ Alert,
45
+ AntCal,
46
+ Antenna,
47
+ ArchivedSiteLog,
48
+ ArchiveIndex,
49
+ DataCenter,
50
+ Equipment,
51
+ GeodesyMLInvalid,
52
+ Help,
53
+ LogEntry,
54
+ Manufacturer,
55
+ Network,
56
+ Radome,
57
+ Receiver,
58
+ ReviewRequested,
59
+ SatelliteSystem,
60
+ Site,
61
+ SiteAlert,
62
+ SiteFileUpload,
63
+ SiteLogPublished,
64
+ TideGauge,
65
+ UnpublishedFilesAlert,
66
+ UpdatesRejected,
67
+ UserAlert,
68
+ UserProfile,
69
+ )
70
+ from slm.widgets import GraphicTextarea
71
+
72
+ admin.site.unregister(Group)
73
+
74
+
75
+ class UserAgencyInline(admin.TabularInline):
76
+ model = Agency.users.through
77
+ extra = 0
78
+
79
+
80
+ class TideGaugeInline(admin.TabularInline):
81
+ model = TideGauge.sites.through
82
+ extra = 0
83
+
84
+
85
+ class SiteTGInline(admin.TabularInline):
86
+ model = Site.tide_gauges.through
87
+ extra = 0
88
+
89
+
90
+ class ProfileInline(admin.StackedInline):
91
+ model = UserProfile
92
+ can_delete = False
93
+ verbose_name_plural = "Profile"
94
+ fk_name = "user"
95
+
96
+
97
+ class ModeratorListFilter(admin.SimpleListFilter):
98
+ title = _("Moderator")
99
+
100
+ parameter_name = "moderator"
101
+
102
+ def lookups(self, request, model_admin):
103
+ return (
104
+ (True, _("Yes")),
105
+ (False, _("No")),
106
+ )
107
+
108
+ def queryset(self, request, queryset):
109
+ queryset = queryset.annotate_moderator_status()
110
+ if self.value() is not None:
111
+ return queryset.filter(moderator=self.value())
112
+ return queryset
113
+
114
+
115
+ class UserAdmin(BaseUserAdmin):
116
+ # chooses which fields to display for admin users
117
+ list_display = (
118
+ "email",
119
+ "first_name",
120
+ "last_name",
121
+ "last_activity",
122
+ "is_active",
123
+ "is_superuser",
124
+ "moderator",
125
+ )
126
+ search_fields = ["email", "first_name", "last_name"]
127
+ readonly_fields = ["last_activity", "date_joined"]
128
+
129
+ inlines = [UserAgencyInline, ProfileInline]
130
+
131
+ ordering = ("-last_activity",)
132
+ list_filter = (
133
+ "is_superuser",
134
+ "is_active",
135
+ "html_emails",
136
+ "silence_alerts",
137
+ ModeratorListFilter,
138
+ )
139
+
140
+ fieldsets = (
141
+ (None, {"fields": ("email", "password")}),
142
+ (
143
+ _("Personal Info"),
144
+ {
145
+ "fields": (
146
+ "first_name",
147
+ "last_name",
148
+ )
149
+ },
150
+ ),
151
+ (
152
+ _("Permissions"),
153
+ {
154
+ "fields": ("is_active", "is_superuser", "groups", "user_permissions"),
155
+ },
156
+ ),
157
+ (_("Preferences"), {"fields": ("silence_alerts", "html_emails")}),
158
+ (_("Important Dates"), {"fields": ("last_activity", "date_joined")}),
159
+ )
160
+ add_fieldsets = (
161
+ (
162
+ None,
163
+ {
164
+ "classes": ("wide",),
165
+ "fields": (
166
+ "email",
167
+ "first_name",
168
+ "last_name",
169
+ "is_superuser",
170
+ "password1",
171
+ "password2",
172
+ ),
173
+ },
174
+ ),
175
+ )
176
+
177
+ actions = [
178
+ "request_password_reset",
179
+ "enable_emails",
180
+ "disable_emails",
181
+ "deactivate",
182
+ "activate",
183
+ ]
184
+
185
+ def moderator(self, obj):
186
+ return obj.moderator
187
+
188
+ moderator.boolean = True
189
+
190
+ def get_inline_instances(self, request, obj=None):
191
+ if not obj:
192
+ return list()
193
+ return super().get_inline_instances(request, obj)
194
+
195
+ def request_password_reset(self, request, queryset):
196
+ initiate_password_resets(queryset, request=request)
197
+
198
+ request_password_reset.short_description = _("Request password resets.")
199
+
200
+ def deactivate(self, request, queryset):
201
+ queryset.update(is_active=False)
202
+
203
+ deactivate.short_description = _("Disable user accounts.")
204
+
205
+ def activate(self, request, queryset):
206
+ queryset.update(is_active=True)
207
+
208
+ activate.short_description = _("Re-activate user accounts.")
209
+
210
+ def enable_emails(self, request, queryset):
211
+ queryset.update(silence_alerts=False)
212
+
213
+ enable_emails.short_description = _("Enable alert emails for users.")
214
+
215
+ def disable_emails(self, request, queryset):
216
+ queryset.update(silence_alerts=True)
217
+
218
+ disable_emails.short_description = _("Disable alert emails for users.")
219
+
220
+ def get_actions(self, request):
221
+ """
222
+ Remove delete_selected action. too tempting! Preferred method of
223
+ account disabling is deactivation. Accounts can be fully wiped from
224
+ the system using the delete button on the user page but this will
225
+ remove them from all logs and history, so it should only be done in
226
+ extreme or specific cases.
227
+ """
228
+ actions = super().get_actions(request)
229
+ if "delete_selected" in actions:
230
+ del actions["delete_selected"]
231
+ return actions
232
+
233
+ def get_form(self, request, obj=None, **kwargs):
234
+ form = super().get_form(request, obj, **kwargs)
235
+ # remove all of the standard django object permissions from the admin
236
+ # they are just clutter because we restrict access to the admin to
237
+ # superusers. The only permissions that should be shown are permissions
238
+ # specific the to the SLM
239
+ user_permissions = form.base_fields.get("user_permissions", None)
240
+ if user_permissions:
241
+ user_permissions.queryset = permissions()
242
+ silence_alerts = form.base_fields.get("silence_alerts")
243
+ if silence_alerts is not None:
244
+ silence_alerts.initial = getattr(settings, "SLM_EMAILS_REQUIRE_LOGIN", True)
245
+ return form
246
+
247
+
248
+ class GroupAdmin(BaseGroupAdmin):
249
+ def get_form(self, request, obj=None, **kwargs):
250
+ form = super().get_form(request, obj, **kwargs)
251
+ # remove all of the standard django object permissions from the admin
252
+ # they are just clutter because we restrict access to the admin to
253
+ # superusers. The only permissions that should be shown are permissions
254
+ # specific the to the SLM
255
+ permissions_field = form.base_fields.get("permissions", None)
256
+ if permissions_field:
257
+ permissions_field.queryset = permissions()
258
+ return form
259
+
260
+
261
+ class NetworkInline(admin.TabularInline):
262
+ model = Network.sites.through
263
+ extra = 0
264
+
265
+
266
+ class SiteAgencyInline(admin.TabularInline):
267
+ model = Agency.sites.through
268
+ extra = 0
269
+
270
+
271
+ @admin.register(Site)
272
+ class SiteAdmin(admin.ModelAdmin):
273
+ search_fields = ("name",)
274
+ inlines = [SiteAgencyInline, NetworkInline, TideGaugeInline]
275
+ exclude = ["agencies"]
276
+
277
+
278
+ @admin.register(Network)
279
+ class NetworkAdmin(admin.ModelAdmin):
280
+ search_fields = ("name",)
281
+ ordering = ("name",)
282
+ list_filter = ("public",)
283
+
284
+
285
+ class AgencyUserInline(admin.TabularInline):
286
+ model = Agency.users.through
287
+ extra = 0
288
+ max_num = 0
289
+ readonly_fields = ["user", "is_moderator"]
290
+
291
+ def is_moderator(self, obj):
292
+ return obj.user.is_moderator()
293
+
294
+ is_moderator.boolean = True
295
+
296
+
297
+ class AgencyAdmin(admin.ModelAdmin):
298
+ search_fields = ("name",)
299
+ ordering = ("name",)
300
+ list_filter = ("public",)
301
+ inlines = [AgencyUserInline]
302
+
303
+
304
+ class DataCenterAdmin(admin.ModelAdmin):
305
+ search_fields = ("name",)
306
+ ordering = ("name",)
307
+
308
+
309
+ class SatelliteSystemAdmin(admin.ModelAdmin):
310
+ pass
311
+
312
+
313
+ class TideGaugeAdmin(admin.ModelAdmin):
314
+ search_fields = ("name", "sonel_id")
315
+ list_display = ("name", "sonel_link")
316
+ inlines = [SiteTGInline]
317
+
318
+ def sonel_link(self, obj):
319
+ return mark_safe(
320
+ f'<a href="{obj.sonel_link}" target="_blank">{obj.sonel_link}</a>'
321
+ )
322
+
323
+ def get_queryset(self, request):
324
+ return self.model.objects.prefetch_related("sites")
325
+
326
+
327
+ class AntennaCalibrationAdmin(admin.ModelAdmin):
328
+ search_fields = ("antenna__model", "radome__model")
329
+ list_display = ("antenna", "radome", "method_label", "calibrated")
330
+ list_filter = ("method",)
331
+
332
+ ordering = ("-calibrated",)
333
+
334
+ def method_label(self, obj):
335
+ return str(obj.method.label)
336
+
337
+ def get_queryset(self, request):
338
+ return self.model.objects.select_related("antenna", "radome")
339
+
340
+
341
+ class LogEntryAdmin(admin.ModelAdmin):
342
+ search_fields = ("site__name", "radome__model")
343
+ list_display = ("timestamp", "site", "type", "ip")
344
+ list_filter = ("type", "section")
345
+ ordering = ("-timestamp",)
346
+ readonly_fields = [
347
+ field.name for field in LogEntry._meta.get_fields() if field.name != "id"
348
+ ]
349
+
350
+ def get_queryset(self, request):
351
+ return self.model.objects.select_related("section", "file", "site", "user")
352
+
353
+
354
+ class HelpAdmin(admin.ModelAdmin):
355
+ pass
356
+
357
+
358
+ class AboutAdmin(admin.ModelAdmin):
359
+ pass
360
+
361
+
362
+ class SiteFileUploadAdmin(admin.ModelAdmin):
363
+ search_fields = ["site__name"]
364
+ list_filter = ["file_type", "log_format"]
365
+
366
+
367
+ class AlertAdminMixin:
368
+ actions = ["send_emails"]
369
+
370
+ def send_emails(self, request, queryset):
371
+ queryset.send_emails(request)
372
+
373
+ def get_form(self, request, obj=None, **kwargs):
374
+ form = super().get_form(request, obj, **kwargs)
375
+ form.base_fields["issuer"].initial = request.user
376
+ form.base_fields["issuer"].disabled = True
377
+ form.base_fields["issuer"].widget.can_change_related = False
378
+ form.base_fields["issuer"].widget.can_delete_related = False
379
+ form.base_fields["issuer"].widget.can_add_related = False
380
+ form.base_fields["priority"].initial = getattr(
381
+ self.model, "DEFAULT_PRIORITY", 0
382
+ )
383
+ return form
384
+
385
+
386
+ class AlertChildAdmin(AlertAdminMixin, PolymorphicChildModelAdmin):
387
+ base_model = Alert
388
+
389
+
390
+ @admin.register(UserAlert)
391
+ class UserAlertAdmin(AlertChildAdmin):
392
+ base_model = UserAlert
393
+ show_in_index = True
394
+
395
+
396
+ @admin.register(SiteAlert)
397
+ class SiteAlertAdmin(AlertChildAdmin):
398
+ base_model = SiteAlert
399
+ show_in_index = True
400
+
401
+
402
+ @admin.register(AgencyAlert)
403
+ class AgencyAlertAdmin(AlertChildAdmin):
404
+ base_model = AgencyAlert
405
+ show_in_index = True
406
+
407
+
408
+ @admin.register(GeodesyMLInvalid)
409
+ class GeodesyMLInvalidAdmin(AlertChildAdmin):
410
+ base_model = GeodesyMLInvalid
411
+ show_in_index = True
412
+
413
+
414
+ @admin.register(ReviewRequested)
415
+ class ReviewRequestedAdmin(AlertChildAdmin):
416
+ base_model = ReviewRequested
417
+ show_in_index = True
418
+
419
+
420
+ @admin.register(UnpublishedFilesAlert)
421
+ class UnpublishedFilesAlertAdmin(AlertChildAdmin):
422
+ base_model = UnpublishedFilesAlert
423
+ show_in_index = True
424
+
425
+
426
+ @admin.register(SiteLogPublished)
427
+ class SiteLogPublishedAdmin(AlertChildAdmin):
428
+ base_model = SiteLogPublished
429
+ show_in_index = True
430
+
431
+
432
+ @admin.register(UpdatesRejected)
433
+ class UpdatesRejectedAdmin(AlertChildAdmin):
434
+ base_model = UpdatesRejected
435
+ show_in_index = True
436
+
437
+
438
+ @admin.register(Alert)
439
+ class AlertAdmin(AlertAdminMixin, PolymorphicParentModelAdmin):
440
+ """The parent alert model admin"""
441
+
442
+ base_model = Alert
443
+ child_models = (
444
+ Alert,
445
+ UserAlert,
446
+ SiteAlert,
447
+ AgencyAlert,
448
+ GeodesyMLInvalid,
449
+ ReviewRequested,
450
+ UpdatesRejected,
451
+ )
452
+ list_filter = (PolymorphicChildModelFilter,)
453
+
454
+
455
+ class ArchiveFileInline(admin.TabularInline):
456
+ model = ArchivedSiteLog
457
+ extra = 0
458
+ readonly_fields = [
459
+ field.name if field.name != "file" else "path"
460
+ for field in ArchivedSiteLog._meta.get_fields()
461
+ if field.name not in ["id", "site", "thumbnail", "name", "timestamp"]
462
+ ]
463
+ can_delete = False
464
+ exclude = ["site", "file", "thumbnail", "name", "timestamp"]
465
+
466
+ def path(self, obj):
467
+ return obj.file.path
468
+
469
+ def has_add_permission(self, request, obj):
470
+ return False
471
+
472
+
473
+ class ArchiveIndexAdmin(admin.ModelAdmin):
474
+ search_fields = ["site__name"]
475
+ list_display = ("site", "begin", "end")
476
+
477
+ ordering = ("-begin",)
478
+
479
+ inlines = [ArchiveFileInline]
480
+ readonly_fields = ["site", "begin", "end"]
481
+
482
+ def get_queryset(self, request):
483
+ return self.model.objects.select_related("site").prefetch_related("files")
484
+
485
+
486
+ """
487
+ class SLMAdminSite(admin.AdminSite):
488
+
489
+ def get_app_list(self, request, app_label=None):
490
+ #Return a sorted list of all the installed apps that have been
491
+ #registered in this site.
492
+ ret = super().get_app_list(request, app_label=app_label)
493
+
494
+ # force group all alerts together
495
+ spoofed_alerts_app = {
496
+ 'app_label': 'slm_alerts',
497
+ 'app_url': '/admin/slm/alerts/',
498
+ 'has_module_perms': True,
499
+ 'models': [],
500
+ 'name': 'SLM Alerts'
501
+ }
502
+
503
+ for app in ret:
504
+ models = []
505
+ for model in app['models']:
506
+ if issubclass(model['model'], Alert):
507
+ spoofed_alerts_app['models'].append(model)
508
+ else:
509
+ models.append(model)
510
+ app['models'] = models
511
+
512
+ ret.insert(1, spoofed_alerts_app)
513
+ return ret
514
+ """
515
+
516
+ # admin.site = SLMAdminSite()
517
+
518
+
519
+ @admin.register(Manufacturer)
520
+ class ManufacturerAdmin(admin.ModelAdmin):
521
+ search_fields = ("name", "full_name")
522
+ ordering = ("name",)
523
+ list_display = ("name", "full_name", "url")
524
+
525
+
526
+ class EquipmentForm(forms.ModelForm):
527
+ class Meta:
528
+ model = Equipment
529
+ fields = "__all__"
530
+
531
+ def __init__(self, *args, **kwargs):
532
+ super().__init__(*args, **kwargs)
533
+ obj = kwargs.get("instance", None)
534
+ self.fields["replaced"].required = False
535
+ if obj:
536
+ self.fields["replaced"].queryset = self.Meta.model.objects.filter(
537
+ manufacturer=obj.manufacturer
538
+ )
539
+
540
+
541
+ class EquipmentAdmin(admin.ModelAdmin):
542
+ search_fields = ("model",)
543
+ ordering = ("model",)
544
+ list_display = ("model", "manufacturer", "state", "references", "active")
545
+ list_filter = ("state", "manufacturer")
546
+
547
+ @admin.display(ordering="references")
548
+ def references(self, obj):
549
+ return obj.references
550
+
551
+ @admin.display(ordering="active")
552
+ def active(self, obj):
553
+ return obj.active
554
+
555
+ related_siteform = None # deriving classes must set this
556
+
557
+ def get_queryset(self, request):
558
+ return (
559
+ super()
560
+ .get_queryset(request)
561
+ .select_related("manufacturer")
562
+ .annotate(
563
+ references=Count(
564
+ self.related_siteform,
565
+ filter=(Q(**{f"{self.related_siteform}__published": True})),
566
+ distinct=True,
567
+ ),
568
+ active=Count(
569
+ f"{self.related_siteform}__site",
570
+ filter=(
571
+ Q(**{f"{self.related_siteform}__published": True})
572
+ & Q(**{f"{self.related_siteform}__removed__isnull": True})
573
+ & Q(
574
+ **{
575
+ f"{self.related_siteform}__site__status__in": SiteLogStatus.active_states()
576
+ }
577
+ )
578
+ ),
579
+ distinct=True,
580
+ ),
581
+ )
582
+ )
583
+
584
+
585
+ class HasGraphicListFilter(admin.SimpleListFilter):
586
+ title = _("Has Graphic?")
587
+
588
+ parameter_name = "graphic"
589
+
590
+ def lookups(self, request, model_admin):
591
+ return (
592
+ (True, _("Yes")),
593
+ (False, _("No")),
594
+ )
595
+
596
+ def queryset(self, request, queryset):
597
+ if self.value() is None:
598
+ return queryset
599
+ qry = ~Q(graphic__isnull=True) & ~Q(graphic="")
600
+ if self.value().lower() == "false":
601
+ qry = ~qry
602
+ return queryset.filter(qry)
603
+
604
+
605
+ @admin.register(Antenna)
606
+ class AntennaAdmin(EquipmentAdmin):
607
+ class AntennaForm(EquipmentForm):
608
+ graphic = forms.CharField(
609
+ strip=False,
610
+ widget=GraphicTextarea,
611
+ help_text=Antenna._meta.get_field("graphic").help_text,
612
+ )
613
+
614
+ class Meta(EquipmentForm.Meta):
615
+ model = Antenna
616
+
617
+ form = AntennaForm
618
+ related_siteform = "site_antennas"
619
+
620
+ list_display = (*EquipmentAdmin.list_display, "has_graphic")
621
+ list_filter = (HasGraphicListFilter, *EquipmentAdmin.list_filter)
622
+
623
+ def has_graphic(self, obj):
624
+ return _("Yes") if obj.has_graphic else _("No")
625
+
626
+ def get_queryset(self, request):
627
+ return (
628
+ super()
629
+ .get_queryset(request)
630
+ .annotate(
631
+ has_graphic=ExpressionWrapper(
632
+ ~Q(graphic__isnull=True) & ~Q(graphic=""),
633
+ output_field=BooleanField(),
634
+ )
635
+ )
636
+ )
637
+
638
+
639
+ @admin.register(Receiver)
640
+ class ReceiverAdmin(EquipmentAdmin):
641
+ class ReceiverForm(EquipmentForm):
642
+ class Meta(EquipmentForm.Meta):
643
+ model = Receiver
644
+
645
+ form = ReceiverForm
646
+ related_siteform = "site_receivers"
647
+
648
+
649
+ @admin.register(Radome)
650
+ class RadomeAdmin(EquipmentAdmin):
651
+ class RadomeForm(EquipmentForm):
652
+ class Meta(EquipmentForm.Meta):
653
+ model = Radome
654
+
655
+ form = RadomeForm
656
+ related_siteform = "site_radomes"
657
+
658
+
659
+ admin.site.site_header = _("SLM Admin")
660
+
661
+ admin.site.register(get_user_model(), UserAdmin)
662
+ admin.site.register(Group, GroupAdmin)
663
+ admin.site.register(Agency, AgencyAdmin)
664
+ admin.site.register(TideGauge, TideGaugeAdmin)
665
+ admin.site.register(SatelliteSystem, SatelliteSystemAdmin)
666
+ admin.site.register(SiteFileUpload, SiteFileUploadAdmin)
667
+ admin.site.register(Help, HelpAdmin)
668
+ admin.site.register(About, AboutAdmin)
669
+ admin.site.register(AntCal, AntennaCalibrationAdmin)
670
+ admin.site.register(DataCenter, DataCenterAdmin)
671
+ admin.site.register(LogEntry, LogEntryAdmin)
672
+ admin.site.register(ArchiveIndex, ArchiveIndexAdmin)
673
+
674
+ admin.autodiscover()
File without changes