igs-slm 0.1.4b0__py3-none-any.whl → 0.1.5b1__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.5b1.dist-info/METADATA +115 -0
- {igs_slm-0.1.4b0.dist-info → igs_slm-0.1.5b1.dist-info}/RECORD +193 -173
- {igs_slm-0.1.4b0.dist-info → igs_slm-0.1.5b1.dist-info}/WHEEL +1 -1
- igs_slm-0.1.5b1.dist-info/entry_points.txt +3 -0
- {igs_slm-0.1.4b0.dist-info → igs_slm-0.1.5b1.dist-info/licenses}/LICENSE +1 -1
- slm/__init__.py +17 -14
- slm/admin.py +32 -5
- slm/api/edit/views.py +22 -9
- slm/api/public/views.py +9 -7
- slm/api/views.py +45 -6
- slm/apps.py +28 -6
- slm/authentication.py +3 -2
- slm/bin/startproject.py +102 -31
- slm/bin/templates/{{ project_dir }}/pyproject.toml +30 -21
- slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/base.py +12 -1
- slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/__init__.py +5 -27
- slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/production/__init__.py +6 -27
- slm/bin/templates/{{ project_dir }}/src/sites/{{ site }}/urls.py +15 -0
- slm/bin/templates/{{ project_dir }}/src/sites/{{ site }}/validation.py +29 -0
- slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/urls.py +1 -0
- slm/context.py +5 -0
- slm/defines/AlertLevel.py +10 -2
- slm/defines/AntennaCalibration.py +6 -4
- slm/defines/AntennaFeatures.py +12 -9
- slm/defines/AntennaReferencePoint.py +12 -10
- slm/defines/Aspiration.py +4 -2
- slm/defines/CardinalDirection.py +6 -4
- slm/defines/CollocationStatus.py +3 -1
- slm/defines/EquipmentState.py +6 -12
- slm/defines/FlagSeverity.py +4 -2
- slm/defines/FractureSpacing.py +7 -5
- slm/defines/FrequencyStandardType.py +6 -4
- slm/defines/GeodesyMLVersion.py +2 -0
- slm/defines/Instrumentation.py +9 -7
- slm/defines/LogEntryType.py +17 -15
- slm/defines/SLMFileType.py +4 -2
- slm/defines/SiteFileUploadStatus.py +7 -24
- slm/defines/SiteLogFormat.py +8 -32
- slm/defines/SiteLogStatus.py +9 -28
- slm/defines/TectonicPlates.py +18 -16
- slm/manage.py +24 -0
- slm/management/commands/check_upgrade.py +142 -0
- slm/management/commands/generate_sinex.py +110 -92
- slm/management/commands/head_from_index.py +11 -8
- slm/management/commands/import_archive.py +27 -18
- slm/management/commands/import_equipment.py +1 -1
- slm/management/commands/sitelog.py +1 -3
- slm/management/commands/synchronize.py +1 -1
- slm/management/commands/validate_db.py +6 -4
- slm/map/defines.py +18 -14
- slm/map/templates/slm/map.html +5 -7
- slm/migrations/0001_remove_archiveindex_no_overlapping_ranges_per_site_and_more.py +26 -0
- slm/migrations/0002_alter_archivedsitelog_file_and_more.py +44 -0
- slm/migrations/0003_alter_archivedsitelog_name_and_more.py +35 -0
- slm/migrations/0004_alter_site_name.py +24 -0
- slm/migrations/0005_slmversion.py +30 -0
- slm/migrations/0017_alter_logentry_unique_together_and_more.py +3 -1
- slm/migrations/0018_afix_deleted.py +3 -1
- slm/migrations/0018_alter_siteantenna_options_and_more.py +87 -56
- slm/migrations/0019_remove_siteantenna_marker_enu_siteantenna_marker_une_and_more.py +1 -1
- slm/migrations/0023_archivedsitelog_gml_version_and_more.py +1 -1
- slm/migrations/0031_alter_antenna_features.py +44 -0
- slm/migrations/0032_archiveindex_valid_range_and_more.py +84 -0
- slm/migrations/add_index_order_index.py +54 -0
- slm/migrations/normalize_index.py +147 -0
- slm/migrations/simplify_index.py +48 -0
- slm/migrations/verify_index.py +67 -0
- slm/models/__init__.py +2 -0
- slm/models/alerts.py +7 -10
- slm/models/data.py +1 -2
- slm/models/equipment.py +1 -1
- slm/models/fields.py +41 -0
- slm/models/index.py +183 -53
- slm/models/sitelog.py +35 -38
- slm/models/system.py +72 -31
- slm/models/user.py +1 -1
- slm/parsing/__init__.py +33 -16
- slm/parsing/legacy/binding.py +65 -34
- slm/parsing/legacy/parser.py +2 -2
- slm/parsing/xsd/binding.py +1 -1
- slm/parsing/xsd/parser.py +1 -2
- slm/receivers/__init__.py +2 -2
- slm/receivers/index.py +2 -1
- slm/receivers/migration.py +21 -0
- slm/settings/__init__.py +192 -4
- slm/settings/assets.py +26 -0
- slm/settings/auth.py +18 -14
- slm/settings/ckeditor.py +12 -6
- slm/settings/debug.py +2 -2
- slm/settings/emails.py +50 -0
- slm/settings/internationalization.py +8 -6
- slm/settings/logging.py +100 -88
- slm/settings/platform/darwin.py +16 -6
- slm/settings/rest.py +20 -15
- slm/settings/root.py +192 -98
- slm/settings/routines.py +5 -1
- slm/settings/secrets.py +20 -31
- slm/settings/security.py +7 -5
- slm/settings/slm.py +35 -23
- slm/settings/static_templates.py +12 -9
- slm/settings/templates.py +31 -25
- slm/settings/uploads.py +33 -5
- slm/settings/urls.py +7 -12
- slm/settings/validation.py +165 -165
- slm/signals.py +3 -2
- slm/static/slm/css/style.css +37 -36
- slm/static/slm/js/autocomplete.js +6 -4
- slm/static/slm/js/enums.js +6 -5
- slm/static/slm/js/file_modal.js +62 -0
- slm/static/slm/js/form.js +3 -3
- slm/static/slm/js/formWidget.js +3 -3
- slm/static/slm/js/persistable.js +5 -1
- slm/templates/admin/base.html +1 -0
- slm/templates/rest_framework/base.html +23 -11
- slm/templates/slm/base.html +27 -22
- slm/templates/slm/forms/widgets/auto_complete.html +12 -11
- slm/templates/slm/forms/widgets/auto_complete_multiple.html +8 -7
- slm/templates/slm/station/download.html +6 -6
- slm/templates/slm/station/edit.html +9 -17
- slm/templates/slm/station/review.html +5 -3
- slm/templates/slm/station/upload.html +4 -1
- slm/templates/slm/station/uploads/legacy.html +1 -1
- slm/templates/slm/widgets/alert_scroll.html +4 -8
- slm/templates/slm/widgets/filelist.html +0 -5
- slm/templates/slm/widgets/log_scroll.html +2 -13
- slm/templates/slm/widgets/stationlist.html +2 -8
- slm/templatetags/slm.py +70 -9
- slm/utils.py +13 -4
- slm/validators.py +14 -14
- slm/views.py +6 -6
- slm/wsgi.py +16 -0
- igs_slm-0.1.4b0.dist-info/METADATA +0 -154
- igs_slm-0.1.4b0.dist-info/entry_points.txt +0 -3
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/urls.py +0 -7
- slm/bin/templates/{{ project_dir }}/sites/{{ site }}/validation.py +0 -11
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/local.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/wsgi.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/manage.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/production/wsgi.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/admin.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/apps.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/commands/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/commands/import_archive.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/migrations/__init__.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/models.py +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/templates/slm/base.html +0 -0
- /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/views.py +0 -0
|
@@ -5,11 +5,15 @@ Template for View/Diff page.
|
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
7
|
{% extends "slm/station/base.html" %}
|
|
8
|
-
{% load
|
|
8
|
+
{% load slm i18n static %}
|
|
9
9
|
|
|
10
10
|
{% block head %}
|
|
11
11
|
{{ block.super }}
|
|
12
12
|
|
|
13
|
+
{% comment %}there is a bug in diff-match-patch where nodejs snippet at the end throws an error - this code allows that to proceed silently{% endcomment %}
|
|
14
|
+
<script>
|
|
15
|
+
var module = {exports: {}}
|
|
16
|
+
</script>
|
|
13
17
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff-match-patch/1.0.5/index.min.js" integrity="sha512-s/r2YIRA8VD7KT0c9uJqKrZFrNFgKlOPeLyVXp7noa6+F8vw5LMvR+hxteawjCpp6+5A4nTYoWtwLcXEJW1YzA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
14
18
|
|
|
15
19
|
{% endblock head %}
|
|
@@ -92,7 +96,6 @@ Template for View/Diff page.
|
|
|
92
96
|
|
|
93
97
|
{{ review_stack|json_script:"slm-review-stack" }}
|
|
94
98
|
|
|
95
|
-
{% compress js inline %}
|
|
96
99
|
<script>
|
|
97
100
|
const reviewActions = $('#review-actions');
|
|
98
101
|
const submitBtn = reviewActions.find('button[name="submit"]');
|
|
@@ -457,5 +460,4 @@ Template for View/Diff page.
|
|
|
457
460
|
|
|
458
461
|
setDiff(1, 0);
|
|
459
462
|
</script>
|
|
460
|
-
{% endcompress %}
|
|
461
463
|
{% endblock %}
|
|
@@ -269,7 +269,10 @@ Template for Download page.
|
|
|
269
269
|
error: function(file, message, xhr) {
|
|
270
270
|
console.log(xhr);
|
|
271
271
|
completedFiles += 1;
|
|
272
|
-
if (
|
|
272
|
+
if (xhr.status === 500) {
|
|
273
|
+
document.write(`<pre>${message}</pre>`);
|
|
274
|
+
}
|
|
275
|
+
if (numFiles === 1 && message.file) {
|
|
273
276
|
window.location = slm.urls.reverse(
|
|
274
277
|
'slm:upload', {
|
|
275
278
|
kwargs: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% load slm compress i18n %}
|
|
2
2
|
|
|
3
3
|
<pre id="slm-parsed-legacy">{% for line in file|file_lines %}{% with idx=forloop.counter0 %}
|
|
4
|
-
<span data-line-number={{ idx }} class="{{ findings|finding_class:idx }}" {% if findings|finding_content:idx %}data-bs-toggle="popover" title="{{ findings|finding_title:idx }}" data-bs-custom-class="{{ findings|finding_class:idx }}" data-bs-html="true" data-bs-content="<pre>{{ findings|finding_content:idx }}</pre>"{% endif %}>{{ line }}</span>{% endwith %}{% endfor %}
|
|
4
|
+
<span data-line-number={{ idx }}>{{ findings|clear_prefix:idx|get_part:line }}<span class="{{ findings|finding_class:idx }}" {% if findings|finding_content:idx %}data-bs-toggle="popover" title="{{ findings|finding_title:idx }}" data-bs-custom-class="{{ findings|finding_class:idx }}" data-bs-html="true" data-bs-content="<pre>{{ findings|finding_content:idx }}</pre>"{% endif %}>{{ findings|marked_part:idx|get_part:line }}</span>{{ findings|clear_postfix:idx|get_part:line }}</span>{% endwith %}{% endfor %}
|
|
5
5
|
</pre>
|
|
6
6
|
|
|
7
7
|
{% compress css inline %}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{% load
|
|
1
|
+
{% load i18n %}
|
|
2
2
|
|
|
3
3
|
<div class="slm-scroll-container">
|
|
4
4
|
<div
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
{% compress js inline %}
|
|
17
16
|
<script>
|
|
18
17
|
const deleteAlert = function(alertId) {
|
|
19
18
|
this.event.stopPropagation();
|
|
@@ -125,11 +124,8 @@
|
|
|
125
124
|
/* this is a little clunky - avoid double init when station
|
|
126
125
|
* filter is present on the page
|
|
127
126
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
} else {
|
|
131
|
-
slm.stationFilterCallbacks.push(stationsFiltered);
|
|
132
|
-
}
|
|
127
|
+
init();
|
|
128
|
+
slm.stationFilterCallbacks.push(stationsFiltered);
|
|
133
129
|
});
|
|
134
130
|
</script>
|
|
135
|
-
|
|
131
|
+
|
|
@@ -118,10 +118,6 @@ Template for stations list.
|
|
|
118
118
|
</div>
|
|
119
119
|
</div>
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
{% load compress %}
|
|
123
|
-
|
|
124
|
-
{% compress js inline %}
|
|
125
121
|
<script>
|
|
126
122
|
const fileFilter = $('#slm-file-filter');
|
|
127
123
|
const filterFiles = $('#filter-files');
|
|
@@ -255,4 +251,3 @@ Template for stations list.
|
|
|
255
251
|
});
|
|
256
252
|
|
|
257
253
|
</script>
|
|
258
|
-
{% endcompress %}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
{% load compress %}
|
|
2
|
-
|
|
3
1
|
<div class="slm-scroll-container">
|
|
4
2
|
<div
|
|
5
3
|
id='slm-log-scroll'
|
|
@@ -13,14 +11,12 @@
|
|
|
13
11
|
</div>
|
|
14
12
|
</div>
|
|
15
13
|
|
|
16
|
-
{% compress js inline %}
|
|
17
14
|
<script>
|
|
18
15
|
$(document).ready(function() {
|
|
19
16
|
let reinitialize = true;
|
|
20
17
|
const scrollDiv = $("#slm-log-scroll");
|
|
21
18
|
const drawLogs = function(position, data) {
|
|
22
19
|
if (reinitialize) {
|
|
23
|
-
console.log(reinitialize);
|
|
24
20
|
scrollDiv.find('.slm-log-item').remove();
|
|
25
21
|
scrollDiv.find('h3').remove();
|
|
26
22
|
}
|
|
@@ -75,14 +71,7 @@
|
|
|
75
71
|
scrollDiv.data('slmQuery', queryParams);
|
|
76
72
|
init();
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*/
|
|
81
|
-
if ($('#filter-stations').length === 0) {
|
|
82
|
-
init();
|
|
83
|
-
} else {
|
|
84
|
-
slm.stationFilterCallbacks.push(stationsFiltered);
|
|
85
|
-
}
|
|
74
|
+
init();
|
|
75
|
+
slm.stationFilterCallbacks.push(stationsFiltered);
|
|
86
76
|
});
|
|
87
77
|
</script>
|
|
88
|
-
{% endcompress %}
|
|
@@ -71,14 +71,9 @@
|
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
<script>
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<script type="module">
|
|
79
|
-
|
|
80
|
-
import { Form } from '{% static "slm/js/form.js" %}';
|
|
81
|
-
const filterForm = new Form($('div#filter form'));
|
|
76
|
+
const filterForm = new slm.Form($('div#filter form'));
|
|
82
77
|
|
|
83
78
|
let keyupTimeoutID = 0;
|
|
84
79
|
const stationFilter = $('#slm-station-filter');
|
|
@@ -230,4 +225,3 @@
|
|
|
230
225
|
});
|
|
231
226
|
|
|
232
227
|
</script>
|
|
233
|
-
{% endcompress %}
|
slm/templatetags/slm.py
CHANGED
|
@@ -127,7 +127,7 @@ def multi_line(text):
|
|
|
127
127
|
limited.append(unescape(line[0:mark]))
|
|
128
128
|
line = line[mark:]
|
|
129
129
|
limited.append(unescape(line))
|
|
130
|
-
return f
|
|
130
|
+
return f"\n{' ' * 30}: ".join([line for line in limited if line.strip()])
|
|
131
131
|
return ""
|
|
132
132
|
|
|
133
133
|
|
|
@@ -138,7 +138,7 @@ def iso6709(decimal_degrees, padding):
|
|
|
138
138
|
number = f"{dddmmssss:.2f}"
|
|
139
139
|
integer, dec = number.split(".") if "." in number else (number, None)
|
|
140
140
|
iso_frmt = f"{abs(int(integer)):0{int(padding)}}{'.' if dec else ''}{dec}"
|
|
141
|
-
return f'
|
|
141
|
+
return f"{'+' if float(dddmmssss) > 0 else '-'}{iso_frmt}"
|
|
142
142
|
return ""
|
|
143
143
|
|
|
144
144
|
|
|
@@ -225,7 +225,7 @@ def antenna_radome(antenna):
|
|
|
225
225
|
radome = "NONE"
|
|
226
226
|
if hasattr(antenna, "radome_type"):
|
|
227
227
|
radome = antenna.radome_type.model
|
|
228
|
-
return f
|
|
228
|
+
return f"{antenna.antenna_type.model}{' ' * spacing}{radome}"
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
@register.filter(name="antenna_codelist")
|
|
@@ -238,7 +238,7 @@ def antenna_codelist(antenna):
|
|
|
238
238
|
|
|
239
239
|
@register.filter(name="rpad_space")
|
|
240
240
|
def rpad_space(text, length):
|
|
241
|
-
return f
|
|
241
|
+
return f"{text}{' ' * (int(length) - len(str(text)))}"
|
|
242
242
|
|
|
243
243
|
|
|
244
244
|
@register.filter(name="file_icon")
|
|
@@ -261,10 +261,7 @@ def file_lines(file):
|
|
|
261
261
|
return content.decode(detect(content).get("encoding", "utf-8")).split("\n")
|
|
262
262
|
except (UnicodeDecodeError, LookupError, ValueError):
|
|
263
263
|
return [
|
|
264
|
-
_(
|
|
265
|
-
"** Unable to determine text encoding - please upload as "
|
|
266
|
-
"UTF-8. **"
|
|
267
|
-
)
|
|
264
|
+
_("** Unable to determine text encoding - please upload as UTF-8. **")
|
|
268
265
|
]
|
|
269
266
|
return [""]
|
|
270
267
|
|
|
@@ -300,6 +297,46 @@ def finding_title(findings, line_number):
|
|
|
300
297
|
return ""
|
|
301
298
|
|
|
302
299
|
|
|
300
|
+
# these filters support marking slices of lines with a finding given a column range
|
|
301
|
+
# its messy - could use a refactor and also currently doesnt support more than one marking per line
|
|
302
|
+
# if you do refactor be sure to support old findings contexts that are cached in the DB for previous
|
|
303
|
+
# uploads!
|
|
304
|
+
@register.filter(name="get_part")
|
|
305
|
+
def get_part(slice, line):
|
|
306
|
+
return line[slice[0] : slice[1]]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@register.filter(name="clear_prefix")
|
|
310
|
+
def clear_prefix(findings, line_number):
|
|
311
|
+
if findings:
|
|
312
|
+
ret = findings.get(
|
|
313
|
+
str(line_number), findings.get(int(line_number), [None, "", None])
|
|
314
|
+
)
|
|
315
|
+
if len(ret) >= 3 and ret[2]:
|
|
316
|
+
return None, ret[2][0]
|
|
317
|
+
return 0, 0
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@register.filter(name="marked_part")
|
|
321
|
+
def marked_part(findings, line_number):
|
|
322
|
+
if findings:
|
|
323
|
+
ret = findings.get(
|
|
324
|
+
str(line_number), findings.get(int(line_number), [None, "", None])
|
|
325
|
+
)
|
|
326
|
+
if len(ret) >= 3 and ret[2]:
|
|
327
|
+
return ret[2][0], ret[2][1]
|
|
328
|
+
return None, None
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@register.filter(name="clear_postfix")
|
|
332
|
+
def clear_postfix(findings, line_number):
|
|
333
|
+
if findings:
|
|
334
|
+
ret = findings.get(str(line_number), findings.get(int(line_number), [None, ""]))
|
|
335
|
+
if len(ret) >= 3 and ret[2]:
|
|
336
|
+
return ret[2][1], None
|
|
337
|
+
return 0, 0
|
|
338
|
+
|
|
339
|
+
|
|
303
340
|
@register.filter(name="split_rows")
|
|
304
341
|
def split_rows(iterable, row_length):
|
|
305
342
|
rows = []
|
|
@@ -337,7 +374,7 @@ def absolute_url(path, request=None):
|
|
|
337
374
|
def file_url(path, request=None):
|
|
338
375
|
file_domain = getattr(settings, "SLM_FILE_DOMAIN", None)
|
|
339
376
|
if file_domain:
|
|
340
|
-
return f
|
|
377
|
+
return f"{file_domain.rstrip('/')}/{path.lstrip('/')}"
|
|
341
378
|
return absolute_url(path, request=request)
|
|
342
379
|
|
|
343
380
|
|
|
@@ -457,3 +494,27 @@ def set_global(context, name, val):
|
|
|
457
494
|
@register.simple_tag(takes_context=True)
|
|
458
495
|
def get_global(context, name):
|
|
459
496
|
return context.render_context.get(name, None)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@register.filter(name="chop_time")
|
|
500
|
+
def chop_time(filename):
|
|
501
|
+
"""
|
|
502
|
+
Chop the time ext off of a sitelog filename.
|
|
503
|
+
|
|
504
|
+
e.g. SITE_YYYYMMDD_HHMMSS.log -> SITE_YYYYMMDD_HHMMSS.log
|
|
505
|
+
"""
|
|
506
|
+
if filename.count("_") == 2:
|
|
507
|
+
return f"{filename[: filename.rindex('_')]}{filename[filename.rindex('.') :]}"
|
|
508
|
+
return filename
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@register.filter(name="chop_datetime")
|
|
512
|
+
def chop_datetime(filename):
|
|
513
|
+
"""
|
|
514
|
+
Chop the date and time ext off of a sitelog filename.
|
|
515
|
+
|
|
516
|
+
e.g. SITE_YYYYMMDD_HHMMSS.log -> SITE.log
|
|
517
|
+
"""
|
|
518
|
+
if "_" in filename:
|
|
519
|
+
return f"{filename[: filename.index('_')]}{filename[filename.rindex('.') :]}"
|
|
520
|
+
return filename
|
slm/utils.py
CHANGED
|
@@ -3,7 +3,6 @@ import re
|
|
|
3
3
|
from datetime import date, datetime, timedelta
|
|
4
4
|
from math import atan2, cos, sin, sqrt
|
|
5
5
|
|
|
6
|
-
import numpy as np
|
|
7
6
|
from dateutil import parser as date_parser
|
|
8
7
|
from django.conf import settings
|
|
9
8
|
from django.contrib.gis.geos import Point
|
|
@@ -30,7 +29,7 @@ def get_record_model():
|
|
|
30
29
|
_site_record = apps.get_app_config(app_label).get(model_class.lower(), None)
|
|
31
30
|
if not _site_record:
|
|
32
31
|
raise ImproperlyConfigured(
|
|
33
|
-
f'SLM_SITE_RECORD "{slm_site_record}" is not a registered '
|
|
32
|
+
f'SLM_SITE_RECORD "{slm_site_record}" is not a registered model'
|
|
34
33
|
)
|
|
35
34
|
return _site_record
|
|
36
35
|
except ValueError as ve:
|
|
@@ -97,7 +96,7 @@ def build_absolute_url(path, request=None):
|
|
|
97
96
|
return path
|
|
98
97
|
if request:
|
|
99
98
|
return request.build_absolute_uri(path)
|
|
100
|
-
return f
|
|
99
|
+
return f"{get_url()}/{path.lstrip('/')}"
|
|
101
100
|
|
|
102
101
|
|
|
103
102
|
def get_url():
|
|
@@ -255,7 +254,7 @@ def xyz2llh(xyz):
|
|
|
255
254
|
f_e = 1 / 298.25642 # IERS2000 standards
|
|
256
255
|
radians2degree = 45 / atan2(1, 1)
|
|
257
256
|
|
|
258
|
-
xyz_array =
|
|
257
|
+
xyz_array = [v / a_e for v in xyz]
|
|
259
258
|
(x, y, z) = (xyz_array[0], xyz_array[1], xyz_array[2])
|
|
260
259
|
e2 = f_e * (2 - f_e)
|
|
261
260
|
z2 = z**2
|
|
@@ -297,3 +296,13 @@ def convert_4to9(text: str, name: str) -> str:
|
|
|
297
296
|
return name
|
|
298
297
|
|
|
299
298
|
return re.compile(re.escape(name[0:4]), re.IGNORECASE).sub(match_case, text)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def transliterate(unicode_str: str) -> str:
|
|
302
|
+
"""
|
|
303
|
+
Transliterate a string that potentially contains multi-byte ASCII characters to its closest
|
|
304
|
+
ASCII equivalent if one exists.
|
|
305
|
+
"""
|
|
306
|
+
import unicodedata
|
|
307
|
+
|
|
308
|
+
return unicodedata.normalize("NFKD", unicode_str).encode("ascii", "ignore").decode()
|
slm/validators.py
CHANGED
|
@@ -4,7 +4,7 @@ from datetime import datetime, timezone
|
|
|
4
4
|
from django.core.exceptions import ValidationError
|
|
5
5
|
from django.core.validators import RegexValidator
|
|
6
6
|
from django.db.models import Model
|
|
7
|
-
from django.utils.translation import
|
|
7
|
+
from django.utils.translation import gettext_lazy as _
|
|
8
8
|
|
|
9
9
|
from slm.defines import EquipmentState, FlagSeverity
|
|
10
10
|
|
|
@@ -19,16 +19,16 @@ Flag = namedtuple("Flag", "message manual severity")
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def get_validators(model, field):
|
|
22
|
-
from django.conf import settings
|
|
23
|
-
|
|
24
22
|
"""
|
|
25
|
-
Get the validator list for a given model and field from validation
|
|
23
|
+
Get the validator list for a given model and field from validation
|
|
26
24
|
settings.
|
|
27
|
-
|
|
25
|
+
|
|
28
26
|
:param model: The Django model name <app_name>.<ModelClass>
|
|
29
|
-
:param field: The field name
|
|
27
|
+
:param field: The field name
|
|
30
28
|
:return:
|
|
31
29
|
"""
|
|
30
|
+
from django.conf import settings
|
|
31
|
+
|
|
32
32
|
if isinstance(model, Model) or (
|
|
33
33
|
isinstance(model, type) and issubclass(model, Model)
|
|
34
34
|
):
|
|
@@ -177,7 +177,7 @@ class FourIDValidator(SLMValidator):
|
|
|
177
177
|
if not instance.site.name.startswith(value):
|
|
178
178
|
self.throw_error(
|
|
179
179
|
f"{field.verbose_name} "
|
|
180
|
-
f
|
|
180
|
+
f"{_('must be the prefix of the 9 character site name')}.",
|
|
181
181
|
instance,
|
|
182
182
|
field,
|
|
183
183
|
)
|
|
@@ -190,9 +190,9 @@ class ARPValidator(SLMValidator):
|
|
|
190
190
|
)
|
|
191
191
|
if at_arp != value:
|
|
192
192
|
self.throw_error(
|
|
193
|
-
f
|
|
194
|
-
f
|
|
195
|
-
f
|
|
193
|
+
f"{getattr(value, 'name', None)} "
|
|
194
|
+
f"{_('must does not match the antenna reference point: ')}"
|
|
195
|
+
f"{getattr(at_arp, 'name', None)}.",
|
|
196
196
|
instance,
|
|
197
197
|
field,
|
|
198
198
|
)
|
|
@@ -265,7 +265,7 @@ class TimeRangeValidator(SLMValidator):
|
|
|
265
265
|
if start > value:
|
|
266
266
|
self.throw_error(
|
|
267
267
|
f"{field.verbose_name} "
|
|
268
|
-
f
|
|
268
|
+
f"{_('must be greater than')} "
|
|
269
269
|
f"{instance._meta.get_field(self.start_field).verbose_name}",
|
|
270
270
|
instance,
|
|
271
271
|
field,
|
|
@@ -280,9 +280,9 @@ class TimeRangeValidator(SLMValidator):
|
|
|
280
280
|
and end != NULL_TIME
|
|
281
281
|
):
|
|
282
282
|
self.throw_error(
|
|
283
|
-
f
|
|
283
|
+
f"{_('Cannot define')} "
|
|
284
284
|
f"{instance._meta.get_field(self.end_field).verbose_name} "
|
|
285
|
-
f
|
|
285
|
+
f"{_('without defining')} "
|
|
286
286
|
f"{field.verbose_name}.",
|
|
287
287
|
instance,
|
|
288
288
|
field,
|
|
@@ -290,7 +290,7 @@ class TimeRangeValidator(SLMValidator):
|
|
|
290
290
|
elif end < value:
|
|
291
291
|
self.throw_error(
|
|
292
292
|
f"{field.verbose_name} "
|
|
293
|
-
f
|
|
293
|
+
f"{_('must be less than')} "
|
|
294
294
|
f"{instance._meta.get_field(self.end_field).verbose_name}",
|
|
295
295
|
instance,
|
|
296
296
|
field,
|
slm/views.py
CHANGED
|
@@ -200,7 +200,7 @@ class StationContextView(SLMView):
|
|
|
200
200
|
):
|
|
201
201
|
raise PermissionDenied()
|
|
202
202
|
except Site.DoesNotExist:
|
|
203
|
-
raise Http404(f
|
|
203
|
+
raise Http404(f"Site {context['station']} was not found!")
|
|
204
204
|
return context
|
|
205
205
|
|
|
206
206
|
|
|
@@ -295,7 +295,7 @@ class EditView(StationContextView):
|
|
|
295
295
|
"""
|
|
296
296
|
context = super().get_context_data(**kwargs)
|
|
297
297
|
if not self.site:
|
|
298
|
-
raise Http404(f
|
|
298
|
+
raise Http404(f"Station {kwargs.get('station', '')} does not exist!")
|
|
299
299
|
context.update(
|
|
300
300
|
{
|
|
301
301
|
"section_id": kwargs.get("section", None),
|
|
@@ -579,11 +579,11 @@ class StationReviewView(StationContextView):
|
|
|
579
579
|
fmt_q |= Q(log_format=SiteLogFormat.LEGACY)
|
|
580
580
|
for archive in (
|
|
581
581
|
ArchivedSiteLog.objects.filter(Q(site=self.site) & fmt_q)
|
|
582
|
-
.order_by("-
|
|
583
|
-
.values_list("
|
|
584
|
-
.distinct("
|
|
582
|
+
.order_by("-index__valid_range", "-log_format")
|
|
583
|
+
.values_list("index__valid_range", "id")
|
|
584
|
+
.distinct("index__valid_range")
|
|
585
585
|
):
|
|
586
|
-
review_stack[fmt].append((archive[0], archive[1]))
|
|
586
|
+
review_stack[fmt].append((archive[0].lower, archive[1]))
|
|
587
587
|
ctx = {
|
|
588
588
|
**ctx,
|
|
589
589
|
"richtextform": RichTextForm(),
|
slm/wsgi.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WSGI config for SLM production deployment.
|
|
3
|
+
|
|
4
|
+
It exposes the WSGI callable as a module-level variable named ``application``.
|
|
5
|
+
|
|
6
|
+
For more information on this file, see
|
|
7
|
+
https://docs.djangoproject.com/en/stable/howto/deployment/wsgi/
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from django.core.wsgi import get_wsgi_application
|
|
13
|
+
|
|
14
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "slm.settings.root")
|
|
15
|
+
|
|
16
|
+
application = get_wsgi_application()
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: igs-slm
|
|
3
|
-
Version: 0.1.4b0
|
|
4
|
-
Summary: IGS Site Log Manager
|
|
5
|
-
Home-page: https://igs-slm.readthedocs.io
|
|
6
|
-
License: MIT
|
|
7
|
-
Keywords: SLM,Site Log Manager,IGS,International GNSS Service,GNSS,GPS,GLONASS,Galileo,BeiDou,QZSS
|
|
8
|
-
Author: Ashley Santiago
|
|
9
|
-
Author-email: ashley.k.santiago@jpl.nasa.gov
|
|
10
|
-
Requires-Python: >=3.8,<4.0
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Web Environment
|
|
13
|
-
Classifier: Framework :: Django
|
|
14
|
-
Classifier: Framework :: Django :: 4.2
|
|
15
|
-
Classifier: Framework :: Django :: 5.0
|
|
16
|
-
Classifier: Intended Audience :: Developers
|
|
17
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
-
Classifier: Natural Language :: English
|
|
19
|
-
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Programming Language :: Python
|
|
21
|
-
Classifier: Programming Language :: Python :: 3
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
28
|
-
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
29
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
30
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
|
-
Requires-Dist: Django (>=4.2.0,<6.0.0)
|
|
32
|
-
Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
|
|
33
|
-
Requires-Dist: Pillow (>=10.0.0,<11.0.0)
|
|
34
|
-
Requires-Dist: Sphinx (>=7.0.0,<8.0.0) ; python_version <= "3.8"
|
|
35
|
-
Requires-Dist: Sphinx (>=7.2.0,<8.0.0) ; python_version > "3.8"
|
|
36
|
-
Requires-Dist: backports.tarfile (>=1.2.0,<2.0.0) ; python_version <= "3.8"
|
|
37
|
-
Requires-Dist: chardet (>=5.1.0,<6.0.0)
|
|
38
|
-
Requires-Dist: crispy-bootstrap5 (>=2024.2,<2025.0)
|
|
39
|
-
Requires-Dist: django-allauth (>=0.63.3,<0.64.0)
|
|
40
|
-
Requires-Dist: django-ckeditor (>=6.5.1,<7.0.0)
|
|
41
|
-
Requires-Dist: django-compressor (>=4.0,<5.0)
|
|
42
|
-
Requires-Dist: django-crispy-forms (>=2.0,<3.0)
|
|
43
|
-
Requires-Dist: django-enum (>=1.2.2,<2.0.0)
|
|
44
|
-
Requires-Dist: django-filter (>=24.2,<25.0)
|
|
45
|
-
Requires-Dist: django-ipware (>=7.0.1,<8.0.0)
|
|
46
|
-
Requires-Dist: django-polymorphic (>=3.1.0,<4.0.0)
|
|
47
|
-
Requires-Dist: django-render-static (>=3.1.2,<4.0.0)
|
|
48
|
-
Requires-Dist: django-routines (>=1.1.3,<2.0.0)
|
|
49
|
-
Requires-Dist: django-split-settings (>=1.2.0,<2.0.0)
|
|
50
|
-
Requires-Dist: django-typer (>=2.1.2,<3.0.0)
|
|
51
|
-
Requires-Dist: django-widget-tweaks (>=1.4.12,<2.0.0)
|
|
52
|
-
Requires-Dist: djangorestframework (>=3.15.2,<4.0.0)
|
|
53
|
-
Requires-Dist: djangorestframework-gis (>=1.0,<2.0)
|
|
54
|
-
Requires-Dist: enum-properties (>=1.7.0,<2.0.0)
|
|
55
|
-
Requires-Dist: geojson (>=3.1.0,<4.0.0)
|
|
56
|
-
Requires-Dist: importlib-resources (>1.3.0)
|
|
57
|
-
Requires-Dist: lxml (>=5.2.1,<6.0.0)
|
|
58
|
-
Requires-Dist: numpy (<=1.24) ; python_version <= "3.8"
|
|
59
|
-
Requires-Dist: numpy (>=1.26) ; python_version > "3.8"
|
|
60
|
-
Requires-Dist: polyline (>=2.0.0,<3.0.0)
|
|
61
|
-
Requires-Dist: psycopg[binary] (==3.1.18)
|
|
62
|
-
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
63
|
-
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
64
|
-
Requires-Dist: rich (>=13.7.1,<14.0.0)
|
|
65
|
-
Requires-Dist: tqdm (>=4.64.1,<5.0.0)
|
|
66
|
-
Project-URL: Repository, https://github.com/International-GNSS-Service/SLM
|
|
67
|
-
Description-Content-Type: text/markdown
|
|
68
|
-
|
|
69
|
-
[](https://opensource.org/licenses/MIT)
|
|
70
|
-
[](https://pypi.python.org/pypi/igs-slm/)
|
|
71
|
-
[](https://pypi.python.org/pypi/igs-slm/)
|
|
72
|
-
[](https://pypi.org/project/igs-slm/)
|
|
73
|
-
[](https://pypi.python.org/pypi/igs-slm)
|
|
74
|
-
[](http://igs-slm.readthedocs.io/?badge=latest/)
|
|
75
|
-
[](https://codecov.io/github/International-GNSS-Service/SLM)
|
|
76
|
-
[](https://github.com/International-GNSS-Service/SLM/actions/workflows/test.yml?branch=master)
|
|
77
|
-
[](https://github.com/International-GNSS-Service/SLM/actions/workflows/lint.yml)
|
|
78
|
-
[](https://github.com/International-GNSS-Service/SLM/actions/workflows/security.yml)
|
|
79
|
-
[](https://github.com/astral-sh/ruff)
|
|
80
|
-
|
|
81
|
-
# 
|
|
82
|
-
Site Log Manager (SLM)
|
|
83
|
-
|
|
84
|
-
The Site Log Manager (SLM) is a web platform that aims to provide:
|
|
85
|
-
|
|
86
|
-
1. GNSS Site meta data (site log) management with a moderation workflow.
|
|
87
|
-
2. Support for multiple organizations and networks to be managed in an access controlled way.
|
|
88
|
-
3. Full legacy site log format support (both import and export).
|
|
89
|
-
4. Full GeodesyML support (both import and export).
|
|
90
|
-
5. JSON renderings of meta data.
|
|
91
|
-
6. Point-and-click graphical editing of site log data.
|
|
92
|
-
7. Public RESTful api for searching site log data.
|
|
93
|
-
8. Authenticated RESTful api for updating site log data.
|
|
94
|
-
9. Full access to the historical record.
|
|
95
|
-
10. Visualizations of networks and site information.
|
|
96
|
-
11. Configurable data validation that goes above and beyond schema validation.
|
|
97
|
-
12. Image and file attachments to sites.
|
|
98
|
-
13. A no-fork extensible architecture that allows organizations to modify out-of-the box
|
|
99
|
-
behavior with plugins.
|
|
100
|
-
|
|
101
|
-
This code base has reached beta-maturity but is still undergoing rapid development. Check back soon
|
|
102
|
-
for new documentation and updates.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
## Table of Contents
|
|
106
|
-
1. [Design](#Design)
|
|
107
|
-
1. [Stack](#Stack)
|
|
108
|
-
2. [Organization](#Organization)
|
|
109
|
-
|
|
110
|
-
## Design
|
|
111
|
-
|
|
112
|
-
SLM is built in Python using the [Django website development framework.](https://www.djangoproject.com/)
|
|
113
|
-
Django is well documented. A basic understanding of how it works is helpful to understand how SLM is
|
|
114
|
-
put together. In addition to the [good intro tutorials](https://docs.djangoproject.com/en/stable/intro/tutorial01/), it's
|
|
115
|
-
helpful to understand [how reusable Django apps work](https://docs.djangoproject.com/en/stable/intro/reusable-apps/), how
|
|
116
|
-
[settings files work](https://docs.djangoproject.com/en/stable/topics/settings/) and how
|
|
117
|
-
[management commands work.](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/)
|
|
118
|
-
|
|
119
|
-
### Stack
|
|
120
|
-
|
|
121
|
-
Django can be served behind many http servers. A common production environment uses [Apache](https://httpd.apache.org/)
|
|
122
|
-
managing Django as a [WSGI](https://modwsgi.readthedocs.io/en/develop/index.html) daemon, but
|
|
123
|
-
another common setup involves proxying a [gunicorn](https://gunicorn.org/) instance behind [nginx](https://www.nginx.com).
|
|
124
|
-
In addition to Django, other critical components of the software stack are listed in the table below. Not all Python
|
|
125
|
-
dependencies are listed because many are incidental.
|
|
126
|
-
|
|
127
|
-
| Dependency | Description |
|
|
128
|
-
| ------------------------------------------------------------------------------ | ---------------------------------------------------- |
|
|
129
|
-
| [PostgreSQL](https://www.postgresql.org/) | Relation database management system |
|
|
130
|
-
| [Django](https://djangoproject.com) | Website development framework |
|
|
131
|
-
| [jQuery](https://jquery.com/) | Javascript DOM navigation library |
|
|
132
|
-
| [DataTables](https://datatables.net/) | Javascript tables library |
|
|
133
|
-
| [Bootstrap](https://getbootstrap.com/) | CSS framework |
|
|
134
|
-
| [djangorestframework](https://www.django-rest-framework.org/) | RESTful API framework for Django |
|
|
135
|
-
| [django-split-settings](https://github.com/sobolevn/django-split-settings) | Composite settings files for Django |
|
|
136
|
-
| [django_compressor](https://django-compressor.readthedocs.io/en/stable/) | Static file compression and management |
|
|
137
|
-
| [memcached](https://memcached.org/) | Memory object caching system |
|
|
138
|
-
| [django-render-static](https://django-render-static.readthedocs.io/en/latest/) | Static file rendering, javascript urls |
|
|
139
|
-
| [django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/latest/) | Debugging components for Django sites (test only) |
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
### Organization
|
|
143
|
-
|
|
144
|
-
#### Environment & Setup
|
|
145
|
-
|
|
146
|
-
1. [pyenv](https://github.com/pyenv/pyenv) is not strictly required, but it is highly recommended to help manage multiple
|
|
147
|
-
local Python installations and keep environments clean. Python 3.8+ is required.
|
|
148
|
-
2. [Poetry](https://Python-poetry.org/) is used for dependency and package management.
|
|
149
|
-
3. SLM requires PostgresSQL along with the PostGIS extension that enables geographic queries to be run directly by the database.
|
|
150
|
-
|
|
151
|
-
| RDBMS | Minimum Version | Management Utilities |
|
|
152
|
-
| ---------------------------------------------| ----------------- | ------------------------------------------------------------|
|
|
153
|
-
| [PostgreSQL](https://www.postgresql.org/) | 12 | [PgAdmin](https://www.pgadmin.org/) |
|
|
154
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{% if not use_igs_validation %}
|
|
2
|
-
# You selected to not use the IGS sitelog field validators by default, you may
|
|
3
|
-
# define your own validation configuration here. See slm.settings.validation
|
|
4
|
-
# module (https://github.com/International-GNSS-Service/SLM/blob/master/slm/settings/validation.py)
|
|
5
|
-
# for what the default settings are and documentation here (TODO) for the SLM's pluggable
|
|
6
|
-
# validation system.
|
|
7
|
-
SLM_REQUIRED_SECTIONS_TO_PUBLISH = []
|
|
8
|
-
SLM_DATA_VALIDATORS = {}
|
|
9
|
-
|
|
10
|
-
# You may delete this file to always use IGS's default validation routines
|
|
11
|
-
{% endif %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|