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/forms.py
ADDED
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles forms for site log sections, user
|
|
3
|
+
management.
|
|
4
|
+
|
|
5
|
+
There is a form for each sitelog section.
|
|
6
|
+
|
|
7
|
+
More info on forms:
|
|
8
|
+
https://docs.djangoproject.com/en/stable/topics/forms/
|
|
9
|
+
|
|
10
|
+
More info on field types:
|
|
11
|
+
https://docs.djangoproject.com/en/stable/ref/models/fields/
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
from ckeditor.widgets import CKEditorWidget
|
|
17
|
+
from crispy_forms.helper import FormHelper
|
|
18
|
+
from crispy_forms.layout import Div, Layout
|
|
19
|
+
from django import forms
|
|
20
|
+
from django.contrib.gis.forms import PointField
|
|
21
|
+
from django.contrib.gis.geos import Point, Polygon
|
|
22
|
+
from django.core.exceptions import FieldDoesNotExist, ValidationError
|
|
23
|
+
from django.core.validators import MinValueValidator
|
|
24
|
+
from django.db import transaction
|
|
25
|
+
from django.db.models import Max
|
|
26
|
+
from django.db.models.fields import NOT_PROVIDED
|
|
27
|
+
from django.forms.fields import BooleanField, CharField, TypedMultipleChoiceField
|
|
28
|
+
from django.forms.widgets import (
|
|
29
|
+
CheckboxInput,
|
|
30
|
+
CheckboxSelectMultiple,
|
|
31
|
+
MultiWidget,
|
|
32
|
+
NullBooleanSelect,
|
|
33
|
+
NumberInput,
|
|
34
|
+
Select,
|
|
35
|
+
TextInput,
|
|
36
|
+
)
|
|
37
|
+
from django.urls import reverse_lazy
|
|
38
|
+
from django.utils.functional import Promise, SimpleLazyObject, cached_property
|
|
39
|
+
from django.utils.translation import gettext as _
|
|
40
|
+
from django_enum.choices import choices
|
|
41
|
+
from django_enum.forms import EnumChoiceField
|
|
42
|
+
from polyline import polyline
|
|
43
|
+
|
|
44
|
+
from slm.api.edit.serializers import UserProfileSerializer, UserSerializer
|
|
45
|
+
from slm.defines import (
|
|
46
|
+
AlertLevel,
|
|
47
|
+
CardinalDirection,
|
|
48
|
+
FrequencyStandardType,
|
|
49
|
+
ISOCountry,
|
|
50
|
+
SiteLogStatus,
|
|
51
|
+
SLMFileType,
|
|
52
|
+
)
|
|
53
|
+
from slm.models import (
|
|
54
|
+
Agency,
|
|
55
|
+
Alert,
|
|
56
|
+
Antenna,
|
|
57
|
+
Network,
|
|
58
|
+
Radome,
|
|
59
|
+
Receiver,
|
|
60
|
+
SatelliteSystem,
|
|
61
|
+
Site,
|
|
62
|
+
SiteAntenna,
|
|
63
|
+
SiteCollocation,
|
|
64
|
+
SiteFileUpload,
|
|
65
|
+
SiteForm,
|
|
66
|
+
SiteFrequencyStandard,
|
|
67
|
+
SiteHumiditySensor,
|
|
68
|
+
SiteIdentification,
|
|
69
|
+
SiteLocalEpisodicEffects,
|
|
70
|
+
SiteLocation,
|
|
71
|
+
SiteMoreInformation,
|
|
72
|
+
SiteMultiPathSources,
|
|
73
|
+
SiteOperationalContact,
|
|
74
|
+
SiteOtherInstrumentation,
|
|
75
|
+
SitePressureSensor,
|
|
76
|
+
SiteRadioInterferences,
|
|
77
|
+
SiteReceiver,
|
|
78
|
+
SiteResponsibleAgency,
|
|
79
|
+
SiteSignalObstructions,
|
|
80
|
+
SiteSurveyedLocalTies,
|
|
81
|
+
SiteTemperatureSensor,
|
|
82
|
+
SiteWaterVaporRadiometer,
|
|
83
|
+
)
|
|
84
|
+
from slm.validators import FieldRequired, get_validators
|
|
85
|
+
from slm.widgets import (
|
|
86
|
+
AutoComplete,
|
|
87
|
+
AutoCompleteEnumSelectMultiple,
|
|
88
|
+
AutoCompleteSelectMultiple,
|
|
89
|
+
DatePicker,
|
|
90
|
+
EnumSelectMultiple,
|
|
91
|
+
GraphicTextarea,
|
|
92
|
+
SLMCheckboxSelectMultiple,
|
|
93
|
+
SLMDateTimeWidget,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class SLMCheckboxInput(CheckboxInput):
|
|
98
|
+
"""
|
|
99
|
+
Django's BooleanField/NullBooleanField does not properly handle value ==
|
|
100
|
+
'on' or value == 'off' which are submitted as form data for checkboxes and
|
|
101
|
+
switches in certain circumstances. We Provide our own extensions here that
|
|
102
|
+
do this. This might change in the future and if so these fields can be
|
|
103
|
+
deleted and replaced by the Django internal ones.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def format_value(self, value):
|
|
107
|
+
if isinstance(value, list):
|
|
108
|
+
value = value[0]
|
|
109
|
+
return {"on": "true", "off": "false"}.get(
|
|
110
|
+
value.lower() if isinstance(value, str) else value,
|
|
111
|
+
super().format_value(value),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def value_from_datadict(self, data, files, name):
|
|
115
|
+
value = data.get(name)
|
|
116
|
+
if isinstance(value, list):
|
|
117
|
+
value = value[0]
|
|
118
|
+
return {"on": True, "off": False}.get(
|
|
119
|
+
value.lower() if isinstance(value, str) else value,
|
|
120
|
+
super().value_from_datadict(data, files, name),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class SLMBooleanField(BooleanField):
|
|
125
|
+
widget = SLMCheckboxInput
|
|
126
|
+
|
|
127
|
+
def to_python(self, value):
|
|
128
|
+
if isinstance(value, str) and value.lower() in ("false", "0", "off"):
|
|
129
|
+
value = False
|
|
130
|
+
else:
|
|
131
|
+
value = bool(value)
|
|
132
|
+
return super().to_python(value)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class SLMNullBooleanSelect(NullBooleanSelect):
|
|
136
|
+
def __init__(self, attrs=None):
|
|
137
|
+
choices = (
|
|
138
|
+
("", _("Unknown")),
|
|
139
|
+
("true", _("Yes")),
|
|
140
|
+
("false", _("No")),
|
|
141
|
+
)
|
|
142
|
+
# skip NullBooleanSelect init
|
|
143
|
+
Select.__init__(self, attrs, choices)
|
|
144
|
+
|
|
145
|
+
def format_value(self, value):
|
|
146
|
+
if value in [None, "", "None"]:
|
|
147
|
+
return [""]
|
|
148
|
+
return [super().format_value(value)]
|
|
149
|
+
|
|
150
|
+
def value_from_datadict(self, data, files, name):
|
|
151
|
+
value = data.get(name)
|
|
152
|
+
return {None: None, "": None}.get(
|
|
153
|
+
value, super().value_from_datadict(data, files, name)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class SiteAntennaGraphicField(CharField):
|
|
158
|
+
widget = GraphicTextarea
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class PolylineWidget(TextInput):
|
|
162
|
+
def value_from_datadict(self, data, files, name):
|
|
163
|
+
if hasattr(data, "getlist"):
|
|
164
|
+
return data.getlist(name, []) or None
|
|
165
|
+
return data.get(name, []) or None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class PolylineListField(forms.CharField):
|
|
169
|
+
default_error_messages = {
|
|
170
|
+
"invalid": _(
|
|
171
|
+
"Unable to decode polyline {poly} - {error}. Bounding boxes "
|
|
172
|
+
"should be line strings holding (longitude, latitude) tuples "
|
|
173
|
+
"encoded using Google's polyline algorithm"
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
widget = PolylineWidget
|
|
178
|
+
|
|
179
|
+
def clean(self, value):
|
|
180
|
+
if value is None or value == [""]:
|
|
181
|
+
return None
|
|
182
|
+
polygons = []
|
|
183
|
+
for poly in value:
|
|
184
|
+
try:
|
|
185
|
+
linear_ring = polyline.decode(poly)
|
|
186
|
+
linear_ring.append(linear_ring[0]) # close the ring
|
|
187
|
+
polygons.append(Polygon(linear_ring))
|
|
188
|
+
except Exception as exc:
|
|
189
|
+
raise ValidationError(
|
|
190
|
+
self.error_messages["invalid"].format(poly=poly, error=str(exc)),
|
|
191
|
+
code="invalid",
|
|
192
|
+
)
|
|
193
|
+
return polygons
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class EnumMultipleChoiceField(EnumChoiceField, TypedMultipleChoiceField):
|
|
197
|
+
"""
|
|
198
|
+
The default ``ChoiceField`` will only accept the base enumeration values.
|
|
199
|
+
Use this field on forms to accept any value mappable to an enumeration
|
|
200
|
+
including any labels or symmetric properties.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
widget = EnumSelectMultiple
|
|
204
|
+
|
|
205
|
+
def __init__(self, *args, **kwargs):
|
|
206
|
+
coerce = self.coerce
|
|
207
|
+
super().__init__(*args, **kwargs)
|
|
208
|
+
self.coerce = coerce
|
|
209
|
+
|
|
210
|
+
def coerce(self, value):
|
|
211
|
+
if isinstance(value, self.enum):
|
|
212
|
+
return value
|
|
213
|
+
return [super(EnumMultipleChoiceField, self).coerce(val) for val in value]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class SLMDateField(forms.DateField):
|
|
217
|
+
input_type = "date"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class SLMTimeField(forms.TimeField):
|
|
221
|
+
input_type = "time"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class SLMDateTimeField(forms.SplitDateTimeField):
|
|
225
|
+
widget = SLMDateTimeWidget
|
|
226
|
+
|
|
227
|
+
def __init__(self, *args, **kwargs):
|
|
228
|
+
super().__init__(*args, **kwargs)
|
|
229
|
+
self.widget.widgets[0].input_type = "date"
|
|
230
|
+
self.widget.widgets[1].input_type = "time"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class PointWidget(MultiWidget):
|
|
234
|
+
dim = 3
|
|
235
|
+
|
|
236
|
+
template_name = "slm/forms/widgets/inline_multi.html"
|
|
237
|
+
|
|
238
|
+
def __init__(self, dim=dim, attrs=None):
|
|
239
|
+
self.dim = dim
|
|
240
|
+
super().__init__([NumberInput(attrs=attrs) for _ in range(0, self.dim)], attrs)
|
|
241
|
+
|
|
242
|
+
def get_context(self, name, value, attrs):
|
|
243
|
+
context = super().get_context(name, value, attrs)
|
|
244
|
+
# fix the names so they are all the same
|
|
245
|
+
for subwidget in context["widget"]["subwidgets"]:
|
|
246
|
+
subwidget["name"] = "_".join(subwidget["name"].split("_")[:-1])
|
|
247
|
+
return context
|
|
248
|
+
|
|
249
|
+
def decompress(self, values):
|
|
250
|
+
from django.contrib.gis.geos import Point
|
|
251
|
+
|
|
252
|
+
if values is None:
|
|
253
|
+
return Point(*[None for _ in range(0, self.dim)])
|
|
254
|
+
return Point(*values)
|
|
255
|
+
|
|
256
|
+
def value_from_datadict(self, data, files, name):
|
|
257
|
+
if name in data:
|
|
258
|
+
return [float(coord) for coord in data.getlist(name)]
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class SLMPointField(PointField):
|
|
263
|
+
widget = PointWidget
|
|
264
|
+
|
|
265
|
+
def __init__(self, *args, attrs=None, dim=None, **kwargs):
|
|
266
|
+
if dim is not None:
|
|
267
|
+
self.widget = self.widget(dim=dim)
|
|
268
|
+
super().__init__(*args, **kwargs)
|
|
269
|
+
if self.widget.attrs is None:
|
|
270
|
+
self.widget.attrs = {}
|
|
271
|
+
self.widget.attrs.update(attrs or {})
|
|
272
|
+
|
|
273
|
+
def clean(self, value):
|
|
274
|
+
"""
|
|
275
|
+
Raise a ValidationError if the value cannot be
|
|
276
|
+
instantiated as a Point - otherwise return the Point or None.
|
|
277
|
+
"""
|
|
278
|
+
if value is None:
|
|
279
|
+
return value
|
|
280
|
+
|
|
281
|
+
# Ensuring that the geometry is of the correct type (indicated
|
|
282
|
+
# using the OGC string label).
|
|
283
|
+
if len(value) != self.widget.dim:
|
|
284
|
+
raise ValidationError(
|
|
285
|
+
self.error_messages["invalid_geom_type"], code="invalid_geom_type"
|
|
286
|
+
)
|
|
287
|
+
return (
|
|
288
|
+
Point(*[None if val in ["", None] else float(val) for val in value]) or None
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class AutoSelectMixin:
|
|
293
|
+
def __init__(
|
|
294
|
+
self,
|
|
295
|
+
*args,
|
|
296
|
+
value_param="id",
|
|
297
|
+
label_param=None,
|
|
298
|
+
render_suggestion=None,
|
|
299
|
+
query_params=None,
|
|
300
|
+
menu_class=None,
|
|
301
|
+
**kwargs,
|
|
302
|
+
):
|
|
303
|
+
super().__init__(*args, **kwargs)
|
|
304
|
+
self.widget.attrs.update({"data-value-param": value_param})
|
|
305
|
+
self.widget.attrs["data-value-param"] = value_param
|
|
306
|
+
if label_param:
|
|
307
|
+
self.widget.attrs["data-label-param"] = label_param
|
|
308
|
+
if render_suggestion:
|
|
309
|
+
self.widget.attrs["data-render-suggestion"] = render_suggestion
|
|
310
|
+
if menu_class:
|
|
311
|
+
self.widget.attrs["data-menu-class"] = menu_class
|
|
312
|
+
if query_params:
|
|
313
|
+
self.widget.attrs["data-query-params"] = (
|
|
314
|
+
query_params
|
|
315
|
+
if isinstance(query_params, str)
|
|
316
|
+
else json.dumps(query_params)
|
|
317
|
+
)
|
|
318
|
+
self.widget.attrs["class"] = " ".join(
|
|
319
|
+
[*self.widget.attrs.get("class", "").split(" "), "search-input"]
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class ModelAutoSelectMixin(AutoSelectMixin):
|
|
324
|
+
def __init__(self, service_url, *args, search_param="search", **kwargs):
|
|
325
|
+
super().__init__(*args, **kwargs)
|
|
326
|
+
self.widget.attrs["data-service-url"] = service_url
|
|
327
|
+
self.widget.attrs["data-search-param"] = search_param
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class ModelAutoComplete(ModelAutoSelectMixin, forms.ModelChoiceField):
|
|
331
|
+
widget = AutoComplete
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class ModelMultipleAutoComplete(ModelAutoSelectMixin, forms.ModelMultipleChoiceField):
|
|
335
|
+
widget = AutoCompleteSelectMultiple
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class EnumAutoSelectMixin(AutoSelectMixin):
|
|
339
|
+
class PropertyEncoder(json.JSONEncoder):
|
|
340
|
+
def default(self, obj):
|
|
341
|
+
if isinstance(obj, Promise):
|
|
342
|
+
return str(obj)
|
|
343
|
+
return super().default(obj)
|
|
344
|
+
|
|
345
|
+
def __init__(self, enum, *args, properties=None, data_source=None, **kwargs):
|
|
346
|
+
super().__init__(
|
|
347
|
+
enum,
|
|
348
|
+
*args,
|
|
349
|
+
value_param=kwargs.pop("value_param", "value"),
|
|
350
|
+
label_param=kwargs.pop("label_param", "label"),
|
|
351
|
+
**kwargs,
|
|
352
|
+
)
|
|
353
|
+
properties = set(properties or [])
|
|
354
|
+
properties.update({"value", "label"})
|
|
355
|
+
|
|
356
|
+
def lazy_source():
|
|
357
|
+
"""
|
|
358
|
+
Data sources might hit the database - so we have to evaluate them
|
|
359
|
+
lazily at widget render time instead of before django bootstrapping
|
|
360
|
+
"""
|
|
361
|
+
return json.dumps(
|
|
362
|
+
sorted(
|
|
363
|
+
[
|
|
364
|
+
{prop: getattr(en, prop) for prop in properties}
|
|
365
|
+
for en in (data_source() if data_source else enum or [])
|
|
366
|
+
],
|
|
367
|
+
key=lambda en: en[self.widget.attrs["data-label-param"]],
|
|
368
|
+
),
|
|
369
|
+
cls=MultiSelectEnumAutoComplete.PropertyEncoder,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
self.widget.attrs["data-source"] = SimpleLazyObject(lazy_source)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class MultiSelectEnumAutoComplete(EnumAutoSelectMixin, EnumMultipleChoiceField):
|
|
376
|
+
widget = AutoCompleteEnumSelectMultiple
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class SelectEnumAutoComplete(EnumAutoSelectMixin, EnumChoiceField):
|
|
380
|
+
widget = AutoComplete
|
|
381
|
+
|
|
382
|
+
def __init__(self, *args, widget=AutoComplete, **kwargs):
|
|
383
|
+
super().__init__(*args, widget=widget, **kwargs)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class NewSiteForm(forms.ModelForm):
|
|
387
|
+
@property
|
|
388
|
+
def helper(self):
|
|
389
|
+
helper = FormHelper()
|
|
390
|
+
helper.form_id = "slm-new-site-form"
|
|
391
|
+
helper.layout = Layout(
|
|
392
|
+
Div(
|
|
393
|
+
Div("name", css_class="col-3"),
|
|
394
|
+
Div("agencies", css_class="col-9"),
|
|
395
|
+
css_class="row",
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
return helper
|
|
399
|
+
|
|
400
|
+
agencies = ModelMultipleAutoComplete(
|
|
401
|
+
queryset=Agency.objects.all(),
|
|
402
|
+
help_text=_("Enter the name or abbreviation of an Agency."),
|
|
403
|
+
label=_("Agency"),
|
|
404
|
+
required=False,
|
|
405
|
+
service_url=reverse_lazy("slm_edit_api:agency-list"),
|
|
406
|
+
search_param="search",
|
|
407
|
+
value_param="id",
|
|
408
|
+
label_param="name",
|
|
409
|
+
render_suggestion="return `(${obj.shortname}) ${obj.name}`;",
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
class Meta:
|
|
413
|
+
model = Site
|
|
414
|
+
fields = ["name", "agencies"]
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class SectionForm(forms.ModelForm):
|
|
418
|
+
def __init__(self, instance=None, **kwargs):
|
|
419
|
+
self.diff = instance.published_diff() if instance else {}
|
|
420
|
+
self.flags = instance._flags if instance else {}
|
|
421
|
+
super().__init__(instance=instance, **kwargs)
|
|
422
|
+
for field in self.fields:
|
|
423
|
+
try:
|
|
424
|
+
model_field = self.Meta.model._meta.get_field(field)
|
|
425
|
+
for validator in get_validators(self.Meta.model, model_field.name):
|
|
426
|
+
if isinstance(validator, FieldRequired):
|
|
427
|
+
self.fields[field].required = True
|
|
428
|
+
break
|
|
429
|
+
self.fields[field].required |= not (
|
|
430
|
+
getattr(model_field, "default", None) != NOT_PROVIDED
|
|
431
|
+
and model_field.blank
|
|
432
|
+
)
|
|
433
|
+
self.fields[field].widget.attrs.setdefault("class", "")
|
|
434
|
+
self.fields[field].widget.attrs["class"] += " slm-form-field"
|
|
435
|
+
except FieldDoesNotExist:
|
|
436
|
+
pass
|
|
437
|
+
|
|
438
|
+
@classmethod
|
|
439
|
+
def section_name(cls):
|
|
440
|
+
return cls.Meta.model.section_name()
|
|
441
|
+
|
|
442
|
+
@classmethod
|
|
443
|
+
def section_slug(cls):
|
|
444
|
+
return cls.Meta.model.section_slug()
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def num_flags(self):
|
|
448
|
+
return len(self.flags)
|
|
449
|
+
|
|
450
|
+
@classmethod
|
|
451
|
+
def api(cls):
|
|
452
|
+
return f"slm_edit_api:{cls.Meta.model.__name__.lower()}"
|
|
453
|
+
|
|
454
|
+
@cached_property
|
|
455
|
+
def structured_fields(self):
|
|
456
|
+
# todo this is spaghetti
|
|
457
|
+
# arrange fields in structure to easily produce fieldsets in
|
|
458
|
+
# correct order in the template, reflects old site
|
|
459
|
+
# log order and groupings
|
|
460
|
+
fields = []
|
|
461
|
+
|
|
462
|
+
def flatten(structure):
|
|
463
|
+
flat = []
|
|
464
|
+
for field in structure:
|
|
465
|
+
if isinstance(field, tuple) or isinstance(field, list):
|
|
466
|
+
flat += flatten(field)
|
|
467
|
+
else:
|
|
468
|
+
flat.append(field)
|
|
469
|
+
return flat
|
|
470
|
+
|
|
471
|
+
def resolve_field(field_name):
|
|
472
|
+
fields = []
|
|
473
|
+
if field_name not in self.fields:
|
|
474
|
+
if hasattr(getattr(self.Meta.model, field_name), "field"):
|
|
475
|
+
field = getattr(self.Meta.model, field_name).field
|
|
476
|
+
if isinstance(field, tuple) or isinstance(field, list):
|
|
477
|
+
fields.extend([fd.name for fd in field])
|
|
478
|
+
else:
|
|
479
|
+
fields.append(field.name)
|
|
480
|
+
else:
|
|
481
|
+
fields.append(field_name)
|
|
482
|
+
|
|
483
|
+
return [
|
|
484
|
+
self.fields[field].get_bound_field(form=self, field_name=field)
|
|
485
|
+
for field in fields
|
|
486
|
+
]
|
|
487
|
+
|
|
488
|
+
for structure in self.Meta.model.structure():
|
|
489
|
+
if isinstance(structure, tuple) or isinstance(structure, list):
|
|
490
|
+
group_fields = []
|
|
491
|
+
try:
|
|
492
|
+
self._meta.model._meta.get_field(structure[0])
|
|
493
|
+
group = None
|
|
494
|
+
group_fields.append(resolve_field(structure[0])[0])
|
|
495
|
+
except FieldDoesNotExist:
|
|
496
|
+
group = structure[0]
|
|
497
|
+
|
|
498
|
+
for field in flatten(structure[1]):
|
|
499
|
+
group_fields.extend(resolve_field(field))
|
|
500
|
+
|
|
501
|
+
fields.append((group, group_fields))
|
|
502
|
+
else:
|
|
503
|
+
fields.append((None, resolve_field(structure)))
|
|
504
|
+
fields += [(None, [field]) for field in self.hidden_fields()]
|
|
505
|
+
return fields
|
|
506
|
+
|
|
507
|
+
# todo this might be a security hole - restrict queryset to user's stations
|
|
508
|
+
site = forms.ModelChoiceField(
|
|
509
|
+
queryset=Site.objects.all(), widget=forms.HiddenInput()
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
id = forms.IntegerField(
|
|
513
|
+
validators=[MinValueValidator(0)], widget=forms.HiddenInput(), required=False
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
class Meta:
|
|
517
|
+
fields = ["site", "id"]
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class SubSectionForm(SectionForm):
|
|
521
|
+
subsection = forms.IntegerField(
|
|
522
|
+
validators=[MinValueValidator(0)], widget=forms.HiddenInput(), required=False
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
def save(self, commit=True):
|
|
526
|
+
if self.instance.subsection is None:
|
|
527
|
+
with transaction.atomic():
|
|
528
|
+
# todo is there a race condition here?
|
|
529
|
+
self.instance.subsection = (
|
|
530
|
+
self.Meta.model.objects.select_for_update()
|
|
531
|
+
.filter(site=self.instance.site)
|
|
532
|
+
.aggregate(Max("subsection"))["subsection__max"]
|
|
533
|
+
or 0
|
|
534
|
+
) + 1
|
|
535
|
+
|
|
536
|
+
return super().save(commit=commit)
|
|
537
|
+
return super().save(commit=commit)
|
|
538
|
+
|
|
539
|
+
@classmethod
|
|
540
|
+
def group_name(cls):
|
|
541
|
+
if hasattr(cls, "NAV_HEADING"):
|
|
542
|
+
return cls.NAV_HEADING.replace(" ", "_").replace(".", "").strip().lower()
|
|
543
|
+
return None
|
|
544
|
+
|
|
545
|
+
class Meta(SectionForm.Meta):
|
|
546
|
+
fields = [*SectionForm.Meta.fields, "subsection"]
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class SiteFormForm(SectionForm):
|
|
550
|
+
def __init__(self, *args, **kwargs):
|
|
551
|
+
super().__init__(*args, **kwargs)
|
|
552
|
+
for field in ["report_type", "date_prepared"]:
|
|
553
|
+
self.fields[field].required = False
|
|
554
|
+
self.fields[field].widget.attrs["disabled"] = "disabled"
|
|
555
|
+
|
|
556
|
+
# we only include this for legacy purposes - this is not an editable value
|
|
557
|
+
previous_log = forms.CharField(
|
|
558
|
+
label=_("Previous Site Log"),
|
|
559
|
+
help_text=_(
|
|
560
|
+
"Previous site log in this format: ssss_CCYYMMDD.log "
|
|
561
|
+
"Format: (ssss = 4 character site name). If the site already has "
|
|
562
|
+
"a log at the IGS Central Bureau it can be found at"
|
|
563
|
+
"https://files.igs.org/pub/station/log/"
|
|
564
|
+
),
|
|
565
|
+
disabled=True,
|
|
566
|
+
required=False,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
class Meta(SectionForm.Meta):
|
|
570
|
+
model = SiteForm
|
|
571
|
+
fields = [*SectionForm.Meta.fields, *SiteForm.site_log_fields(), "previous_log"]
|
|
572
|
+
widgets = {"date_prepared": DatePicker}
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class SiteIdentificationForm(SectionForm):
|
|
576
|
+
# we only include this for legacy purposes - this is not an editable value
|
|
577
|
+
nine_character_id = forms.CharField(
|
|
578
|
+
label=_("Nine Character ID"),
|
|
579
|
+
help_text=_(
|
|
580
|
+
"This is the 9 Character station name (XXXXMRCCC) used in RINEX 3 "
|
|
581
|
+
"filenames. Format: (XXXX - existing four character IGS station "
|
|
582
|
+
"name, M - Monument or marker number (0-9), R - Receiver number "
|
|
583
|
+
"(0-9), CCC - Three digit ISO 3166-1 country code)"
|
|
584
|
+
),
|
|
585
|
+
disabled=True,
|
|
586
|
+
required=False,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
class Meta(SectionForm.Meta):
|
|
590
|
+
model = SiteIdentification
|
|
591
|
+
fields = [
|
|
592
|
+
*SectionForm.Meta.fields,
|
|
593
|
+
*SiteIdentification.site_log_fields(),
|
|
594
|
+
"nine_character_id",
|
|
595
|
+
]
|
|
596
|
+
field_classes = {"date_installed": SLMDateTimeField}
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class SiteLocationForm(SectionForm):
|
|
600
|
+
country = SelectEnumAutoComplete(
|
|
601
|
+
ISOCountry,
|
|
602
|
+
help_text=SiteLocation._meta.get_field("country").help_text,
|
|
603
|
+
label=SiteLocation._meta.get_field("country").verbose_name,
|
|
604
|
+
render_suggestion=(
|
|
605
|
+
'return `<span class="fi fi-${obj.value.toLowerCase()}"></span>'
|
|
606
|
+
'<span class="matchable">${obj.label}</span>`;'
|
|
607
|
+
),
|
|
608
|
+
strict=False,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
xyz = SLMPointField(
|
|
612
|
+
help_text=SiteLocation._meta.get_field("xyz").help_text,
|
|
613
|
+
label=SiteLocation._meta.get_field("xyz").verbose_name,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
llh = SLMPointField(
|
|
617
|
+
help_text=SiteLocation._meta.get_field("llh").help_text,
|
|
618
|
+
label=SiteLocation._meta.get_field("llh").verbose_name,
|
|
619
|
+
attrs={"step": 0.0000001},
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
class Meta:
|
|
623
|
+
model = SiteLocation
|
|
624
|
+
fields = [*SectionForm.Meta.fields, *SiteLocation.site_log_fields()]
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class SiteReceiverForm(SubSectionForm):
|
|
628
|
+
COPY_LAST_ON_ADD = [
|
|
629
|
+
field
|
|
630
|
+
for field in [*SubSectionForm.Meta.fields, *SiteReceiver.site_log_fields()]
|
|
631
|
+
if field not in {"installed", "removed", "additional_info"}
|
|
632
|
+
]
|
|
633
|
+
|
|
634
|
+
satellite_system = forms.ModelChoiceField(
|
|
635
|
+
queryset=SatelliteSystem.objects.all(),
|
|
636
|
+
help_text=SiteReceiver._meta.get_field("satellite_system").help_text,
|
|
637
|
+
label=SiteReceiver._meta.get_field("satellite_system").verbose_name,
|
|
638
|
+
required=True,
|
|
639
|
+
widget=SLMCheckboxSelectMultiple(columns=4),
|
|
640
|
+
empty_label=None,
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
receiver_type = ModelAutoComplete(
|
|
644
|
+
queryset=Receiver.objects.all(),
|
|
645
|
+
service_url=reverse_lazy("slm_public_api:receiver-list"),
|
|
646
|
+
help_text=SiteReceiver._meta.get_field("receiver_type").help_text,
|
|
647
|
+
label=SiteReceiver._meta.get_field("receiver_type").verbose_name,
|
|
648
|
+
search_param="model",
|
|
649
|
+
value_param="model",
|
|
650
|
+
label_param="model",
|
|
651
|
+
to_field_name="model",
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
temp_stabilized = forms.NullBooleanField(
|
|
655
|
+
help_text=SiteReceiver._meta.get_field("temp_stabilized").help_text,
|
|
656
|
+
label=SiteReceiver._meta.get_field("temp_stabilized").verbose_name,
|
|
657
|
+
widget=SLMNullBooleanSelect(),
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
def __init__(self, **kwargs):
|
|
661
|
+
super().__init__(**kwargs)
|
|
662
|
+
# todo why is this not automatically done?
|
|
663
|
+
if "satellite_system" in self.initial:
|
|
664
|
+
self.initial["satellite_system"] = [
|
|
665
|
+
system.name for system in self.initial["satellite_system"].all()
|
|
666
|
+
]
|
|
667
|
+
|
|
668
|
+
class Meta(SubSectionForm):
|
|
669
|
+
model = SiteReceiver
|
|
670
|
+
fields = [*SubSectionForm.Meta.fields, *SiteReceiver.site_log_fields()]
|
|
671
|
+
field_classes = {"installed": SLMDateTimeField, "removed": SLMDateTimeField}
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
class SiteAntennaForm(SubSectionForm):
|
|
675
|
+
COPY_LAST_ON_ADD = [
|
|
676
|
+
field
|
|
677
|
+
for field in [*SubSectionForm.Meta.fields, *SiteAntenna.site_log_fields()]
|
|
678
|
+
if field not in {"installed", "removed", "additional_info"}
|
|
679
|
+
]
|
|
680
|
+
|
|
681
|
+
marker_une = SLMPointField(
|
|
682
|
+
help_text=SiteAntenna._meta.get_field("marker_une").help_text,
|
|
683
|
+
label=SiteAntenna._meta.get_field("marker_une").verbose_name,
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
alignment = forms.FloatField(
|
|
687
|
+
required=SiteAntenna._meta.get_field("alignment").blank,
|
|
688
|
+
help_text=SiteAntenna._meta.get_field("alignment").help_text,
|
|
689
|
+
label=SiteAntenna._meta.get_field("alignment").verbose_name,
|
|
690
|
+
max_value=180,
|
|
691
|
+
min_value=-180,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
antenna_type = ModelAutoComplete(
|
|
695
|
+
queryset=Antenna.objects.all(),
|
|
696
|
+
service_url=reverse_lazy("slm_public_api:antenna-list"),
|
|
697
|
+
help_text=SiteAntenna._meta.get_field("antenna_type").help_text,
|
|
698
|
+
label=SiteAntenna._meta.get_field("antenna_type").verbose_name,
|
|
699
|
+
search_param="model",
|
|
700
|
+
value_param="model",
|
|
701
|
+
label_param="model",
|
|
702
|
+
to_field_name="model",
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
radome_type = ModelAutoComplete(
|
|
706
|
+
queryset=Radome.objects.all(),
|
|
707
|
+
service_url=reverse_lazy("slm_public_api:radome-list"),
|
|
708
|
+
help_text=SiteAntenna._meta.get_field("radome_type").help_text,
|
|
709
|
+
label=SiteAntenna._meta.get_field("radome_type").verbose_name,
|
|
710
|
+
search_param="model",
|
|
711
|
+
value_param="model",
|
|
712
|
+
label_param="model",
|
|
713
|
+
to_field_name="model",
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
def __init__(self, *args, **kwargs):
|
|
717
|
+
super().__init__(*args, **kwargs)
|
|
718
|
+
# Check if the instance exists (i.e., it's an edit form, not a new form)
|
|
719
|
+
if self.instance and self.instance.pk:
|
|
720
|
+
self.fields["custom_graphic"].initial = self.instance.graphic
|
|
721
|
+
|
|
722
|
+
class Meta(SubSectionForm):
|
|
723
|
+
model = SiteAntenna
|
|
724
|
+
fields = [*SubSectionForm.Meta.fields, *SiteAntenna.site_log_fields()]
|
|
725
|
+
field_classes = {
|
|
726
|
+
"installed": SLMDateTimeField,
|
|
727
|
+
"removed": SLMDateTimeField,
|
|
728
|
+
"custom_graphic": SiteAntennaGraphicField,
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
class SiteSurveyedLocalTiesForm(SubSectionForm):
|
|
733
|
+
diff_xyz = SLMPointField(
|
|
734
|
+
help_text=SiteSurveyedLocalTies._meta.get_field("diff_xyz").help_text,
|
|
735
|
+
label=SiteSurveyedLocalTies._meta.get_field("diff_xyz").verbose_name,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
class Meta(SubSectionForm.Meta):
|
|
739
|
+
model = SiteSurveyedLocalTies
|
|
740
|
+
fields = [*SubSectionForm.Meta.fields, *SiteSurveyedLocalTies.site_log_fields()]
|
|
741
|
+
field_classes = {"measured": SLMDateTimeField, "diff_xyz": SLMPointField}
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
class SiteFrequencyStandardForm(SubSectionForm):
|
|
745
|
+
class Meta(SubSectionForm.Meta):
|
|
746
|
+
model = SiteFrequencyStandard
|
|
747
|
+
fields = [*SubSectionForm.Meta.fields, *SiteFrequencyStandard.site_log_fields()]
|
|
748
|
+
widgets = {"effective_start": DatePicker, "effective_end": DatePicker}
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
class SiteCollocationForm(SubSectionForm):
|
|
752
|
+
class Meta(SubSectionForm.Meta):
|
|
753
|
+
model = SiteCollocation
|
|
754
|
+
fields = [*SubSectionForm.Meta.fields, *SiteCollocation.site_log_fields()]
|
|
755
|
+
widgets = {"effective_start": DatePicker, "effective_end": DatePicker}
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
class MeteorologicalForm(SubSectionForm):
|
|
759
|
+
NAV_HEADING = _("Meteorological Instr.")
|
|
760
|
+
|
|
761
|
+
class Meta(SubSectionForm):
|
|
762
|
+
fields = SubSectionForm.Meta.fields
|
|
763
|
+
widgets = {
|
|
764
|
+
"calibration": DatePicker,
|
|
765
|
+
"effective_start": DatePicker,
|
|
766
|
+
"effective_end": DatePicker,
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
class SiteHumiditySensorForm(MeteorologicalForm):
|
|
771
|
+
class Meta(MeteorologicalForm.Meta):
|
|
772
|
+
model = SiteHumiditySensor
|
|
773
|
+
fields = [
|
|
774
|
+
*MeteorologicalForm.Meta.fields,
|
|
775
|
+
*SiteHumiditySensor.site_log_fields(),
|
|
776
|
+
]
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
class SitePressureSensorForm(MeteorologicalForm):
|
|
780
|
+
class Meta(MeteorologicalForm.Meta):
|
|
781
|
+
model = SitePressureSensor
|
|
782
|
+
fields = [
|
|
783
|
+
*MeteorologicalForm.Meta.fields,
|
|
784
|
+
*SitePressureSensor.site_log_fields(),
|
|
785
|
+
]
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
class SiteTemperatureSensorForm(MeteorologicalForm):
|
|
789
|
+
class Meta(MeteorologicalForm.Meta):
|
|
790
|
+
model = SiteTemperatureSensor
|
|
791
|
+
fields = [
|
|
792
|
+
*MeteorologicalForm.Meta.fields,
|
|
793
|
+
*SiteTemperatureSensor.site_log_fields(),
|
|
794
|
+
]
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
class SiteWaterVaporRadiometerForm(MeteorologicalForm):
|
|
798
|
+
class Meta(MeteorologicalForm.Meta):
|
|
799
|
+
model = SiteWaterVaporRadiometer
|
|
800
|
+
fields = [
|
|
801
|
+
*MeteorologicalForm.Meta.fields,
|
|
802
|
+
*SiteWaterVaporRadiometer.site_log_fields(),
|
|
803
|
+
]
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
class SiteOtherInstrumentationForm(MeteorologicalForm):
|
|
807
|
+
class Meta(MeteorologicalForm.Meta):
|
|
808
|
+
model = SiteOtherInstrumentation
|
|
809
|
+
fields = [
|
|
810
|
+
*MeteorologicalForm.Meta.fields,
|
|
811
|
+
*SiteOtherInstrumentation.site_log_fields(),
|
|
812
|
+
]
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
class LocalConditionForm(SubSectionForm):
|
|
816
|
+
NAV_HEADING = _("Local Conditions")
|
|
817
|
+
|
|
818
|
+
class Meta(SubSectionForm.Meta):
|
|
819
|
+
fields = SubSectionForm.Meta.fields
|
|
820
|
+
widgets = {"effective_start": DatePicker, "effective_end": DatePicker}
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
class SiteRadioInterferencesForm(LocalConditionForm):
|
|
824
|
+
class Meta(LocalConditionForm.Meta):
|
|
825
|
+
model = SiteRadioInterferences
|
|
826
|
+
fields = [
|
|
827
|
+
*LocalConditionForm.Meta.fields,
|
|
828
|
+
*SiteRadioInterferences.site_log_fields(),
|
|
829
|
+
]
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
class SiteMultiPathSourcesForm(LocalConditionForm):
|
|
833
|
+
class Meta(LocalConditionForm.Meta):
|
|
834
|
+
model = SiteMultiPathSources
|
|
835
|
+
fields = [
|
|
836
|
+
*LocalConditionForm.Meta.fields,
|
|
837
|
+
*SiteMultiPathSources.site_log_fields(),
|
|
838
|
+
]
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
class SiteSignalObstructionsForm(LocalConditionForm):
|
|
842
|
+
class Meta(LocalConditionForm.Meta):
|
|
843
|
+
model = SiteSignalObstructions
|
|
844
|
+
fields = [
|
|
845
|
+
*LocalConditionForm.Meta.fields,
|
|
846
|
+
*SiteSignalObstructions.site_log_fields(),
|
|
847
|
+
]
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
class SiteLocalEpisodicEffectsForm(SubSectionForm):
|
|
851
|
+
class Meta(SubSectionForm.Meta):
|
|
852
|
+
model = SiteLocalEpisodicEffects
|
|
853
|
+
fields = [
|
|
854
|
+
*SubSectionForm.Meta.fields,
|
|
855
|
+
*SiteLocalEpisodicEffects.site_log_fields(),
|
|
856
|
+
]
|
|
857
|
+
widgets = {"effective_start": DatePicker, "effective_end": DatePicker}
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
class AgencyPOCForm(SectionForm):
|
|
861
|
+
class Meta(SectionForm.Meta):
|
|
862
|
+
fields = SectionForm.Meta.fields
|
|
863
|
+
widgets = {
|
|
864
|
+
"agency": forms.Textarea(attrs={"rows": 2}),
|
|
865
|
+
"mailing_address": forms.Textarea(attrs={"rows": 4}),
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
class SiteOperationalContactForm(AgencyPOCForm):
|
|
870
|
+
class Meta(AgencyPOCForm.Meta):
|
|
871
|
+
model = SiteOperationalContact
|
|
872
|
+
fields = [*AgencyPOCForm.Meta.fields, *SiteOperationalContact.site_log_fields()]
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
class SiteResponsibleAgencyForm(AgencyPOCForm):
|
|
876
|
+
class Meta(AgencyPOCForm.Meta):
|
|
877
|
+
model = SiteResponsibleAgency
|
|
878
|
+
fields = [*AgencyPOCForm.Meta.fields, *SiteResponsibleAgency.site_log_fields()]
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
class SiteMoreInformationForm(SectionForm):
|
|
882
|
+
class Meta(SectionForm.Meta):
|
|
883
|
+
model = SiteMoreInformation
|
|
884
|
+
fields = [*SectionForm.Meta.fields, *SiteMoreInformation.site_log_fields()]
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
class UserForm(forms.ModelForm):
|
|
888
|
+
@property
|
|
889
|
+
def helper(self):
|
|
890
|
+
helper = FormHelper()
|
|
891
|
+
helper.form_id = "slm-user-form"
|
|
892
|
+
helper.layout = Layout(
|
|
893
|
+
Div(Div("email"), css_class="row"),
|
|
894
|
+
Div(
|
|
895
|
+
Div("first_name", css_class="col-6"),
|
|
896
|
+
Div("last_name", css_class="col-6"),
|
|
897
|
+
css_class="row",
|
|
898
|
+
),
|
|
899
|
+
Div(
|
|
900
|
+
Div("silence_alerts", css_class="col-6"),
|
|
901
|
+
Div("html_emails", css_class="col-6"),
|
|
902
|
+
css_class="row",
|
|
903
|
+
),
|
|
904
|
+
Div(Div("agencies"), css_class="row"),
|
|
905
|
+
)
|
|
906
|
+
return helper
|
|
907
|
+
|
|
908
|
+
agencies = forms.ModelMultipleChoiceField(
|
|
909
|
+
queryset=Agency.objects.all(), required=False, disabled=True
|
|
910
|
+
)
|
|
911
|
+
|
|
912
|
+
def __init__(self, *args, instance=None, **kwargs):
|
|
913
|
+
super().__init__(*args, instance=instance, **kwargs)
|
|
914
|
+
if instance:
|
|
915
|
+
self.fields["agencies"].queryset = instance.agencies.all()
|
|
916
|
+
|
|
917
|
+
class Meta:
|
|
918
|
+
model = UserSerializer.Meta.model
|
|
919
|
+
fields = UserSerializer.Meta.fields
|
|
920
|
+
exclude = ("date_joined", "profile")
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
class UserProfileForm(forms.ModelForm):
|
|
924
|
+
@property
|
|
925
|
+
def helper(self):
|
|
926
|
+
helper = FormHelper()
|
|
927
|
+
helper.form_id = "slm-user-profile-form"
|
|
928
|
+
helper.layout = Layout(
|
|
929
|
+
Div(
|
|
930
|
+
Div("phone1", css_class="col-6"),
|
|
931
|
+
Div("phone2", css_class="col-6"),
|
|
932
|
+
css_class="row",
|
|
933
|
+
),
|
|
934
|
+
Div(Div("address1"), css_class="row"),
|
|
935
|
+
Div(Div("address2"), css_class="row"),
|
|
936
|
+
Div(Div("address3"), css_class="row"),
|
|
937
|
+
Div(
|
|
938
|
+
Div("city", css_class="col-6"),
|
|
939
|
+
Div("state_province", css_class="col-6"),
|
|
940
|
+
css_class="row",
|
|
941
|
+
),
|
|
942
|
+
Div(
|
|
943
|
+
Div("country", css_class="col-6"),
|
|
944
|
+
Div("postal_code", css_class="col-6"),
|
|
945
|
+
css_class="row",
|
|
946
|
+
),
|
|
947
|
+
)
|
|
948
|
+
return helper
|
|
949
|
+
|
|
950
|
+
class Meta:
|
|
951
|
+
model = UserProfileSerializer.Meta.model
|
|
952
|
+
fields = UserProfileSerializer.Meta.fields
|
|
953
|
+
exclude = ("registration_agency",)
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
class SiteFileForm(forms.ModelForm):
|
|
957
|
+
name = forms.SlugField(max_length=255, help_text=_("The name of the file."))
|
|
958
|
+
|
|
959
|
+
direction = EnumChoiceField(
|
|
960
|
+
CardinalDirection,
|
|
961
|
+
help_text=SiteFileUpload._meta.get_field("direction").help_text,
|
|
962
|
+
label=SiteFileUpload._meta.get_field("direction").verbose_name,
|
|
963
|
+
choices=[("", "-" * 10), *choices(CardinalDirection)],
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
def __init__(self, *args, instance=None, **kwargs):
|
|
967
|
+
super().__init__(*args, instance=instance, **kwargs)
|
|
968
|
+
if instance and instance.file_type != SLMFileType.SITE_IMAGE:
|
|
969
|
+
self.fields["direction"].widget = forms.HiddenInput()
|
|
970
|
+
self.fields["direction"].disabled = True
|
|
971
|
+
|
|
972
|
+
class Meta:
|
|
973
|
+
model = SiteFileUpload
|
|
974
|
+
fields = ["name", "description", "direction"]
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
class RichTextForm(forms.Form):
|
|
978
|
+
text = forms.CharField(widget=CKEditorWidget(config_name="richtextinput"))
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
class StationFilterForm(forms.Form):
|
|
982
|
+
name = forms.CharField(required=False)
|
|
983
|
+
|
|
984
|
+
status = EnumMultipleChoiceField(
|
|
985
|
+
SiteLogStatus,
|
|
986
|
+
# help_text=_('Include stations with these statuses.'),
|
|
987
|
+
label=_("Site Status"),
|
|
988
|
+
required=False,
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
alert = forms.MultipleChoiceField(
|
|
992
|
+
choices=(
|
|
993
|
+
(alert.__name__, alert._meta.verbose_name)
|
|
994
|
+
for alert in Alert.objects.site_alerts()
|
|
995
|
+
),
|
|
996
|
+
widget=forms.CheckboxSelectMultiple(),
|
|
997
|
+
required=False,
|
|
998
|
+
# help_text=_('Include stations with the following alert types.'),
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
alert_level = EnumMultipleChoiceField(
|
|
1002
|
+
AlertLevel,
|
|
1003
|
+
# help_text=_('Include stations with alerts at this level.'),
|
|
1004
|
+
label=_("Alert Level"),
|
|
1005
|
+
required=False,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
station = ModelMultipleAutoComplete(
|
|
1009
|
+
queryset=Site.objects.public(),
|
|
1010
|
+
service_url=reverse_lazy("slm_public_api:name-list"),
|
|
1011
|
+
# help_text=Site._meta.get_field('name').help_text,
|
|
1012
|
+
# label=Site._meta.get_field('name').verbose_name,
|
|
1013
|
+
search_param="name",
|
|
1014
|
+
value_param="name",
|
|
1015
|
+
label_param="name",
|
|
1016
|
+
menu_class="station-names",
|
|
1017
|
+
to_field_name="name",
|
|
1018
|
+
required=False,
|
|
1019
|
+
)
|
|
1020
|
+
|
|
1021
|
+
satellite_system = forms.ModelMultipleChoiceField(
|
|
1022
|
+
queryset=SatelliteSystem.objects.all(),
|
|
1023
|
+
label=SiteReceiver._meta.get_field("satellite_system").verbose_name,
|
|
1024
|
+
required=False,
|
|
1025
|
+
widget=CheckboxSelectMultiple(),
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
frequency_standard = EnumMultipleChoiceField(
|
|
1029
|
+
FrequencyStandardType, required=False, label=_("Frequency Standard")
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
current = SLMBooleanField(
|
|
1033
|
+
label=_("Current"),
|
|
1034
|
+
help_text=_("Only include sites that currently have this equipment."),
|
|
1035
|
+
initial=True,
|
|
1036
|
+
required=False,
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
receiver = ModelMultipleAutoComplete(
|
|
1040
|
+
queryset=Receiver.objects.all(),
|
|
1041
|
+
# help_text=_('Enter the name or abbreviation of an Agency.'),
|
|
1042
|
+
label=_("Receiver"),
|
|
1043
|
+
required=False,
|
|
1044
|
+
service_url=reverse_lazy("slm_public_api:receiver-list"),
|
|
1045
|
+
search_param="model",
|
|
1046
|
+
value_param="id",
|
|
1047
|
+
label_param="model",
|
|
1048
|
+
query_params={"in_use": True},
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
antenna = ModelMultipleAutoComplete(
|
|
1052
|
+
queryset=Antenna.objects.all(),
|
|
1053
|
+
# help_text=_('Enter the name or abbreviation of an Agency.'),
|
|
1054
|
+
label=_("Antenna"),
|
|
1055
|
+
required=False,
|
|
1056
|
+
service_url=reverse_lazy("slm_public_api:antenna-list"),
|
|
1057
|
+
search_param="model",
|
|
1058
|
+
value_param="id",
|
|
1059
|
+
label_param="model",
|
|
1060
|
+
query_params={"in_use": True},
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
radome = ModelMultipleAutoComplete(
|
|
1064
|
+
queryset=Radome.objects.all(),
|
|
1065
|
+
# help_text=_('Enter the name or abbreviation of an Agency.'),
|
|
1066
|
+
label=_("Radome"),
|
|
1067
|
+
required=False,
|
|
1068
|
+
service_url=reverse_lazy("slm_public_api:radome-list"),
|
|
1069
|
+
search_param="model",
|
|
1070
|
+
value_param="id",
|
|
1071
|
+
label_param="model",
|
|
1072
|
+
query_params={"in_use": True},
|
|
1073
|
+
)
|
|
1074
|
+
|
|
1075
|
+
agency = ModelMultipleAutoComplete(
|
|
1076
|
+
queryset=Agency.objects.all(),
|
|
1077
|
+
# help_text=_('Enter the name or abbreviation of an Agency.'),
|
|
1078
|
+
label=_("Agency"),
|
|
1079
|
+
required=False,
|
|
1080
|
+
service_url=reverse_lazy("slm_edit_api:agency-list"),
|
|
1081
|
+
search_param="search",
|
|
1082
|
+
value_param="id",
|
|
1083
|
+
label_param="name",
|
|
1084
|
+
render_suggestion=(
|
|
1085
|
+
'return `<span class="matchable">(${obj.shortname})</span>'
|
|
1086
|
+
'<span class="matchable">${obj.name}</span>`;'
|
|
1087
|
+
),
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
network = ModelMultipleAutoComplete(
|
|
1091
|
+
queryset=Network.objects.all(),
|
|
1092
|
+
# help_text=_('Enter the name of an IGS Network.'),
|
|
1093
|
+
label=_("Network"),
|
|
1094
|
+
required=False,
|
|
1095
|
+
service_url=reverse_lazy("slm_edit_api:network-list"),
|
|
1096
|
+
search_param="name",
|
|
1097
|
+
value_param="id",
|
|
1098
|
+
label_param="name",
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
country = MultiSelectEnumAutoComplete(
|
|
1102
|
+
# help_text=_('Enter the name of a country or region.'),
|
|
1103
|
+
ISOCountry,
|
|
1104
|
+
label=_("Country/Region"),
|
|
1105
|
+
required=False,
|
|
1106
|
+
render_suggestion=(
|
|
1107
|
+
'return `<span class="fi fi-${obj.value.toLowerCase()}"></span>'
|
|
1108
|
+
'<span class="matchable">${obj.label}</span>`;'
|
|
1109
|
+
),
|
|
1110
|
+
data_source=ISOCountry.with_stations,
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
geography = PolylineListField(
|
|
1114
|
+
required=False,
|
|
1115
|
+
label=_("Geographic Bounds"),
|
|
1116
|
+
help_text=_(
|
|
1117
|
+
"Bounding boxes should be line strings holding (longitude, "
|
|
1118
|
+
"latitude) tuples encoded using Google's polyline algorithm."
|
|
1119
|
+
),
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
def clean_current(self):
|
|
1123
|
+
# todo mixin that does this
|
|
1124
|
+
if self["current"].html_name not in self.data:
|
|
1125
|
+
return self.fields["current"].initial
|
|
1126
|
+
return self.cleaned_data["current"]
|