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
@@ -22,7 +22,7 @@ from typing_extensions import Annotated
22
22
 
23
23
  from slm.defines import ISOCountry
24
24
  from slm.models import Network, Site, SiteAntenna, SiteReceiver
25
- from slm.utils import dddmmss_ss_parts, xyz2llh
25
+ from slm.utils import dddmmss_ss_parts, transliterate, xyz2llh
26
26
 
27
27
  DEFAULT_ANTEX = "https://files.igs.org/pub/station/general/igs20.atx.gz"
28
28
 
@@ -35,7 +35,7 @@ def sinex_time(time):
35
35
  if time is None:
36
36
  return "00:000:00000"
37
37
  return (
38
- f'{time.strftime("%y:%j")}:'
38
+ f"{time.strftime('%y:%j')}:"
39
39
  f"{time.hour * 60 * 60 + time.minute * 60 + time.second:05.0f}"
40
40
  )
41
41
 
@@ -78,6 +78,7 @@ class Command(TyperCommand):
78
78
  siteAnt: dict
79
79
  atx: dict
80
80
  sat_phase_center: dict
81
+ utf8: bool = False
81
82
 
82
83
  suppressed_base_arguments = {
83
84
  *TyperCommand.suppressed_base_arguments,
@@ -117,11 +118,18 @@ class Command(TyperCommand):
117
118
  bool,
118
119
  Option("--include-former", help=_("Include former sites in the list.")),
119
120
  ] = False,
121
+ utf8: Annotated[
122
+ bool,
123
+ Option(
124
+ "--utf8", help=_("Allow multi-byte UTF-8 characters in the output.")
125
+ ),
126
+ ] = False,
120
127
  ):
121
128
  self.sinex_code = ""
122
129
  self.siteAnt = rec_dd()
123
130
  self.atx = rec_dd()
124
131
  self.sat_phase_center = rec_dd()
132
+ self.utf8 = utf8
125
133
 
126
134
  sites = Site.objects.all().order_by("name")
127
135
  sites = sites.public() if include_former else sites.active()
@@ -144,52 +152,65 @@ class Command(TyperCommand):
144
152
 
145
153
  output = self.stdout
146
154
  if destination:
147
- output = open(destination, "w")
155
+ output = open(
156
+ destination, "wt", encoding="ascii" if not self.utf8 else "utf8"
157
+ )
148
158
 
149
159
  # write header
150
- for line in self.header(antex_file):
160
+ for line in self.transliterate(self.header(antex_file)):
151
161
  output.write(f"{line}\n")
152
162
 
153
163
  # write SITE/ID section
154
- for line in self.site_ids(sites):
164
+ for line in self.transliterate(self.site_ids(sites)):
155
165
  output.write(f"{line}\n")
156
166
 
157
167
  # write SITE/RECEIVER section
158
- for line in self.site_receivers(sites):
168
+ for line in self.transliterate(self.site_receivers(sites)):
159
169
  output.write(f"{line}\n")
160
170
 
161
171
  # write SITE/ANTENNA section
162
- for line in self.site_antennas(sites):
172
+ for line in self.transliterate(self.site_antennas(sites)):
163
173
  output.write(f"{line}\n")
164
174
 
165
175
  # write GPS/PHASECENTER section
166
- for line in self.gps_phase_center():
176
+ for line in self.transliterate(self.gps_phase_center()):
167
177
  output.write(f"{line}\n")
168
178
 
169
179
  # write GAL/PHASECENTER section
170
- for line in self.gal_phase_center():
180
+ for line in self.transliterate(self.gal_phase_center()):
171
181
  output.write(f"{line}\n")
172
182
 
173
183
  # write SITE/ECCENTRICITY
174
- for line in self.site_eccentricity(sites):
184
+ for line in self.transliterate(self.site_eccentricity(sites)):
175
185
  output.write(f"{line}\n")
176
186
 
177
187
  # write SATELLITE/ID
178
- for line in self.satellite_ids():
188
+ for line in self.transliterate(self.satellite_ids()):
179
189
  output.write(f"{line}\n")
180
190
 
181
191
  # write +SATELLITE/PHASE_CENTER
182
- for line in self.satellite_phase_centers():
192
+ for line in self.transliterate(self.satellite_phase_centers()):
183
193
  output.write(f"{line}\n")
184
194
 
185
195
  # close out the file
186
- for line in self.footer():
196
+ for line in self.transliterate(self.footer()):
187
197
  output.write(f"{line}\n")
