igs-slm 0.1.2b0__py3-none-any.whl → 0.1.5b0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. igs_slm-0.1.5b0.dist-info/METADATA +115 -0
  2. {igs_slm-0.1.2b0.dist-info → igs_slm-0.1.5b0.dist-info}/RECORD +192 -172
  3. {igs_slm-0.1.2b0.dist-info → igs_slm-0.1.5b0.dist-info}/WHEEL +1 -1
  4. igs_slm-0.1.5b0.dist-info/entry_points.txt +3 -0
  5. {igs_slm-0.1.2b0.dist-info → igs_slm-0.1.5b0.dist-info/licenses}/LICENSE +1 -1
  6. slm/__init__.py +17 -14
  7. slm/admin.py +32 -5
  8. slm/api/edit/views.py +22 -9
  9. slm/api/public/views.py +10 -8
  10. slm/api/views.py +45 -6
  11. slm/apps.py +28 -6
  12. slm/authentication.py +3 -2
  13. slm/bin/startproject.py +102 -31
  14. slm/bin/templates/{{ project_dir }}/pyproject.toml +30 -21
  15. slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/base.py +12 -1
  16. slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/__init__.py +5 -27
  17. slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/production/__init__.py +6 -27
  18. slm/bin/templates/{{ project_dir }}/src/sites/{{ site }}/validation.py +29 -0
  19. slm/context.py +5 -0
  20. slm/defines/AlertLevel.py +10 -2
  21. slm/defines/AntennaCalibration.py +6 -4
  22. slm/defines/AntennaFeatures.py +12 -9
  23. slm/defines/AntennaReferencePoint.py +12 -10
  24. slm/defines/Aspiration.py +4 -2
  25. slm/defines/CardinalDirection.py +6 -4
  26. slm/defines/CollocationStatus.py +3 -1
  27. slm/defines/EquipmentState.py +6 -12
  28. slm/defines/FlagSeverity.py +4 -2
  29. slm/defines/FractureSpacing.py +7 -5
  30. slm/defines/FrequencyStandardType.py +6 -4
  31. slm/defines/GeodesyMLVersion.py +2 -0
  32. slm/defines/Instrumentation.py +9 -7
  33. slm/defines/LogEntryType.py +17 -15
  34. slm/defines/SLMFileType.py +4 -2
  35. slm/defines/SiteFileUploadStatus.py +7 -24
  36. slm/defines/SiteLogFormat.py +8 -32
  37. slm/defines/SiteLogStatus.py +9 -28
  38. slm/defines/TectonicPlates.py +18 -16
  39. slm/manage.py +24 -0
  40. slm/management/commands/check_upgrade.py +142 -0
  41. slm/management/commands/generate_sinex.py +110 -92
  42. slm/management/commands/head_from_index.py +11 -8
  43. slm/management/commands/import_archive.py +27 -18
  44. slm/management/commands/import_equipment.py +1 -1
  45. slm/management/commands/sitelog.py +1 -3
  46. slm/management/commands/synchronize.py +1 -1
  47. slm/management/commands/validate_db.py +6 -4
  48. slm/map/defines.py +18 -14
  49. slm/map/templates/slm/map.html +4 -6
  50. slm/migrations/0001_remove_archiveindex_no_overlapping_ranges_per_site_and_more.py +26 -0
  51. slm/migrations/0002_alter_archivedsitelog_file_and_more.py +44 -0
  52. slm/migrations/0003_alter_archivedsitelog_name_and_more.py +35 -0
  53. slm/migrations/0004_alter_site_name.py +24 -0
  54. slm/migrations/0005_slmversion.py +30 -0
  55. slm/migrations/0017_alter_logentry_unique_together_and_more.py +3 -1
  56. slm/migrations/0018_afix_deleted.py +3 -1
  57. slm/migrations/0018_alter_siteantenna_options_and_more.py +87 -56
  58. slm/migrations/0019_remove_siteantenna_marker_enu_siteantenna_marker_une_and_more.py +1 -1
  59. slm/migrations/0023_archivedsitelog_gml_version_and_more.py +1 -1
  60. slm/migrations/0031_alter_antenna_features.py +44 -0
  61. slm/migrations/0032_archiveindex_valid_range_and_more.py +84 -0
  62. slm/migrations/add_index_order_index.py +54 -0
  63. slm/migrations/normalize_index.py +147 -0
  64. slm/migrations/simplify_index.py +48 -0
  65. slm/migrations/verify_index.py +67 -0
  66. slm/models/__init__.py +2 -0
  67. slm/models/alerts.py +7 -10
  68. slm/models/data.py +1 -2
  69. slm/models/equipment.py +1 -1
  70. slm/models/fields.py +41 -0
  71. slm/models/index.py +183 -53
  72. slm/models/sitelog.py +35 -38
  73. slm/models/system.py +72 -31
  74. slm/models/user.py +1 -1
  75. slm/parsing/__init__.py +34 -17
  76. slm/parsing/legacy/binding.py +65 -34
  77. slm/parsing/legacy/parser.py +2 -2
  78. slm/parsing/xsd/binding.py +1 -1
  79. slm/parsing/xsd/parser.py +1 -2
  80. slm/receivers/__init__.py +2 -2
  81. slm/receivers/index.py +2 -1
  82. slm/receivers/migration.py +21 -0
  83. slm/settings/__init__.py +192 -4
  84. slm/settings/assets.py +26 -0
  85. slm/settings/auth.py +18 -14
  86. slm/settings/ckeditor.py +12 -6
  87. slm/settings/debug.py +2 -2
  88. slm/settings/emails.py +50 -0
  89. slm/settings/internationalization.py +8 -6
  90. slm/settings/logging.py +100 -88
  91. slm/settings/platform/darwin.py +16 -6
  92. slm/settings/rest.py +20 -15
  93. slm/settings/root.py +192 -98
  94. slm/settings/routines.py +5 -1
  95. slm/settings/secrets.py +20 -31
  96. slm/settings/security.py +7 -5
  97. slm/settings/slm.py +35 -16
  98. slm/settings/static_templates.py +12 -9
  99. slm/settings/templates.py +31 -25
  100. slm/settings/uploads.py +33 -5
  101. slm/settings/urls.py +1 -1
  102. slm/settings/validation.py +165 -165
  103. slm/signals.py +3 -2
  104. slm/static/slm/css/style.css +37 -36
  105. slm/static/slm/js/autocomplete.js +6 -4
  106. slm/static/slm/js/file_modal.js +62 -0
  107. slm/static/slm/js/form.js +3 -3
  108. slm/static/slm/js/formWidget.js +3 -3
  109. slm/static/slm/js/persistable.js +5 -1
  110. slm/templates/admin/base.html +1 -0
  111. slm/templates/rest_framework/base.html +23 -11
  112. slm/templates/slm/base.html +27 -22
  113. slm/templates/slm/forms/widgets/auto_complete.html +12 -11
  114. slm/templates/slm/forms/widgets/auto_complete_multiple.html +8 -7
  115. slm/templates/slm/station/download.html +6 -6
  116. slm/templates/slm/station/edit.html +9 -17
  117. slm/templates/slm/station/review.html +5 -3
  118. slm/templates/slm/station/upload.html +4 -1
  119. slm/templates/slm/station/uploads/legacy.html +1 -1
  120. slm/templates/slm/widgets/alert_scroll.html +4 -8
  121. slm/templates/slm/widgets/filelist.html +0 -5
  122. slm/templates/slm/widgets/log_scroll.html +2 -13
  123. slm/templates/slm/widgets/stationlist.html +2 -8
  124. slm/templatetags/slm.py +70 -9
  125. slm/utils.py +13 -4
  126. slm/validators.py +14 -14
  127. slm/views.py +6 -6
  128. slm/wsgi.py +16 -0
  129. igs_slm-0.1.2b0.dist-info/METADATA +0 -151
  130. igs_slm-0.1.2b0.dist-info/entry_points.txt +0 -3
  131. slm/bin/templates/{{ project_dir }}/sites/{{ site }}/validation.py +0 -11
  132. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/__init__.py +0 -0
  133. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/__init__.py +0 -0
  134. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/local.py +0 -0
  135. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/develop/wsgi.py +0 -0
  136. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/manage.py +0 -0
  137. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/production/wsgi.py +0 -0
  138. /slm/bin/templates/{{ project_dir }}/{sites → src/sites}/{{ site }}/urls.py +0 -0
  139. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/__init__.py +0 -0
  140. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/admin.py +0 -0
  141. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/apps.py +0 -0
  142. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/__init__.py +0 -0
  143. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/commands/__init__.py +0 -0
  144. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/management/commands/import_archive.py +0 -0
  145. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/migrations/__init__.py +0 -0
  146. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/models.py +0 -0
  147. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/templates/slm/base.html +0 -0
  148. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/urls.py +0 -0
  149. /slm/bin/templates/{{ project_dir }}/{{{ extension_app }} → src/{{ extension_app }}}/views.py +0 -0
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'\n{" "*30}: '.join([line for line in limited if line.strip()])
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'{"+" if float(dddmmssss) > 0 else "-"}{iso_frmt}'
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'{antenna.antenna_type.model}{" " * spacing}{radome}'
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'{text}{" " * (int(length) - len(str(text)))}'
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'{file_domain.rstrip("/")}/{path.lstrip("/")}'
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 ' f"model"
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'{get_url()}/{path.lstrip("/")}'
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 = np.array(xyz) / a_e
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 gettext as _
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'{_("must be the prefix of the 9 character site name")}.',
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'{getattr(value, "name", None)} '
194
- f'{_("must does not match the antenna reference point: ")}'
195
- f'{getattr(at_arp, "name", None)}.',
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'{_("must be greater than")} '
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'{_("Cannot define")} '
283
+ f"{_('Cannot define')} "
284
284
  f"{instance._meta.get_field(self.end_field).verbose_name} "
