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.
- igs_slm-0.1.0b0.dist-info/LICENSE +21 -0
- igs_slm-0.1.0b0.dist-info/METADATA +151 -0
- igs_slm-0.1.0b0.dist-info/RECORD +447 -0
- igs_slm-0.1.0b0.dist-info/WHEEL +4 -0
- igs_slm-0.1.0b0.dist-info/entry_points.txt +3 -0
- igs_tools/__init__.py +0 -0
- igs_tools/connection.py +88 -0
- igs_tools/defines/__init__.py +8 -0
- igs_tools/defines/constellation.py +21 -0
- igs_tools/defines/data_center.py +75 -0
- igs_tools/defines/rinex.py +49 -0
- igs_tools/directory.py +247 -0
- igs_tools/utils.py +66 -0
- slm/__init__.py +21 -0
- slm/admin.py +674 -0
- slm/api/edit/__init__.py +0 -0
- slm/api/edit/serializers.py +316 -0
- slm/api/edit/views.py +1632 -0
- slm/api/fields.py +89 -0
- slm/api/filter.py +504 -0
- slm/api/pagination.py +55 -0
- slm/api/permissions.py +65 -0
- slm/api/public/__init__.py +0 -0
- slm/api/public/serializers.py +249 -0
- slm/api/public/views.py +606 -0
- slm/api/serializers.py +132 -0
- slm/api/views.py +148 -0
- slm/apps.py +323 -0
- slm/authentication.py +198 -0
- slm/bin/__init__.py +0 -0
- slm/bin/startproject.py +262 -0
- slm/bin/templates/{{ project_dir }}/pyproject.toml +35 -0
- slm/bin/templates/{{ project_dir }}/sites/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/base.py +15 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/__init__.py +56 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/local.py +4 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/develop/wsgi.py +16 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/manage.py +34 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/production/__init__.py +61 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/production/wsgi.py +16 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/urls.py +7 -0
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/validation.py +11 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/admin.py +5 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/apps.py +14 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/commands/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/management/commands/import_archive.py +64 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/migrations/__init__.py +0 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/models.py +6 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/templates/slm/base.html +8 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/urls.py +10 -0
- slm/bin/templates/{{ project_dir }}/{{ extension_app }}/views.py +5 -0
- slm/defines/AlertLevel.py +24 -0
- slm/defines/AntennaCalibration.py +25 -0
- slm/defines/AntennaFeatures.py +27 -0
- slm/defines/AntennaReferencePoint.py +22 -0
- slm/defines/Aspiration.py +13 -0
- slm/defines/CardinalDirection.py +19 -0
- slm/defines/CollocationStatus.py +12 -0
- slm/defines/EquipmentState.py +22 -0
- slm/defines/FlagSeverity.py +14 -0
- slm/defines/FractureSpacing.py +15 -0
- slm/defines/FrequencyStandardType.py +15 -0
- slm/defines/GeodesyMLVersion.py +48 -0
- slm/defines/ISOCountry.py +1194 -0
- slm/defines/Instrumentation.py +19 -0
- slm/defines/LogEntryType.py +30 -0
- slm/defines/SLMFileType.py +18 -0
- slm/defines/SiteFileUploadStatus.py +61 -0
- slm/defines/SiteLogFormat.py +49 -0
- slm/defines/SiteLogStatus.py +78 -0
- slm/defines/TectonicPlates.py +28 -0
- slm/defines/__init__.py +46 -0
- slm/forms.py +1126 -0
- slm/jinja2/slm/sitelog/ascii_9char.log +346 -0
- slm/jinja2/slm/sitelog/legacy.log +346 -0
- slm/jinja2/slm/sitelog/xsd/0.4/collocationInformation.xml +12 -0
- slm/jinja2/slm/sitelog/xsd/0.4/condition.xml +12 -0
- slm/jinja2/slm/sitelog/xsd/0.4/contact.xml +52 -0
- slm/jinja2/slm/sitelog/xsd/0.4/formInformation.xml +5 -0
- slm/jinja2/slm/sitelog/xsd/0.4/frequencyStandard.xml +12 -0
- slm/jinja2/slm/sitelog/xsd/0.4/gnssAntenna.xml +16 -0
- slm/jinja2/slm/sitelog/xsd/0.4/gnssReceiver.xml +11 -0
- slm/jinja2/slm/sitelog/xsd/0.4/humiditySensor.xml +13 -0
- slm/jinja2/slm/sitelog/xsd/0.4/localEpisodicEffect.xml +10 -0
- slm/jinja2/slm/sitelog/xsd/0.4/moreInformation.xml +22 -0
- slm/jinja2/slm/sitelog/xsd/0.4/multipathSource.xml +10 -0
- slm/jinja2/slm/sitelog/xsd/0.4/otherInstrumentation.xml +5 -0
- slm/jinja2/slm/sitelog/xsd/0.4/pressureSensor.xml +12 -0
- slm/jinja2/slm/sitelog/xsd/0.4/radioInterference.xml +11 -0
- slm/jinja2/slm/sitelog/xsd/0.4/sensor.xml +16 -0
- slm/jinja2/slm/sitelog/xsd/0.4/signalObstruction.xml +10 -0
- slm/jinja2/slm/sitelog/xsd/0.4/siteIdentification.xml +22 -0
- slm/jinja2/slm/sitelog/xsd/0.4/siteLocation.xml +21 -0
- slm/jinja2/slm/sitelog/xsd/0.4/surveyedLocalTie.xml +20 -0
- slm/jinja2/slm/sitelog/xsd/0.4/temperatureSensor.xml +13 -0
- slm/jinja2/slm/sitelog/xsd/0.4/waterVaporSensor.xml +11 -0
- slm/jinja2/slm/sitelog/xsd/0.5/document.xml +10 -0
- slm/jinja2/slm/sitelog/xsd/geodesyml_0.4.xml +99 -0
- slm/jinja2/slm/sitelog/xsd/geodesyml_0.5.xml +112 -0
- slm/management/__init__.py +0 -0
- slm/management/commands/__init__.py +53 -0
- slm/management/commands/build_index.py +96 -0
- slm/management/commands/generate_sinex.py +675 -0
- slm/management/commands/head_from_index.py +541 -0
- slm/management/commands/import_archive.py +908 -0
- slm/management/commands/import_equipment.py +351 -0
- slm/management/commands/set_site.py +56 -0
- slm/management/commands/sitelog.py +144 -0
- slm/management/commands/synchronize.py +60 -0
- slm/management/commands/update_data_availability.py +167 -0
- slm/management/commands/validate_db.py +186 -0
- slm/management/commands/validate_gml.py +73 -0
- slm/map/__init__.py +1 -0
- slm/map/admin.py +5 -0
- slm/map/api/__init__.py +0 -0
- slm/map/api/edit/__init__.py +0 -0
- slm/map/api/edit/serializers.py +28 -0
- slm/map/api/edit/views.py +46 -0
- slm/map/api/public/__init__.py +0 -0
- slm/map/api/public/serializers.py +29 -0
- slm/map/api/public/views.py +64 -0
- slm/map/apps.py +7 -0
- slm/map/defines.py +53 -0
- slm/map/migrations/0001_initial.py +115 -0
- slm/map/migrations/__init__.py +0 -0
- slm/map/models.py +63 -0
- slm/map/static/slm/css/map.css +86 -0
- slm/map/static/slm/js/map.js +159 -0
- slm/map/templates/slm/map.html +374 -0
- slm/map/templates/slm/station/base.html +11 -0
- slm/map/templates/slm/station/edit.html +10 -0
- slm/map/templates/slm/top_nav.html +17 -0
- slm/map/templatetags/__init__.py +0 -0
- slm/map/templatetags/slm_map.py +18 -0
- slm/map/urls.py +25 -0
- slm/map/views.py +36 -0
- slm/middleware.py +29 -0
- slm/migrations/0001_alter_siteantenna_marker_enu_alter_sitelocation_llh_and_more.py +47 -0
- slm/migrations/0001_initial.py +4826 -0
- slm/migrations/0002_alter_dataavailability_site.py +22 -0
- slm/migrations/0003_remove_logentry_slm_logentr_site_lo_7a2af7_idx_and_more.py +80 -0
- slm/migrations/0004_alter_logentry_timestamp_and_more.py +25 -0
- slm/migrations/0005_alter_logentry_options_alter_logentry_section_and_more.py +46 -0
- slm/migrations/0006_alter_logentry_options_alter_logentry_index_together.py +24 -0
- slm/migrations/0007_alter_dataavailability_rate.py +23 -0
- slm/migrations/0008_alter_archiveindex_options_and_more.py +64 -0
- slm/migrations/0009_alter_archiveindex_end.py +21 -0
- slm/migrations/0010_alter_dataavailability_rinex_version_and_more.py +844 -0
- slm/migrations/0011_alter_siteidentification_fracture_spacing.py +33 -0
- slm/migrations/0012_alter_logentry_type.py +36 -0
- slm/migrations/0013_unpublishedfilesalert.py +48 -0
- slm/migrations/0014_sitelogpublished.py +48 -0
- slm/migrations/0015_alter_siteantenna_options_and_more.py +181 -0
- slm/migrations/0016_alter_antenna_description_alter_radome_description_and_more.py +42 -0
- slm/migrations/0017_alter_logentry_unique_together_and_more.py +54 -0
- slm/migrations/0018_afix_deleted.py +34 -0
- slm/migrations/0018_alter_siteantenna_options_and_more.py +244 -0
- slm/migrations/0019_remove_siteantenna_marker_enu_siteantenna_marker_une_and_more.py +101 -0
- slm/migrations/0020_alter_manufacturer_options.py +16 -0
- slm/migrations/0021_alter_siteform_report_type.py +23 -0
- slm/migrations/0022_rename_antcal_antenna_radome_slm_antcal_antenna_20827a_idx_and_more.py +297 -0
- slm/migrations/0023_archivedsitelog_gml_version_and_more.py +55 -0
- slm/migrations/0024_alter_agency_name_alter_agency_shortname.py +24 -0
- slm/migrations/0025_alter_archivedsitelog_log_format_and_more.py +61 -0
- slm/migrations/0026_alter_archivedsitelog_log_format_and_more.py +61 -0
- slm/migrations/0027_importalert_file_contents_importalert_findings_and_more.py +41 -0
- slm/migrations/0028_antenna_replaced_manufacturer_url_radome_replaced_and_more.py +46 -0
- slm/migrations/0029_manufacturer_full_name.py +17 -0
- slm/migrations/0030_alter_antenna_state_alter_radome_state_and_more.py +43 -0
- slm/migrations/__init__.py +0 -0
- slm/migrations/load_satellitesystems.py +27 -0
- slm/models/__init__.py +118 -0
- slm/models/about.py +14 -0
- slm/models/alerts.py +1204 -0
- slm/models/data.py +58 -0
- slm/models/equipment.py +229 -0
- slm/models/help.py +14 -0
- slm/models/index.py +428 -0
- slm/models/sitelog.py +4279 -0
- slm/models/system.py +723 -0
- slm/models/user.py +304 -0
- slm/parsing/__init__.py +786 -0
- slm/parsing/legacy/__init__.py +4 -0
- slm/parsing/legacy/binding.py +817 -0
- slm/parsing/legacy/parser.py +377 -0
- slm/parsing/xsd/__init__.py +34 -0
- slm/parsing/xsd/binding.py +86 -0
- slm/parsing/xsd/geodesyml/0.4/commonTypes.xsd +133 -0
- slm/parsing/xsd/geodesyml/0.4/contact.xsd +29 -0
- slm/parsing/xsd/geodesyml/0.4/dataStreams.xsd +129 -0
- slm/parsing/xsd/geodesyml/0.4/document.xsd +64 -0
- slm/parsing/xsd/geodesyml/0.4/equipment.xsd +427 -0
- slm/parsing/xsd/geodesyml/0.4/fieldMeasurement.xsd +170 -0
- slm/parsing/xsd/geodesyml/0.4/geodesyML.xsd +71 -0
- slm/parsing/xsd/geodesyml/0.4/geodeticEquipment.xsd +343 -0
- slm/parsing/xsd/geodesyml/0.4/geodeticMonument.xsd +147 -0
- slm/parsing/xsd/geodesyml/0.4/lineage.xsd +614 -0
- slm/parsing/xsd/geodesyml/0.4/localInterferences.xsd +131 -0
- slm/parsing/xsd/geodesyml/0.4/measurement.xsd +473 -0
- slm/parsing/xsd/geodesyml/0.4/monumentInfo.xsd +251 -0
- slm/parsing/xsd/geodesyml/0.4/observationSystem.xsd +429 -0
- slm/parsing/xsd/geodesyml/0.4/project.xsd +38 -0
- slm/parsing/xsd/geodesyml/0.4/quality.xsd +176 -0
- slm/parsing/xsd/geodesyml/0.4/referenceFrame.xsd +194 -0
- slm/parsing/xsd/geodesyml/0.4/siteLog.xsd +71 -0
- slm/parsing/xsd/geodesyml/0.5/commonTypes.xsd +133 -0
- slm/parsing/xsd/geodesyml/0.5/contact.xsd +29 -0
- slm/parsing/xsd/geodesyml/0.5/dataStreams.xsd +129 -0
- slm/parsing/xsd/geodesyml/0.5/document.xsd +64 -0
- slm/parsing/xsd/geodesyml/0.5/equipment.xsd +427 -0
- slm/parsing/xsd/geodesyml/0.5/fieldMeasurement.xsd +170 -0
- slm/parsing/xsd/geodesyml/0.5/geodesyML.xsd +71 -0
- slm/parsing/xsd/geodesyml/0.5/geodeticEquipment.xsd +343 -0
- slm/parsing/xsd/geodesyml/0.5/geodeticMonument.xsd +147 -0
- slm/parsing/xsd/geodesyml/0.5/lineage.xsd +614 -0
- slm/parsing/xsd/geodesyml/0.5/localInterferences.xsd +131 -0
- slm/parsing/xsd/geodesyml/0.5/measurement.xsd +473 -0
- slm/parsing/xsd/geodesyml/0.5/monumentInfo.xsd +306 -0
- slm/parsing/xsd/geodesyml/0.5/observationSystem.xsd +429 -0
- slm/parsing/xsd/geodesyml/0.5/project.xsd +38 -0
- slm/parsing/xsd/geodesyml/0.5/quality.xsd +176 -0
- slm/parsing/xsd/geodesyml/0.5/referenceFrame.xsd +194 -0
- slm/parsing/xsd/geodesyml/0.5/siteLog.xsd +73 -0
- slm/parsing/xsd/parser.py +116 -0
- slm/parsing/xsd/resolver.py +28 -0
- slm/receivers/__init__.py +11 -0
- slm/receivers/alerts.py +87 -0
- slm/receivers/cleanup.py +41 -0
- slm/receivers/event_loggers.py +175 -0
- slm/receivers/index.py +67 -0
- slm/settings/__init__.py +55 -0
- slm/settings/auth.py +15 -0
- slm/settings/ckeditor.py +14 -0
- slm/settings/debug.py +47 -0
- slm/settings/internationalization.py +12 -0
- slm/settings/logging.py +113 -0
- slm/settings/platform/__init__.py +0 -0
- slm/settings/platform/darwin.py +10 -0
- slm/settings/rest.py +21 -0
- slm/settings/root.py +152 -0
- slm/settings/routines.py +43 -0
- slm/settings/secrets.py +37 -0
- slm/settings/security.py +5 -0
- slm/settings/slm.py +188 -0
- slm/settings/static_templates.py +53 -0
- slm/settings/templates.py +29 -0
- slm/settings/uploads.py +8 -0
- slm/settings/urls.py +126 -0
- slm/settings/validation.py +196 -0
- slm/signals.py +250 -0
- slm/singleton.py +49 -0
- slm/static/rest_framework/css/bootstrap-tweaks.css +204 -0
- slm/static/rest_framework/css/bootstrap.min.css +7 -0
- slm/static/rest_framework/css/bootstrap.min.css.map +1 -0
- slm/static/rest_framework/css/default.css +82 -0
- slm/static/rest_framework/css/prettify.css +30 -0
- slm/static/rest_framework/docs/css/base.css +344 -0
- slm/static/rest_framework/docs/css/highlight.css +125 -0
- slm/static/rest_framework/docs/css/jquery.json-view.min.css +11 -0
- slm/static/rest_framework/docs/img/favicon.ico +0 -0
- slm/static/rest_framework/docs/img/grid.png +0 -0
- slm/static/rest_framework/docs/js/api.js +321 -0
- slm/static/rest_framework/docs/js/highlight.pack.js +2 -0
- slm/static/rest_framework/docs/js/jquery.json-view.min.js +7 -0
- slm/static/rest_framework/img/grid.png +0 -0
- slm/static/rest_framework/js/ajax-form.js +127 -0
- slm/static/rest_framework/js/bootstrap.bundle.min.js +7 -0
- slm/static/rest_framework/js/bootstrap.bundle.min.js.map +1 -0
- slm/static/rest_framework/js/bootstrap.min.js.map +1 -0
- slm/static/rest_framework/js/coreapi-0.1.1.js +2043 -0
- slm/static/rest_framework/js/csrf.js +52 -0
- slm/static/rest_framework/js/default.js +47 -0
- slm/static/rest_framework/js/jquery-3.5.1.min.js +2 -0
- slm/static/rest_framework/js/prettify-min.js +28 -0
- slm/static/slm/css/admin.css +3 -0
- slm/static/slm/css/defines.css +82 -0
- slm/static/slm/css/forms.css +1 -0
- slm/static/slm/css/style.css +1004 -0
- slm/static/slm/img/email-branding.png +0 -0
- slm/static/slm/img/favicon.ico +0 -0
- slm/static/slm/img/login-bg.jpg +0 -0
- slm/static/slm/img/slm-logo.svg +4 -0
- slm/static/slm/js/autocomplete.js +341 -0
- slm/static/slm/js/enums.js +322 -0
- slm/static/slm/js/fileIcons.js +30 -0
- slm/static/slm/js/form.js +404 -0
- slm/static/slm/js/formWidget.js +23 -0
- slm/static/slm/js/persistable.js +33 -0
- slm/static/slm/js/slm.js +1028 -0
- slm/static/slm/js/time24.js +212 -0
- slm/static_templates/slm/css/defines.css +26 -0
- slm/static_templates/slm/js/enums.js +28 -0
- slm/static_templates/slm/js/fileIcons.js +16 -0
- slm/static_templates/slm/js/urls.js +5 -0
- slm/templates/account/base.html +20 -0
- slm/templates/account/email/base.html +43 -0
- slm/templates/account/email/base_message.txt +7 -0
- slm/templates/account/email/email_confirmation_message.html +16 -0
- slm/templates/account/email/email_confirmation_message.txt +7 -0
- slm/templates/account/email/email_confirmation_signup_message.html +1 -0
- slm/templates/account/email/email_confirmation_signup_message.txt +1 -0
- slm/templates/account/email/email_confirmation_signup_subject.txt +1 -0
- slm/templates/account/email/email_confirmation_subject.txt +4 -0
- slm/templates/account/email/password_reset_key_message.html +28 -0
- slm/templates/account/email/password_reset_key_message.txt +9 -0
- slm/templates/account/email/password_reset_key_subject.txt +4 -0
- slm/templates/account/email/unknown_account_message.html +25 -0
- slm/templates/account/email/unknown_account_message.txt +12 -0
- slm/templates/account/email/unknown_account_subject.txt +4 -0
- slm/templates/account/login.html +67 -0
- slm/templates/account/logout.html +38 -0
- slm/templates/account/password_change.html +48 -0
- slm/templates/account/password_reset.html +51 -0
- slm/templates/account/password_reset_done.html +20 -0
- slm/templates/account/password_reset_from_key.html +52 -0
- slm/templates/account/password_reset_from_key_done.html +17 -0
- slm/templates/admin/base.html +7 -0
- slm/templates/messages.html +8 -0
- slm/templates/rest_framework/README +16 -0
- slm/templates/rest_framework/admin/detail.html +10 -0
- slm/templates/rest_framework/admin/dict_value.html +11 -0
- slm/templates/rest_framework/admin/list.html +32 -0
- slm/templates/rest_framework/admin/list_value.html +11 -0
- slm/templates/rest_framework/admin/simple_list_value.html +2 -0
- slm/templates/rest_framework/admin.html +282 -0
- slm/templates/rest_framework/api.html +3 -0
- slm/templates/rest_framework/base.html +334 -0
- slm/templates/rest_framework/docs/auth/basic.html +42 -0
- slm/templates/rest_framework/docs/auth/session.html +40 -0
- slm/templates/rest_framework/docs/auth/token.html +41 -0
- slm/templates/rest_framework/docs/document.html +37 -0
- slm/templates/rest_framework/docs/error.html +71 -0
- slm/templates/rest_framework/docs/index.html +55 -0
- slm/templates/rest_framework/docs/interact.html +57 -0
- slm/templates/rest_framework/docs/langs/javascript-intro.html +5 -0
- slm/templates/rest_framework/docs/langs/javascript.html +15 -0
- slm/templates/rest_framework/docs/langs/python-intro.html +3 -0
- slm/templates/rest_framework/docs/langs/python.html +13 -0
- slm/templates/rest_framework/docs/langs/shell-intro.html +3 -0
- slm/templates/rest_framework/docs/langs/shell.html +6 -0
- slm/templates/rest_framework/docs/link.html +113 -0
- slm/templates/rest_framework/docs/sidebar.html +78 -0
- slm/templates/rest_framework/filters/base.html +16 -0
- slm/templates/rest_framework/filters/ordering.html +17 -0
- slm/templates/rest_framework/filters/search.html +13 -0
- slm/templates/rest_framework/horizontal/checkbox.html +23 -0
- slm/templates/rest_framework/horizontal/checkbox_multiple.html +32 -0
- slm/templates/rest_framework/horizontal/dict_field.html +11 -0
- slm/templates/rest_framework/horizontal/fieldset.html +16 -0
- slm/templates/rest_framework/horizontal/form.html +6 -0
- slm/templates/rest_framework/horizontal/input.html +21 -0
- slm/templates/rest_framework/horizontal/list_field.html +11 -0
- slm/templates/rest_framework/horizontal/list_fieldset.html +13 -0
- slm/templates/rest_framework/horizontal/radio.html +42 -0
- slm/templates/rest_framework/horizontal/select.html +36 -0
- slm/templates/rest_framework/horizontal/select_multiple.html +38 -0
- slm/templates/rest_framework/horizontal/textarea.html +21 -0
- slm/templates/rest_framework/inline/checkbox.html +8 -0
- slm/templates/rest_framework/inline/checkbox_multiple.html +14 -0
- slm/templates/rest_framework/inline/dict_field.html +9 -0
- slm/templates/rest_framework/inline/fieldset.html +6 -0
- slm/templates/rest_framework/inline/form.html +8 -0
- slm/templates/rest_framework/inline/input.html +9 -0
- slm/templates/rest_framework/inline/list_field.html +9 -0
- slm/templates/rest_framework/inline/list_fieldset.html +3 -0
- slm/templates/rest_framework/inline/radio.html +25 -0
- slm/templates/rest_framework/inline/select.html +24 -0
- slm/templates/rest_framework/inline/select_multiple.html +25 -0
- slm/templates/rest_framework/inline/textarea.html +9 -0
- slm/templates/rest_framework/login.html +3 -0
- slm/templates/rest_framework/login_base.html +65 -0
- slm/templates/rest_framework/pagination/numbers.html +47 -0
- slm/templates/rest_framework/pagination/previous_and_next.html +21 -0
- slm/templates/rest_framework/raw_data_form.html +11 -0
- slm/templates/rest_framework/schema.js +3 -0
- slm/templates/rest_framework/vertical/checkbox.html +16 -0
- slm/templates/rest_framework/vertical/checkbox_multiple.html +30 -0
- slm/templates/rest_framework/vertical/dict_field.html +7 -0
- slm/templates/rest_framework/vertical/fieldset.html +13 -0
- slm/templates/rest_framework/vertical/form.html +6 -0
- slm/templates/rest_framework/vertical/input.html +17 -0
- slm/templates/rest_framework/vertical/list_field.html +7 -0
- slm/templates/rest_framework/vertical/list_fieldset.html +7 -0
- slm/templates/rest_framework/vertical/radio.html +40 -0
- slm/templates/rest_framework/vertical/select.html +34 -0
- slm/templates/rest_framework/vertical/select_multiple.html +31 -0
- slm/templates/rest_framework/vertical/textarea.html +17 -0
- slm/templates/slm/about.html +21 -0
- slm/templates/slm/alerts/alert.html +15 -0
- slm/templates/slm/alerts/geodesymlinvalid.html +8 -0
- slm/templates/slm/alerts/importalert.html +10 -0
- slm/templates/slm/alerts.html +18 -0
- slm/templates/slm/auth_menu.html +41 -0
- slm/templates/slm/base.html +195 -0
- slm/templates/slm/emails/alert_issued.html +31 -0
- slm/templates/slm/emails/alert_issued.txt +9 -0
- slm/templates/slm/emails/base.html +6 -0
- slm/templates/slm/emails/changes_rejected.txt +7 -0
- slm/templates/slm/emails/review_requested.txt +7 -0
- slm/templates/slm/forms/widgets/auto_complete.html +21 -0
- slm/templates/slm/forms/widgets/auto_complete_multiple.html +18 -0
- slm/templates/slm/forms/widgets/checkbox_multiple.html +6 -0
- slm/templates/slm/forms/widgets/inline_multi.html +1 -0
- slm/templates/slm/forms/widgets/splitdatetime.html +14 -0
- slm/templates/slm/forms/widgets/time24.html +37 -0
- slm/templates/slm/help.html +54 -0
- slm/templates/slm/messages.html +13 -0
- slm/templates/slm/new_site.html +88 -0
- slm/templates/slm/profile.html +57 -0
- slm/templates/slm/register.html +40 -0
- slm/templates/slm/reports/file_log.html +43 -0
- slm/templates/slm/reports/head_log.html +23 -0
- slm/templates/slm/reports/head_report.html +55 -0
- slm/templates/slm/reports/index_log.html +23 -0
- slm/templates/slm/reports/index_report.html +71 -0
- slm/templates/slm/station/alert.html +8 -0
- slm/templates/slm/station/alerts.html +19 -0
- slm/templates/slm/station/base.html +104 -0
- slm/templates/slm/station/download.html +87 -0
- slm/templates/slm/station/edit.html +283 -0
- slm/templates/slm/station/form.html +110 -0
- slm/templates/slm/station/log.html +18 -0
- slm/templates/slm/station/review.html +461 -0
- slm/templates/slm/station/upload.html +295 -0
- slm/templates/slm/station/uploads/attachment.html +20 -0
- slm/templates/slm/station/uploads/geodesyml.html +1 -0
- slm/templates/slm/station/uploads/image.html +27 -0
- slm/templates/slm/station/uploads/json.html +0 -0
- slm/templates/slm/station/uploads/legacy.html +77 -0
- slm/templates/slm/top_nav.html +14 -0
- slm/templates/slm/user_activity.html +16 -0
- slm/templates/slm/widgets/alert_scroll.html +135 -0
- slm/templates/slm/widgets/filelist.html +258 -0
- slm/templates/slm/widgets/legend.html +12 -0
- slm/templates/slm/widgets/log_scroll.html +88 -0
- slm/templates/slm/widgets/stationlist.html +233 -0
- slm/templatetags/__init__.py +0 -0
- slm/templatetags/jinja2.py +9 -0
- slm/templatetags/slm.py +459 -0
- slm/urls.py +148 -0
- slm/utils.py +299 -0
- slm/validators.py +297 -0
- slm/views.py +654 -0
- 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"]
|