188
198
 
189
199
  if destination:
190
200
  output.close()
191
201
 
192
- def header(self, antex_file):
202
+ def transliterate(
203
+ self, utf8_str: t.Generator[str, None, None]
204
+ ) -> t.Generator[str, None, None]:
205
+ """
206
+ Transliterate utf8 -> ascii if we were told to.
207
+ """
208
+ if self.utf8:
209
+ yield from utf8_str
210
+ for ustr in utf8_str:
211
+ yield transliterate(ustr)
212
+
213
+ def header(self, antex_file) -> t.Generator[str, None, None]:
193
214
  now = datetime.now()
194
215
  now_date = "%04d-%02d-%02dT%02d:%02d:%02d" % (
195
216
  now.year,
@@ -217,19 +238,18 @@ class Command(TyperCommand):
217
238
  yield " This file is generated daily from all current IGS site logs in"
218
239
  yield " https://files.igs.org/pub/station/log/"
219
240
  yield (
220
- " Phase center offsets are a function of antenna type, using as "
221
- "reference"
241
+ " Phase center offsets are a function of antenna type, using as reference"
222
242
  )
223
- yield f' {antex_file.rstrip(".gz")}'
243
+ yield f" {antex_file.rstrip('.gz')}"
224
244
  yield "-FILE/COMMENT"
225
245
  yield "+INPUT/ACKNOWLEDGMENTS"
226
246
  yield " IGS International GNSS Service"
227
247
  yield "-INPUT/ACKNOWLEDGMENTS"
228
248
 
229
- def footer(self):
249
+ def footer(self) -> t.Generator[str, None, None]:
230
250
  yield "%ENDSNX"
231
251
 
232
- def site_ids(self, sites):
252
+ def site_ids(self, sites) -> t.Generator[str, None, None]:
233
253
  sites = sites.with_location_fields().with_identification_fields()
234
254
  yield "+SITE/ID"
235
255
  yield (
@@ -247,7 +267,7 @@ class Command(TyperCommand):
247
267
  llh = xyz2llh(site.xyz.coords)
248
268
  location = html.unescape(
249
269
  f"{site.city or site.state}"
250
- f'{"," if site.city or site.state else ""}{ctry}'
270
+ f"{',' if site.city or site.state else ''}{ctry}"
251
271
  )[:21]
252
272
  lat_deg, lat_min, lat_sec = dddmmss_ss_parts(llh[0])
253
273
 
@@ -268,7 +288,7 @@ class Command(TyperCommand):
268
288
  )
269
289
  yield "-SITE/ID"
270
290
 
271
- def site_receivers(self, sites):
291
+ def site_receivers(self, sites) -> t.Generator[str, None, None]:
272
292
  yield "+SITE/RECEIVER"
273
293
  yield (
274
294
  "*CODE PT SOLN T _DATA START_ __DATA_END__ ___RECEIVER_TYPE____ "
@@ -293,11 +313,10 @@ class Command(TyperCommand):
293
313
  )
294
314
  yield "-SITE/RECEIVER"
295
315
 
296
- def site_antennas(self, sites):
316
+ def site_antennas(self, sites) -> t.Generator[str, None, None]:
297
317
  yield "+SITE/ANTENNA"
298
318
  yield (
299
- "*CODE PT SOLN T _DATA START_ __DATA_END__ ____ANTENNA_TYPE____ "
300
- "_S/N_ _DAZ"
319
+ "*CODE PT SOLN T _DATA START_ __DATA_END__ ____ANTENNA_TYPE____ _S/N_ _DAZ"
301
320
  )
302
321
  for site in sites.prefetch_related(
303
322
  Prefetch(
@@ -321,7 +340,7 @@ class Command(TyperCommand):
321
340
  # seems wrong
322
341
  yield "-SITE/ANTENNA"
323
342
 
324
- def gps_phase_center(self):
343
+ def gps_phase_center(self) -> t.Generator[str, None, None]:
325
344
  yield "+SITE/GPS_PHASE_CENTER"
326
345
  yield (
327
346
  "*ANTENNA_NAME____DOME S_NO_ __UP__ NORTH_ _EAST_ __UP__ NORTH_ "
@@ -330,23 +349,23 @@ class Command(TyperCommand):
330
349
  for ant in sorted(self.siteAnt):
331
350
  if "G02" in self.siteAnt[ant]:
332
351
  yield (
333
- f' {ant:20} ----- {self.siteAnt[ant]["G01"]["up"]:6.6} '
334
- f'{self.siteAnt[ant]["G01"]["north"]:6.6} '
335
- f'{self.siteAnt[ant]["G01"]["east"]:6.6} '
336
- f'{self.siteAnt[ant]["G02"]["up"]:6.6} '
337
- f'{self.siteAnt[ant]["G02"]["north"]:6.6} '
338
- f'{self.siteAnt[ant]["G02"]["east"]:6.6} {self.sinex_code:10.10}'
352
+ f" {ant:20} ----- {self.siteAnt[ant]['G01']['up']:6.6} "
353
+ f"{self.siteAnt[ant]['G01']['north']:6.6} "
354
+ f"{self.siteAnt[ant]['G01']['east']:6.6} "
355
+ f"{self.siteAnt[ant]['G02']['up']:6.6} "
356
+ f"{self.siteAnt[ant]['G02']['north']:6.6} "
357
+ f"{self.siteAnt[ant]['G02']['east']:6.6} {self.sinex_code:10.10}"
339
358
  )
340
359
  else:
341
360
  yield (
342
- f' {ant:20} ----- {self.siteAnt[ant]["G01"]["up"]:6.6} '
343
- f'{self.siteAnt[ant]["G01"]["north"]:6.6} '
344
- f'{self.siteAnt[ant]["G01"]["east"]:6.6} ------ ------ ------ '
361
+ f" {ant:20} ----- {self.siteAnt[ant]['G01']['up']:6.6} "
362
+ f"{self.siteAnt[ant]['G01']['north']:6.6} "
363
+ f"{self.siteAnt[ant]['G01']['east']:6.6} ------ ------ ------ "
345
364
  f"{self.sinex_code:10.10}"
346
365
  )
347
366
  yield "-SITE/GPS_PHASE_CENTER"
348
367
 
349
- def gal_phase_center(self):
368
+ def gal_phase_center(self) -> t.Generator[str, None, None]:
350
369
  yield "+SITE/GAL_PHASE_CENTER"
351
370
  yield (
352
371
  "*ANTENNA_NAME____DOME S_NO_ __UP__ NORTH_ _EAST_ __UP__ NORTH_ "
@@ -355,17 +374,17 @@ class Command(TyperCommand):
355
374
  for ant in sorted(self.siteAnt):
356
375
  if "E01" in self.siteAnt[ant]:
357
376
  _e01 = (
358
- f'{self.siteAnt[ant]["E01"]["up"]:6.6} '
359
- f'{self.siteAnt[ant]["E01"]["north"]:6.6} '
360
- f'{self.siteAnt[ant]["E01"]["east"]:6.6}'
377
+ f"{self.siteAnt[ant]['E01']['up']:6.6} "
378
+ f"{self.siteAnt[ant]['E01']['north']:6.6} "
379
+ f"{self.siteAnt[ant]['E01']['east']:6.6}"
361
380
  )
362
381
  else:
363
382
  _e01 = "------ ------ ------"
364
383
  if "E05" in self.siteAnt[ant]:
365
384
  _e05 = (
366
- f'{self.siteAnt[ant]["E05"]["up"]:6.6} '
367
- f'{self.siteAnt[ant]["E05"]["north"]:6.6} '
368
- f'{self.siteAnt[ant]["E05"]["east"]:6.6}'
385
+ f"{self.siteAnt[ant]['E05']['up']:6.6} "
386
+ f"{self.siteAnt[ant]['E05']['north']:6.6} "
387
+ f"{self.siteAnt[ant]['E05']['east']:6.6}"
369
388
  )
370
389
  else:
371
390
  _e05 = "------ ------ ------"
@@ -373,17 +392,17 @@ class Command(TyperCommand):
373
392
 
374
393
  if "E06" in self.siteAnt[ant]:
375
394
  _e06 = (
376
- f'{self.siteAnt[ant]["E06"]["up"]:6.6} '
377
- f'{self.siteAnt[ant]["E06"]["north"]:6.6} '
378
- f'{self.siteAnt[ant]["E06"]["east"]:6.6}'
395
+ f"{self.siteAnt[ant]['E06']['up']:6.6} "
396
+ f"{self.siteAnt[ant]['E06']['north']:6.6} "
397
+ f"{self.siteAnt[ant]['E06']['east']:6.6}"
379
398
  )
380
399
  else:
381
400
  _e06 = "------ ------ ------"
382
401
  if "E07" in self.siteAnt[ant]:
383
402
  _e07 = (
384
- f'{self.siteAnt[ant]["E07"]["up"]:6.6} '
385
- f'{self.siteAnt[ant]["E07"]["north"]:6.6} '
386
- f'{self.siteAnt[ant]["E07"]["east"]:6.6}'
403
+ f"{self.siteAnt[ant]['E07']['up']:6.6} "
404
+ f"{self.siteAnt[ant]['E07']['north']:6.6} "
405
+ f"{self.siteAnt[ant]['E07']['east']:6.6}"
387
406
  )
388
407
  else:
389
408
  _e07 = "------ ------ ------"
@@ -391,9 +410,9 @@ class Command(TyperCommand):
391
410
 
392
411
  if "E08" in self.siteAnt[ant]:
393
412
  _e08 = (
394
- f'{self.siteAnt[ant]["E08"]["up"]:6.6} '
395
- f'{self.siteAnt[ant]["E08"]["north"]:6.6} '
396
- f'{self.siteAnt[ant]["E08"]["east"]:6.6}'
413
+ f"{self.siteAnt[ant]['E08']['up']:6.6} "
414
+ f"{self.siteAnt[ant]['E08']['north']:6.6} "
415
+ f"{self.siteAnt[ant]['E08']['east']:6.6}"
397
416
  )
398
417
  else:
399
418
  _e08 = "------ ------ ------"
@@ -401,11 +420,10 @@ class Command(TyperCommand):
401
420
  yield f" {ant} ---- {_e08} ------ ------ ------ {self.sinex_code:10s}"
402
421
  yield "-SITE/GAL_PHASE_CENTER"
403
422
 
404
- def site_eccentricity(self, sites):
423
+ def site_eccentricity(self, sites) -> t.Generator[str, None, None]:
405
424
  yield "+SITE/ECCENTRICITY"
406
425
  yield (
407
- "*CODE PT SOLN T _DATA START_ __DATA_END__ REF __DX_U__ __DX_N__ "
408
- "__DX_E__"
426
+ "*CODE PT SOLN T _DATA START_ __DATA_END__ REF __DX_U__ __DX_N__ __DX_E__"
409
427
  )
410
428
  for site in sites.prefetch_related(
411
429
  Prefetch(
@@ -424,39 +442,39 @@ class Command(TyperCommand):
424
442
  )
425
443
  yield "-SITE/ECCENTRICITY"
426
444
 
427
- def satellite_ids(self):
445
+ def satellite_ids(self) -> t.Generator[str, None, None]:
428
446
  yield "+SATELLITE/ID"
429
- yield ("*CNNN PN COSPARID_ T _START_DATE_ __END_DATE__ " "____ANTENNA_TYPE____")
447
+ yield ("*CNNN PN COSPARID_ T _START_DATE_ __END_DATE__ ____ANTENNA_TYPE____")
430
448
  for sat in sorted(self.atx):
431
449
  for startDate in self.atx[sat]:
432
450
  yield (
433
- f' {sat:.4s} {self.atx[sat][startDate]["prn"]:.2s} '
434
- f'{self.atx[sat][startDate]["cospar_id"]:.9s} P '
435
- f'{startDate:.12s} {self.atx[sat][startDate]["endDate"]:.12s} '
436
- f'{self.atx[sat][startDate]["name"]:.20s}'
451
+ f" {sat:.4s} {self.atx[sat][startDate]['prn']:.2s} "
452
+ f"{self.atx[sat][startDate]['cospar_id']:.9s} P "
453
+ f"{startDate:.12s} {self.atx[sat][startDate]['endDate']:.12s} "
454
+ f"{self.atx[sat][startDate]['name']:.20s}"
437
455
  )
438
456
  yield "-SATELLITE/ID"
439
457
 
440
- def satellite_phase_centers(self):
458
+ def satellite_phase_centers(self) -> t.Generator[str, None, None]:
441
459
  yield "+SATELLITE/PHASE_CENTER"
442
- yield ("*CNNN F __UP__ NORTH_ _EAST_ F __UP__ NORTH_ _EAST_ " "SINEXCODE_ V M")
460
+ yield ("*CNNN F __UP__ NORTH_ _EAST_ F __UP__ NORTH_ _EAST_ SINEXCODE_ V M")
443
461
  for sat in sorted(self.sat_phase_center):
444
462
  if sat.startswith("G"):
445
463
  yield (
446
- f' {sat:4.4} 1 {self.sat_phase_center[sat]["G01"]["up"]:6.6} '
447
- f'{self.sat_phase_center[sat]["G01"]["north"]:6.6} '
448
- f'{self.sat_phase_center[sat]["G01"]["east"]:6.6} 2 '
449
- f'{self.sat_phase_center[sat]["G02"]["up"]:6.6} '
450
- f'{self.sat_phase_center[sat]["G02"]["north"]:6.6} '
451
- f'{self.sat_phase_center[sat]["G02"]["east"]:6.6} '
464
+ f" {sat:4.4} 1 {self.sat_phase_center[sat]['G01']['up']:6.6} "
465
+ f"{self.sat_phase_center[sat]['G01']['north']:6.6} "
466
+ f"{self.sat_phase_center[sat]['G01']['east']:6.6} 2 "
467
+ f"{self.sat_phase_center[sat]['G02']['up']:6.6} "
468
+ f"{self.sat_phase_center[sat]['G02']['north']:6.6} "
469
+ f"{self.sat_phase_center[sat]['G02']['east']:6.6} "
452
470
  f"{self.sinex_code:10.10} A E"
453
471
  )
454
472
  try:
455
473
  yield (
456
474
  f" {sat:4.4} 5 "
457
- f'{self.sat_phase_center[sat]["G05"]["up"]:6.6} '
458
- f'{self.sat_phase_center[sat]["G05"]["north"]:6.6} '
459
- f'{self.sat_phase_center[sat]["G05"]["east"]:6.6} - ------ '
475
+ f"{self.sat_phase_center[sat]['G05']['up']:6.6} "
476
+ f"{self.sat_phase_center[sat]['G05']['north']:6.6} "
477
+ f"{self.sat_phase_center[sat]['G05']['east']:6.6} - ------ "
460
478
  f"------ ------ {self.sinex_code:10.10} A E"
461
479
  )
462
480
  except Exception:
@@ -464,44 +482,44 @@ class Command(TyperCommand):
464
482
 
465
483
  if sat.startswith("R"):
466
484
  yield (
467
- f' {sat:4.4} 1 {self.sat_phase_center[sat]["R01"]["up"]:6.6} '
468
- f'{self.sat_phase_center[sat]["R01"]["north"]:6.6} '
469
- f'{self.sat_phase_center[sat]["R01"]["east"]:6.6} 2 '
470
- f'{self.sat_phase_center[sat]["R02"]["up"]:6.6} '
471
- f'{self.sat_phase_center[sat]["R02"]["north"]:6.6} '
472
- f'{self.sat_phase_center[sat]["R02"]["east"]:6.6} '
485
+ f" {sat:4.4} 1 {self.sat_phase_center[sat]['R01']['up']:6.6} "
486
+ f"{self.sat_phase_center[sat]['R01']['north']:6.6} "
487
+ f"{self.sat_phase_center[sat]['R01']['east']:6.6} 2 "
488
+ f"{self.sat_phase_center[sat]['R02']['up']:6.6} "
489
+ f"{self.sat_phase_center[sat]['R02']['north']:6.6} "
490
+ f"{self.sat_phase_center[sat]['R02']['east']:6.6} "
473
491
  f"{self.sinex_code:10.10} A E"
474
492
  )
475
493
 
476
494
  if sat.startswith("E"):
477
495
  yield (
478
- f' {sat:4.4} 1 {self.sat_phase_center[sat]["E01"]["up"]:6.6} '
479
- f'{self.sat_phase_center[sat]["E01"]["north"]:6.6} '
480
- f'{self.sat_phase_center[sat]["E01"]["east"]:6.6} 5 '
481
- f'{self.sat_phase_center[sat]["E05"]["up"]:6.6} '
482
- f'{self.sat_phase_center[sat]["E05"]["north"]:6.6} '
483
- f'{self.sat_phase_center[sat]["E05"]["east"]:6.6} '
496
+ f" {sat:4.4} 1 {self.sat_phase_center[sat]['E01']['up']:6.6} "
497
+ f"{self.sat_phase_center[sat]['E01']['north']:6.6} "
498
+ f"{self.sat_phase_center[sat]['E01']['east']:6.6} 5 "
499
+ f"{self.sat_phase_center[sat]['E05']['up']:6.6} "
500
+ f"{self.sat_phase_center[sat]['E05']['north']:6.6} "
501
+ f"{self.sat_phase_center[sat]['E05']['east']:6.6} "
484
502
  f"{self.sinex_code:10.10} A E"
485
503
  )
486
504
  yield (
487
- f' {sat:4.4} 6 {self.sat_phase_center[sat]["E06"]["up"]:6.6} '
488
- f'{self.sat_phase_center[sat]["E06"]["north"]:6.6} '
489
- f'{self.sat_phase_center[sat]["E06"]["east"]:6.6} 7 '
490
- f'{self.sat_phase_center[sat]["E07"]["up"]:6.6} '
491
- f'{self.sat_phase_center[sat]["E07"]["north"]:6.6} '
492
- f'{self.sat_phase_center[sat]["E07"]["east"]:6.6} '
505
+ f" {sat:4.4} 6 {self.sat_phase_center[sat]['E06']['up']:6.6} "
506
+ f"{self.sat_phase_center[sat]['E06']['north']:6.6} "
507
+ f"{self.sat_phase_center[sat]['E06']['east']:6.6} 7 "
508
+ f"{self.sat_phase_center[sat]['E07']['up']:6.6} "
509
+ f"{self.sat_phase_center[sat]['E07']['north']:6.6} "
510
+ f"{self.sat_phase_center[sat]['E07']['east']:6.6} "
493
511
  f"{self.sinex_code:10.10} A E"
494
512
  )
495
513
  yield (
496
514
  f" {sat:4.4} 8 "
497
- f'{self.sat_phase_center[sat]["E08"]["up"]:6.6} '
498
- f'{self.sat_phase_center[sat]["E08"]["north"]:6.6} '
499
- f'{self.sat_phase_center[sat]["E08"]["east"]:6.6} - ------ '
515
+ f"{self.sat_phase_center[sat]['E08']['up']:6.6} "
516
+ f"{self.sat_phase_center[sat]['E08']['north']:6.6} "
517
+ f"{self.sat_phase_center[sat]['E08']['east']:6.6} - ------ "
500
518
  f"------ ------ {self.sinex_code:10.10} A E"
501
519
  )
502
520
  yield "-SATELLITE/PHASE_CENTER"
503
521
 
504
- def build_antex_index(self, antex_file):
522
+ def build_antex_index(self, antex_file: str):
505
523
  """
506
524
  Streams the antex file from files.igs.org and builds an index off of
507
525
  it.
@@ -1,13 +1,13 @@
1
1
  """
2
2
  If your site log file index is more current than the database state
3
- (i.e. you've run :ref:`command_import_archive`), you may run this command to pull data
3
+ (i.e. you've run :django-admin:`import_archive`), you may run this command to pull data
4
4
  from the most recent indexed files into the database.
5
5
 
6
6
  .. tip::
7
7
 
8
8
  By default, rich HTML logs of the import process will be written to:
9
9
 
10
- ``settings.LOG_DIR / head_from_index.TIMESTAMP``
10
+ ``settings.SLM_LOG_DIR / head_from_index.TIMESTAMP``
11
11
 
12
12
  ImportAlerts will also be issued for any errors flagged during import. When,
13
13
  appropriate validation flags will be attached to site log fields. This allows you
@@ -85,7 +85,7 @@ class Command(TyperCommand):
85
85
  sites: t.Sequence[Site]
86
86
  clean: bool = True
87
87
  prompt: bool = True
88
- logs: Path = Path("{LOG_DIR}") / "head_from_index.{TIMESTAMP}"
88
+ logs: Path = Path("{SLM_LOG_DIR}") / "head_from_index.{TIMESTAMP}"
89
89
  verbosity = 1
90
90
 
91
91
  indexes: t.Sequence[ArchiveIndex]
@@ -184,7 +184,7 @@ class Command(TyperCommand):
184
184
 
185
185
  self.logs = Path(
186
186
  str(logs).format(
187
- LOG_DIR=getattr(settings, "LOG_DIR", "./"),
187
+ SLM_LOG_DIR=getattr(settings, "SLM_LOG_DIR", "./"),
188
188
  TIMESTAMP=datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
189
189
  )
190
190
  )
@@ -214,8 +214,10 @@ class Command(TyperCommand):
214
214
  self.failed_imports = []
215
215
 
216
216
  self.indexes = (
217
- ArchiveIndex.objects.filter(site__in=self.sites, end__isnull=True)
218
- .order_by("site", "-begin")
217
+ ArchiveIndex.objects.filter(
218
+ site__in=self.sites, valid_range__upper_inf=True
219
+ )
220
+ .order_by("site", "-valid_range")
219
221
  .distinct("site")
220
222
  )
221
223
 
@@ -465,9 +467,10 @@ class Command(TyperCommand):
465
467
  setattr(form, field, value)
466
468
  form.previous = (
467
469
  ArchiveIndex.objects.filter(
468
- site=index.site, end__lte=index.begin
470
+ site=index.site,
471
+ valid_range__startswith__lte=index.begin,
469
472
  )
470
- .order_by("-end")
473
+ .order_by("-valid_range")
471
474
  .first()
472
475
  )
473
476
  form.save(skip_update=True, set_previous=False)
@@ -3,17 +3,17 @@ When transitioning site log management into an SLM you will have to import all o
3
3
  existing data. There are two primary steps involved in doing this:
4
4
 
5
5
  1. Import the index of existing serialized point in time site logs.
6
- 2. Populate the sitelog fields in the database from the most recent site logs for each
6
+ 2. Populate the site log fields in the database from the most recent site logs for each
7
7
  station.
8
8
 
9
- This command will perform #1 and optionally call :ref:`command_head_from_index` on the
9
+ This command will perform #1 and optionally call :django-admin:`head_from_index` on the
10
10
  imported stations to also perform #2.
11
11
 
12
12
  .. tip::
13
13
 
14
14
  Rich HTML logs of the import process will be written to:
15
15
 
16
- ``settings.LOG_DIR / import_archive.TIMESTAMP``
16
+ ``settings.SLM_LOG_DIR / import_archive.TIMESTAMP``
17
17
 
18
18
  Logs will be parsed and errors reported, but parsing errors will not prevent logs from
19
19
  being indexed.
@@ -185,7 +185,7 @@ class Command(TyperCommand):
185
185
  agencies: t.List[Agency] = []
186
186
  owner: t.Optional[User] = None
187
187
 
188
- logs: Path = Path("{LOG_DIR}") / "import_archive.{TIMESTAMP}"
188
+ logs: Path = Path("{SLM_LOG_DIR}") / "import_archive.{TIMESTAMP}"
189
189
  verbosity: int = 1
190
190
  traceback: bool = False
191
191
 
@@ -194,9 +194,9 @@ class Command(TyperCommand):
194
194
 
195
195
  # this matches fourYYMM.log and four_YYYYMMDD.log styles
196
196
  FILE_NAME_REGEX = re.compile(
197
- r"^((?P<four_id>[a-zA-Z\d]{4})(?P<yymm>\d{4})|"
198
- r"((?P<site>[a-zA-Z\d]{4,9})?\D*(?P<date_part>\d{8})(?P<extra>.*)))"
199
- r"[.](?P<ext>(log)|(txt)|(xml))$"
197
+ r"^((?P<four_id>[a-zA-Z\d]{4})(?P<yymm>\d{4})?|"
198
+ r"((?P<site>[a-zA-Z\d]{4,})?\D*(?P<date_part>\d{8})?(?P<extra>.*)?))"
199
+ r"[.](?P<ext>(log)|(sitelog)|(txt)|(xml)|(gml)|(json))$"
200
200
  )
201
201
 
202
202
  def process_filename(
@@ -381,7 +381,9 @@ class Command(TyperCommand):
381
381
  traceback: Traceback = traceback,
382
382
  ):
383
383
  if not archive:
384
- archive = Path(input(_("Where is the archive? (directory or tar/zip): ")))
384
+ archive = Path(
385
+ input(_("Where is the archive? (directory or tar/zip): ")).strip()
386
+ )
385
387
  self.archive = archive.expanduser()
386
388
  if not self.archive.exists():
387
389
  raise CommandError(
@@ -394,7 +396,7 @@ class Command(TyperCommand):
394
396
  self.owner = owner
395
397
  self.logs = Path(
396
398
  str(logs).format(
397
- LOG_DIR=getattr(settings, "LOG_DIR", "./"),
399
+ SLM_LOG_DIR=getattr(settings, "SLM_LOG_DIR", "./"),
398
400
  TIMESTAMP=datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
399
401
  )
400
402
  )
@@ -414,10 +416,11 @@ class Command(TyperCommand):
414
416
  if not self.archive.exists():
415
417
  raise CommandError(_("{file} does not exist.").format(file=self.archive))
416
418
 
417
- if SiteLogFormat.GEODESY_ML in self.formats:
418
- from slm.parsing.xsd import load_schemas
419
+ # if SiteLogFormat.GEODESY_ML in self.formats:
420
+ # # TODO - only do this if necessary
421
+ # from slm.parsing.xsd import load_schemas
419
422
 
420
- load_schemas()
423
+ # load_schemas()
421
424
 
422
425
  if self.archive.is_file() and tarfile.is_tarfile(self.archive):
423
426
  with tarfile.open(self.archive, "r") as archive:
@@ -451,7 +454,7 @@ class Command(TyperCommand):
451
454
  if self.verbosity > 1:
452
455
  self.secho(
453
456
  _(
454
- "Unable to interpret {filename} as a sitelog."
457
+ "Unable to interpret {filename} as a site log."
455
458
  ).format(filename=archive_name),
456
459
  fg="red",
457
460
  )
@@ -503,7 +506,7 @@ class Command(TyperCommand):
503
506
  if self.verbosity > 1:
504
507
  self.secho(
505
508
  _(
506
- "Unable to interpret {filename} as a sitelog."
509
+ "Unable to interpret {filename} as a site log."
507
510
  ).format(filename=file.name),
508
511
  fg="red",
509
512
  )
@@ -537,7 +540,7 @@ class Command(TyperCommand):
537
540
  if not file_meta:
538
541
  raise CommandError(
539
542
  _(
540
- "Unable to interpret {file} as either an tar archive or a sitelog."
543
+ "Unable to interpret {file} as either an tar archive or a site log."
541
544
  ).format(file=self.archive)
542
545
  )
543
546
  file_meta.contents = self.archive.read_bytes()
@@ -582,7 +585,9 @@ class Command(TyperCommand):
582
585
  _("Updating {site} state.").format(site=site.name), fg="blue"
583
586
  )
584
587
  save = False
585
- recent_first = ArchiveIndex.objects.filter(site=site).order_by("-begin")
588
+ recent_first = ArchiveIndex.objects.filter(site=site).order_by(
589
+ "-valid_range"
590
+ )
586
591
  latest = recent_first.first()
587
592
  oldest = recent_first.last()
588
593
  if site.last_publish is None or site.last_publish < latest.begin:
@@ -684,8 +689,12 @@ class Command(TyperCommand):
684
689
  with open(log, "wt") as log_f:
685
690
  log_f.write(log_index)
686
691
 
687
- if log and self.verbosity > 0:
688
- return log.absolute()
692
+ if log:
693
+ import webbrowser
694
+
695
+ webbrowser.open(log.resolve().as_uri())
696
+ if self.verbosity > 0:
697
+ return log.absolute()
689
698
 
690
699
  def process_file(self, file_meta: FileMeta):
691
700
  with transaction.atomic():
@@ -2,7 +2,7 @@
2
2
  Station equipment including Antennas, Radomes and Receivers are coded by IGS and
3
3
  those standard codes are used to uniquely identify equipment in site log files.
4
4
 
5
- The full IGS changelog for equipment codes is recorded in rcvr_ant.tab_.
5
+ The full IGS change log for equipment codes is recorded in rcvr_ant.tab_.
6
6
 
7
7
  The SLM stores these codes in database tables, so it is possible to instantiate an
8
8
  SLM with a different set of equipment codes. This command can be used to load codes
@@ -118,9 +118,7 @@ class Command(TyperCommand):
118
118
  if self.archive:
119
119
  index = ArchiveIndex.objects.filter(site=self.site).first()
120
120
  qry = Q(log_format=SiteLogFormat.GEODESY_ML)
121
- qry &= Q(index__begin__lte=self.archive) & (
122
- Q(index__end__gt=self.archive) | Q(index__end__isnull=True)
123
- )
121
+ qry &= Q(index__valid_range__contains=self.archive)
124
122
  if gml_version:
125
123
  qry &= Q(gml_version=gml_version)
126
124
  log_file = index.files.filter(qry).first()
@@ -9,7 +9,7 @@ Some examples of denormalized state in the SLM include:
9
9
 
10
10
  * Counts of validation flags
11
11
  * Maximum alert levels for stations
12
- * Sitelog status indicators (PUBLISHED/UNPUBLISHED) for stations.
12
+ * Site log status indicators (PUBLISHED/UNPUBLISHED) for stations.
13
13
  """
14
14
 
15
15
  import typing as t