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
|
@@ -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
slm/map/api/__init__.py
ADDED
|
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
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
|