285
- f'{_("without defining")} '
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'{_("must be less than")} '
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'Site {context["station"]} was not found!')
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'Station {kwargs.get("station", "")} does not exist!')
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("-index__begin", "-log_format")
583
- .values_list("index__begin", "id")
584
- .distinct("index__begin")
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,151 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: igs-slm
3
- Version: 0.1.2b0
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 :: 5 - Production/Stable
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: chardet (>=5.1.0,<6.0.0)
35
- Requires-Dist: crispy-bootstrap5 (>=2024.2,<2025.0)
36
- Requires-Dist: django-allauth (>=0.63.3,<0.64.0)
37
- Requires-Dist: django-ckeditor (>=6.5.1,<7.0.0)
38
- Requires-Dist: django-compressor (>=4.0,<5.0)
39
- Requires-Dist: django-crispy-forms (>=2.0,<3.0)
40
- Requires-Dist: django-enum (>=1.2.2,<2.0.0)
41
- Requires-Dist: django-filter (>=24.2,<25.0)
42
- Requires-Dist: django-ipware (>=7.0.1,<8.0.0)
43
- Requires-Dist: django-polymorphic (>=3.1.0,<4.0.0)
44
- Requires-Dist: django-render-static (>=3.1.2,<4.0.0)
45
- Requires-Dist: django-routines (>=1.1.3,<2.0.0)
46
- Requires-Dist: django-split-settings (>=1.2.0,<2.0.0)
47
- Requires-Dist: django-typer (>=2.1.2,<3.0.0)
48
- Requires-Dist: django-widget-tweaks (>=1.4.12,<2.0.0)
49
- Requires-Dist: djangorestframework (>=3.15.2,<4.0.0)
50
- Requires-Dist: djangorestframework-gis (>=1.0,<2.0)
51
- Requires-Dist: enum-properties (>=1.7.0,<2.0.0)
52
- Requires-Dist: geojson (>=3.1.0,<4.0.0)
53
- Requires-Dist: importlib-resources (>1.3.0)
54
- Requires-Dist: lxml (>=5.2.1,<6.0.0)
55
- Requires-Dist: numpy (<=1.24) ; python_version <= "3.8"
56
- Requires-Dist: numpy (>=1.26) ; python_version > "3.8"
57
- Requires-Dist: polyline (>=2.0.0,<3.0.0)
58
- Requires-Dist: psycopg[binary] (==3.1.18)
59
- Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
60
- Requires-Dist: requests (>=2.32.3,<3.0.0)
61
- Requires-Dist: rich (>=13.7.1,<14.0.0)
62
- Requires-Dist: tqdm (>=4.64.1,<5.0.0)
63
- Project-URL: Repository, https://github.com/International-GNSS-Service/SLM
64
- Description-Content-Type: text/markdown
65
-
66
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
67
- [![PyPI version](https://badge.fury.io/py/igs-slm.svg)](https://pypi.python.org/pypi/igs-slm/)
68
- [![PyPI pyversions](https://img.shields.io/pypi/pyversions/igs-slm.svg)](https://pypi.python.org/pypi/igs-slm/)
69
- [![PyPI djversions](https://img.shields.io/pypi/djversions/igs-slm.svg)](https://pypi.org/project/igs-slm/)
70
- [![PyPI status](https://img.shields.io/pypi/status/igs-slm.svg)](https://pypi.python.org/pypi/igs-slm)
71
- [![Documentation Status](https://readthedocs.org/projects/igs-slm/badge/?version=latest)](http://igs-slm.readthedocs.io/?badge=latest/)
72
- [![codecov](https://codecov.io/github/International-GNSS-Service/SLM/graph/badge.svg?token=PQVWN1LNM3)](https://codecov.io/github/International-GNSS-Service/SLM)
73
- [![Test Status](https://github.com/International-GNSS-Service/SLM/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/International-GNSS-Service/SLM/actions/workflows/test.yml?branch=master)
74
- [![Lint Status](https://github.com/International-GNSS-Service/SLM/workflows/lint/badge.svg)](https://github.com/International-GNSS-Service/SLM/actions/workflows/lint.yml)
75
- [![Security Status](https://github.com/International-GNSS-Service/SLM/workflows/security/badge.svg)](https://github.com/International-GNSS-Service/SLM/actions/workflows/security.yml)
76
- [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
77
-
78
- # ![](https://github.com/International-GNSS-Service/SLM/blob/master/slm/static/slm/img/slm-logo.svg?raw=true)
79
- Site Log Manager (SLM)
80
-
81
- The Site Log Manager (SLM) is a web platform that aims to provide:
82
-
83
- 1. GNSS Site meta data (site log) management with a moderation workflow.
84
- 2. Support for multiple organizations and networks to be managed in an access controlled way.
85
- 3. Full legacy site log format support (both import and export).
86
- 4. Full GeodesyML support (both import and export).
87
- 5. JSON renderings of meta data.
88
- 6. Point-and-click graphical editing of site log data.
89
- 7. Public RESTful api for searching site log data.
90
- 8. Authenticated RESTful api for updating site log data.
91
- 9. Full access to the historical record.
92
- 10. Visualizations of networks and site information.
93
- 11. Configurable data validation that goes above and beyond schema validation.
94
- 12. Image and file attachments to sites.
95
- 13. A no-fork extensible architecture that allows organizations to modify out-of-the box
96
- behavior with plugins.
97
-
98
- This code base has reached beta-maturity but is still undergoing rapid development. Check back soon
99
- for new documentation and updates.
100
-
101
-
102
- ## Table of Contents
103
- 1. [Design](#Design)
104
- 1. [Stack](#Stack)
105
- 2. [Organization](#Organization)
106
-
107
- ## Design
108
-
109
- SLM is built in Python using the [Django website development framework.](https://www.djangoproject.com/)
110
- Django is well documented. A basic understanding of how it works is helpful to understand how SLM is
111
- put together. In addition to the [good intro tutorials](https://docs.djangoproject.com/en/stable/intro/tutorial01/), it's
112
- helpful to understand [how reusable Django apps work](https://docs.djangoproject.com/en/stable/intro/reusable-apps/), how
113
- [settings files work](https://docs.djangoproject.com/en/stable/topics/settings/) and how
114
- [management commands work.](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/)
115
-
116
- ### Stack
117
-
118
- Django can be served behind many http servers. A common production environment uses [Apache](https://httpd.apache.org/)
119
- managing Django as a [WSGI](https://modwsgi.readthedocs.io/en/develop/index.html) daemon, but
120
- another common setup involves proxying a [gunicorn](https://gunicorn.org/) instance behind [nginx](https://www.nginx.com).
121
- In addition to Django, other critical components of the software stack are listed in the table below. Not all Python
122
- dependencies are listed because many are incidental.
123
-
124
- | Dependency | Description |
125
- | ------------------------------------------------------------------------------ | ---------------------------------------------------- |
126
- | [PostgreSQL](https://www.postgresql.org/) | Relation database management system |
127
- | [Django](https://djangoproject.com) | Website development framework |
128
- | [jQuery](https://jquery.com/) | Javascript DOM navigation library |
129
- | [DataTables](https://datatables.net/) | Javascript tables library |
130
- | [Bootstrap](https://getbootstrap.com/) | CSS framework |
131
- | [djangorestframework](https://www.django-rest-framework.org/) | RESTful API framework for Django |
132
- | [django-split-settings](https://github.com/sobolevn/django-split-settings) | Composite settings files for Django |
133
- | [django_compressor](https://django-compressor.readthedocs.io/en/stable/) | Static file compression and management |
134
- | [memcached](https://memcached.org/) | Memory object caching system |
135
- | [django-render-static](https://django-render-static.readthedocs.io/en/latest/) | Static file rendering, javascript urls |
136
- | [django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/latest/) | Debugging components for Django sites (test only) |
137
-
138
-
139
- ### Organization
140
-
141
- #### Environment & Setup
142
-
143
- 1. [pyenv](https://github.com/pyenv/pyenv) is not strictly required, but it is highly recommended to help manage multiple
144
- local Python installations and keep environments clean. Python 3.8+ is required.
145
- 2. [Poetry](https://Python-poetry.org/) is used for dependency and package management.
146
- 3. SLM requires PostgresSQL along with the PostGIS extension that enables geographic queries to be run directly by the database.
147
-
148
- | RDBMS | Minimum Version | Management Utilities |
149
- | ---------------------------------------------| ----------------- | ------------------------------------------------------------|
150
- | [PostgreSQL](https://www.postgresql.org/) | 12 | [PgAdmin](https://www.pgadmin.org/) |
151
-
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- slm-startproject=slm.bin.startproject:app
3
-
@@ -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 %}