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
slm/settings/__init__.py
CHANGED
|
@@ -1,13 +1,125 @@
|
|
|
1
|
+
"""
|
|
2
|
+
These settings utilities help us compose settings across multiple files.
|
|
3
|
+
|
|
4
|
+
.. warning::
|
|
5
|
+
|
|
6
|
+
These functions only work when called from a settings file because
|
|
7
|
+
they operate on the calling scope. Do not call these functions from
|
|
8
|
+
any other location.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
1
12
|
import sys
|
|
13
|
+
import typing as t
|
|
14
|
+
from functools import partial
|
|
15
|
+
from os import PathLike
|
|
16
|
+
from pathlib import Path
|
|
2
17
|
|
|
18
|
+
import environ
|
|
3
19
|
import importlib_resources
|
|
20
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
21
|
+
|
|
22
|
+
re.Pattern
|
|
4
23
|
|
|
5
24
|
|
|
6
25
|
class _NotGiven:
|
|
7
26
|
pass
|
|
8
27
|
|
|
9
28
|
|
|
10
|
-
|
|
29
|
+
_environ: t.Optional[environ.FileAwareEnv] = None
|
|
30
|
+
_base_dir: t.Optional[Path] = None
|
|
31
|
+
_email_rgx: t.Optional[re.Pattern] = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def split_email_str(email: str) -> t.Tuple[str, str]:
|
|
35
|
+
"""
|
|
36
|
+
Split an email string into display name and email address.
|
|
37
|
+
|
|
38
|
+
If there is no display name it will be the empty string.
|
|
39
|
+
|
|
40
|
+
:param email: An email string. E.g.:
|
|
41
|
+
* ``name@example.com``
|
|
42
|
+
* ``Last, First <name@example.com>``
|
|
43
|
+
* ``"First Last" <name@example.com>``
|
|
44
|
+
|
|
45
|
+
:return: A 2-tuple of (display name, email address)
|
|
46
|
+
"""
|
|
47
|
+
global _email_rgx
|
|
48
|
+
if "<" not in email:
|
|
49
|
+
return ("", email)
|
|
50
|
+
if not _email_rgx:
|
|
51
|
+
_email_rgx = re.compile(r"\s+[\"']?(?P<display>.*)[\"']?\s+<(?P<email>.*)>")
|
|
52
|
+
if not (match := _email_rgx.search(email)):
|
|
53
|
+
raise ImproperlyConfigured(f"{email} is not a valid email string.")
|
|
54
|
+
return match.groupdict["display"].strip(), match.groupdict["email"].strip()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _is_relative_to(parent: Path, child: Path) -> bool:
|
|
58
|
+
# TODO remove when python >=3.9
|
|
59
|
+
if sys.version_info[:2] < (3, 9):
|
|
60
|
+
try:
|
|
61
|
+
child.relative_to(parent)
|
|
62
|
+
return True
|
|
63
|
+
except ValueError:
|
|
64
|
+
return False
|
|
65
|
+
return child.is_relative_to(parent)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def env(**kwargs) -> environ.FileAwareEnv:
|
|
69
|
+
"""
|
|
70
|
+
Fetch the :class:`environ.Env` object that should be used to configure settings.
|
|
71
|
+
If an environment file exists it will be read in. The environment file precendence
|
|
72
|
+
is:
|
|
73
|
+
|
|
74
|
+
1. The path in the environment variable SLM_ENV
|
|
75
|
+
2. BASE_DIR / .env
|
|
76
|
+
|
|
77
|
+
:return: The :class:`~environ.FileAwareEnv` object configured to read our
|
|
78
|
+
environment variables and files.
|
|
79
|
+
"""
|
|
80
|
+
global _environ
|
|
81
|
+
if _environ is None:
|
|
82
|
+
scope = sys._getframe(1).f_globals
|
|
83
|
+
default_env = scope.get("BASE_DIR", "")
|
|
84
|
+
if default_env:
|
|
85
|
+
default_env = str(Path(default_env) / ".env")
|
|
86
|
+
|
|
87
|
+
DEBUG = kwargs.pop("DEBUG", (bool, False))
|
|
88
|
+
_environ = environ.FileAwareEnv(DEBUG=DEBUG, **kwargs)
|
|
89
|
+
override_env = _environ.str("SLM_ENV", scope.get("SLM_ENV", default_env))
|
|
90
|
+
if override_env and Path(override_env).is_file():
|
|
91
|
+
_environ.read_env(str(override_env))
|
|
92
|
+
return _environ
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def base_dir(scope=None) -> Path:
|
|
96
|
+
"""
|
|
97
|
+
Fetch the :setting:`BASE_DIR`, this will raise an
|
|
98
|
+
:exc:`~django.core.exceptions.ImproperlyConfigured` exception if it does not exist yet.
|
|
99
|
+
|
|
100
|
+
:param scope: The settings scope (do not pass if calling from a settings file)
|
|
101
|
+
:return: The :setting:`BASE_DIR` as a :class:`pathlib.Path` object.
|
|
102
|
+
:raises: :exc:`~django.core.exceptions.ImproperlyConfigured` if :setting:`BASE_DIR` is not
|
|
103
|
+
defined.
|
|
104
|
+
"""
|
|
105
|
+
global _base_dir
|
|
106
|
+
if not _base_dir:
|
|
107
|
+
scope = scope or sys._getframe(1).f_globals
|
|
108
|
+
_base_dir = scope.get("BASE_DIR", None)
|
|
109
|
+
if _base_dir is None:
|
|
110
|
+
raise ImproperlyConfigured("BASE_DIR was needed before it was defined!")
|
|
111
|
+
_base_dir = Path(_base_dir)
|
|
112
|
+
if not _base_dir.is_dir():
|
|
113
|
+
if scope.get("DEBUG", False):
|
|
114
|
+
_base_dir.mkdir(mode=0o775, parents=True, exist_ok=True)
|
|
115
|
+
else:
|
|
116
|
+
raise ImproperlyConfigured(
|
|
117
|
+
f"BASE_DIR: {_base_dir} is not a directory that exists."
|
|
118
|
+
)
|
|
119
|
+
return _base_dir
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def set_default(var_name: str, default: t.Any, set_if_none: bool = True) -> t.Any:
|
|
11
123
|
"""
|
|
12
124
|
Set the value of the given variable in the calling scope to a default value if it has been not been previously
|
|
13
125
|
defined.
|
|
@@ -23,7 +135,7 @@ def set_default(var_name, default, set_if_none=True):
|
|
|
23
135
|
return scope[var_name]
|
|
24
136
|
|
|
25
137
|
|
|
26
|
-
def is_defined(var_name):
|
|
138
|
+
def is_defined(var_name: str) -> bool:
|
|
27
139
|
"""
|
|
28
140
|
Returns true if the given variable has been defined in the calling scope.
|
|
29
141
|
|
|
@@ -33,7 +145,7 @@ def is_defined(var_name):
|
|
|
33
145
|
return var_name in sys._getframe(1).f_globals
|
|
34
146
|
|
|
35
147
|
|
|
36
|
-
def get_setting(var_name, default=_NotGiven):
|
|
148
|
+
def get_setting(var_name: str, default: t.Any = _NotGiven) -> t.Any:
|
|
37
149
|
"""
|
|
38
150
|
Returns the value of the setting if it exists, if it does not exist the value
|
|
39
151
|
given to default is returned. If no default value is given and the setting
|
|
@@ -49,7 +161,83 @@ def get_setting(var_name, default=_NotGiven):
|
|
|
49
161
|
return value
|
|
50
162
|
|
|
51
163
|
|
|
52
|
-
def
|
|
164
|
+
def slm_path(
|
|
165
|
+
value: str, must_exist: bool = False, mk_dirs: bool = False
|
|
166
|
+
) -> t.Optional[str]:
|
|
167
|
+
"""
|
|
168
|
+
Resolve a configuration path. If the path is relative it will be resolved under
|
|
169
|
+
:setting:`BASE_DIR`. If the value is falsey it will be returned as-is even if
|
|
170
|
+
``must_exist`` is set.
|
|
171
|
+
|
|
172
|
+
:param value: The path as a string
|
|
173
|
+
:param must_exist: If True, raise an
|
|
174
|
+
:exc:`~django.core.exceptions.ImproperlyConfigured` if the path does not exist.
|
|
175
|
+
:param mk_dirs: If True and the path is relative to :setting:`BASE_DIR` create the
|
|
176
|
+
path as a directory if it does not already exist.
|
|
177
|
+
:raises: :exc:`~django.core.exceptions.ImproperlyConfigured` if :setting:`BASE_DIR` is
|
|
178
|
+
needed but not defined, or does not exist or if ``must_exist`` is ``True`` but the
|
|
179
|
+
path does not exist.
|
|
180
|
+
"""
|
|
181
|
+
if not value:
|
|
182
|
+
return value
|
|
183
|
+
pth = Path(str(value))
|
|
184
|
+
|
|
185
|
+
if relative := not pth.is_absolute():
|
|
186
|
+
pth = base_dir(sys._getframe(1).f_globals) / pth
|
|
187
|
+
|
|
188
|
+
if not pth.exists() and mk_dirs:
|
|
189
|
+
relative = relative or _is_relative_to(
|
|
190
|
+
base_dir(sys._getframe(1).f_globals), pth
|
|
191
|
+
)
|
|
192
|
+
if relative:
|
|
193
|
+
pth.mkdir(
|
|
194
|
+
mode=0o775 if sys._getframe(1).f_globals.get("DEBUG", False) else 0o770,
|
|
195
|
+
parents=True,
|
|
196
|
+
exist_ok=True,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if must_exist and not pth.exists():
|
|
200
|
+
raise ImproperlyConfigured(f"{pth} does not exist!")
|
|
201
|
+
return str(pth)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
slm_path_must_exist = partial(slm_path, must_exist=True)
|
|
205
|
+
"""
|
|
206
|
+
A shortcut for :func:`~slm.settings.slm_path` with ``must_exist=True``.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
slm_path_mk_dirs_must_exist = partial(slm_path, must_exist=True, mk_dirs=True)
|
|
210
|
+
"""
|
|
211
|
+
A shortcut for :func:`~slm.settings.slm_path` with ``must_exist=True`` and
|
|
212
|
+
``mk_dirs=True``.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def unset(var_name: str):
|
|
217
|
+
"""
|
|
218
|
+
Unset the value of the given variable in the calling scope.
|
|
219
|
+
|
|
220
|
+
:param var_name: The name of the variable to unset in the calling scope.
|
|
221
|
+
"""
|
|
222
|
+
scope = sys._getframe(1).f_globals
|
|
223
|
+
if var_name in scope:
|
|
224
|
+
del scope[var_name]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def resource(package: str, file: PathLike):
|
|
228
|
+
"""
|
|
229
|
+
Use a packaged resource file as a settings file include. If you are
|
|
230
|
+
including a settings file from another package you should pass this to
|
|
231
|
+
split_settings.tools.include:
|
|
232
|
+
|
|
233
|
+
.. code-block:: python
|
|
234
|
+
|
|
235
|
+
include(resource("slm.settings", "root.py"))
|
|
236
|
+
|
|
237
|
+
:param package: The string import path of the package containing the module
|
|
238
|
+
:param file: The name of the python settings module
|
|
239
|
+
:return: The full string path to the resource.
|
|
240
|
+
"""
|
|
53
241
|
profile2 = importlib_resources.files(package) / file
|
|
54
242
|
with importlib_resources.as_file(profile2) as profile2_path:
|
|
55
243
|
return str(profile2_path)
|
slm/settings/assets.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines settings that control how assets are compressed and bundled using django-compressor.
|
|
3
|
+
|
|
4
|
+
https://django-compressor.readthedocs.io
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from slm.settings import get_setting, set_default
|
|
8
|
+
|
|
9
|
+
set_default("COMPRESS_OFFLINE", True)
|
|
10
|
+
set_default("COMPRESS_ROOT", get_setting("STATIC_ROOT"))
|
|
11
|
+
set_default("COMPRESS_URL", get_setting("STATIC_URL"))
|
|
12
|
+
set_default("COMPRESS_OUTPUT_DIR", "assets")
|
|
13
|
+
|
|
14
|
+
set_default(
|
|
15
|
+
"COMPRESS_PRECOMPILERS",
|
|
16
|
+
(
|
|
17
|
+
(
|
|
18
|
+
"text/javascript",
|
|
19
|
+
"npx -yes esbuild@latest {infile} --bundle --minify --outfile={outfile}",
|
|
20
|
+
),
|
|
21
|
+
(
|
|
22
|
+
"module",
|
|
23
|
+
"npx -yes esbuild@latest {infile} --bundle --minify --outfile={outfile}",
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
)
|
slm/settings/auth.py
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
ACCOUNT_UNIQUE_EMAIL = True
|
|
3
|
-
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
|
|
4
|
-
ACCOUNT_EMAIL_REQUIRED = True
|
|
5
|
-
ACCOUNT_USERNAME_REQUIRED = False
|
|
6
|
-
ACCOUNT_AUTHENTICATION_METHOD = "email"
|
|
7
|
-
LOGIN_URL = "/accounts/login/"
|
|
8
|
-
LOGIN_REDIRECT_URL = "/"
|
|
1
|
+
from slm.settings import set_default
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
set_default("ACCOUNT_LOGIN_ON_PASSWORD_RESET", True)
|
|
4
|
+
set_default("ACCOUNT_UNIQUE_EMAIL", True)
|
|
5
|
+
set_default("ACCOUNT_USER_MODEL_USERNAME_FIELD", None)
|
|
6
|
+
set_default("ACCOUNT_LOGIN_METHODS", {"email"})
|
|
7
|
+
set_default("ACCOUNT_SIGNUP_FIELDS", ["email*", "password1*", "password2*"])
|
|
8
|
+
set_default("LOGIN_URL", "/accounts/login/")
|
|
9
|
+
set_default("LOGIN_REDIRECT_URL", "/")
|
|
10
|
+
|
|
11
|
+
set_default(
|
|
12
|
+
"AUTHENTICATION_BACKENDS",
|
|
13
|
+
[
|
|
14
|
+
# Needed to login by username in Django admin, regardless of `allauth`
|
|
15
|
+
"django.contrib.auth.backends.ModelBackend",
|
|
16
|
+
# `allauth` specific authentication methods, such as login by e-mail
|
|
17
|
+
"allauth.account.auth_backends.AuthenticationBackend",
|
|
18
|
+
],
|
|
19
|
+
)
|
slm/settings/ckeditor.py
CHANGED
|
@@ -2,13 +2,19 @@ from slm.settings import get_setting, set_default
|
|
|
2
2
|
|
|
3
3
|
# your webserver should be configured to bypass Django and server
|
|
4
4
|
# MEDIA_ROOT / CKEDITOR_UPLOAD_PATH directly
|
|
5
|
-
CKEDITOR_UPLOAD_PATH
|
|
5
|
+
set_default("CKEDITOR_UPLOAD_PATH", get_setting("MEDIA_ROOT") / "public/")
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
7
|
+
set_default(
|
|
8
|
+
"CKEDITOR_CONFIGS",
|
|
9
|
+
{
|
|
10
|
+
"richtextinput": {
|
|
11
|
+
"width": "100%",
|
|
12
|
+
},
|
|
13
|
+
"default": {
|
|
14
|
+
# exportpdf is not included but still getting the api key warning in js console??
|
|
15
|
+
"removePlugins": "exportpdf",
|
|
16
|
+
},
|
|
10
17
|
},
|
|
11
|
-
|
|
12
|
-
}
|
|
18
|
+
)
|
|
13
19
|
|
|
14
20
|
set_default("SILENCED_SYSTEM_CHECKS", []).append("ckeditor.W001")
|
slm/settings/debug.py
CHANGED
|
@@ -4,10 +4,10 @@ from django.core.exceptions import ImproperlyConfigured
|
|
|
4
4
|
|
|
5
5
|
from slm.settings import get_setting, set_default
|
|
6
6
|
|
|
7
|
-
if get_setting("
|
|
7
|
+
if get_setting("SLM_DEBUG_TOOLBAR", False):
|
|
8
8
|
if not find_spec("debug_toolbar"):
|
|
9
9
|
raise ImproperlyConfigured(
|
|
10
|
-
"
|
|
10
|
+
"SLM_DEBUG_TOOLBAR is True, but django-debug-toolbar is notinstalled."
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
# list any middlewares that might be encountered on the stack that must
|
slm/settings/emails.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from slm.settings import env as settings_environment
|
|
2
|
+
from slm.settings import (
|
|
3
|
+
get_setting,
|
|
4
|
+
slm_path_mk_dirs_must_exist,
|
|
5
|
+
slm_path_must_exist,
|
|
6
|
+
split_email_str,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
env = settings_environment()
|
|
10
|
+
|
|
11
|
+
_email_config = env.email_url(
|
|
12
|
+
"SLM_EMAIL_SERVER", default=get_setting("SLM_EMAIL_SERVER", "smtp://localhost:25")
|
|
13
|
+
)
|
|
14
|
+
if EMAIL_FILE_PATH := _email_config.pop("EMAIL_FILE_PATH", None):
|
|
15
|
+
EMAIL_FILE_PATH = slm_path_mk_dirs_must_exist(EMAIL_FILE_PATH)
|
|
16
|
+
|
|
17
|
+
if not EMAIL_FILE_PATH:
|
|
18
|
+
del EMAIL_FILE_PATH
|
|
19
|
+
|
|
20
|
+
vars().update(_email_config)
|
|
21
|
+
|
|
22
|
+
SERVER_EMAIL = env("SERVER_EMAIL", default=get_setting("SERVER_EMAIL", None))
|
|
23
|
+
if SERVER_EMAIL is None:
|
|
24
|
+
domain = (
|
|
25
|
+
get_setting("SLM_SITE_NAME", None)
|
|
26
|
+
or get_setting("ALLOWED_HOSTS", ["localhost"])[0]
|
|
27
|
+
)
|
|
28
|
+
SERVER_EMAIL = f"noreply@{domain}"
|
|
29
|
+
|
|
30
|
+
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL", default=SERVER_EMAIL)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
EMAIL_SSL_CERTFILE = env(
|
|
34
|
+
"EMAIL_SSL_CERTFILE",
|
|
35
|
+
slm_path_must_exist,
|
|
36
|
+
default=get_setting("EMAIL_SSL_CERTFILE", None),
|
|
37
|
+
)
|
|
38
|
+
EMAIL_SSL_KEYFILE = env(
|
|
39
|
+
"EMAIL_SSL_KEYFILE",
|
|
40
|
+
slm_path_must_exist,
|
|
41
|
+
default=get_setting("EMAIL_SSL_KEYFILE", None),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# these emails will receive email reports when 500 HTTP messages are returned
|
|
46
|
+
# TODO - on Django 6+ change this from email_str -> tuple to tuple -> email str
|
|
47
|
+
ADMINS = [
|
|
48
|
+
split_email_str(email) if isinstance(email, str) else email
|
|
49
|
+
for email in env.list("ADMINS", default=get_setting("ADMINS", []))
|
|
50
|
+
]
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Internationalization
|
|
2
2
|
# https://docs.djangoproject.com/en/stable/topics/i18n/
|
|
3
|
+
from slm.settings import env as settings_environment
|
|
4
|
+
from slm.settings import get_setting, set_default
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
env = settings_environment()
|
|
7
|
+
|
|
8
|
+
LANGUAGE_CODE = env("LANGUAGE_CODE", default=get_setting("LANGUAGE_CODE", "en-us"))
|
|
5
9
|
|
|
6
10
|
# ALL TIMES IN THE DB SHOULD BE STORED IN UTC - The web interface can then
|
|
7
11
|
# reliably translate this into browser local time
|
|
8
|
-
TIME_ZONE
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
USE_TZ = True
|
|
12
|
+
set_default("TIME_ZONE", "UTC")
|
|
13
|
+
set_default("USE_I18N", not bool(LANGUAGE_CODE.startswith("en")))
|
|
14
|
+
set_default("USE_TZ", True)
|
slm/settings/logging.py
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from logging import Filter
|
|
3
3
|
|
|
4
|
-
from slm.settings import
|
|
4
|
+
from slm.settings import env as settings_environment
|
|
5
|
+
from slm.settings import get_setting, is_defined, slm_path_mk_dirs_must_exist
|
|
6
|
+
|
|
7
|
+
env = settings_environment()
|
|
5
8
|
|
|
6
9
|
DEBUG = get_setting("DEBUG", False)
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
SLM_LOG_LEVEL = env(
|
|
11
|
+
"SLM_LOG_LEVEL", default=get_setting("SLM_LOG_LEVEL", "DEBUG" if DEBUG else "INFO")
|
|
12
|
+
)
|
|
13
|
+
SLM_LOG_DIR = env(
|
|
14
|
+
"SLM_LOG_DIR",
|
|
15
|
+
slm_path_mk_dirs_must_exist,
|
|
16
|
+
default=get_setting("SLM_LOG_DIR", get_setting("BASE_DIR") / "logs"),
|
|
17
|
+
)
|
|
9
18
|
SLM_MANAGEMENT_MODE = get_setting("SLM_MANAGEMENT_MODE", False)
|
|
10
19
|
|
|
11
20
|
|
|
@@ -15,99 +24,102 @@ class SquelchStackTraces(Filter):
|
|
|
15
24
|
return super().filter(record)
|
|
16
25
|
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"format": "%(levelname)s %(process)d %(asctime)s %(name)s %(process)d %(thread)d %(message)s"
|
|
27
|
+
if not is_defined("LOGGING"):
|
|
28
|
+
LOGGING = {
|
|
29
|
+
"version": 1,
|
|
30
|
+
"disable_existing_loggers": False,
|
|
31
|
+
"handlers": {
|
|
32
|
+
"file": {
|
|
33
|
+
"level": SLM_LOG_LEVEL, # set in deployment routine
|
|
34
|
+
"class": "logging.handlers.TimedRotatingFileHandler",
|
|
35
|
+
"formatter": "verbose"
|
|
36
|
+
if DEBUG
|
|
37
|
+
else "simple", # set in deployment routine
|
|
38
|
+
"filename": SLM_LOG_DIR
|
|
39
|
+
/ f"{get_setting('SLM_SITE_NAME', '').lower()}{'_manage' if SLM_MANAGEMENT_MODE else ''}.log",
|
|
40
|
+
"when": "midnight",
|
|
41
|
+
"interval": 1,
|
|
42
|
+
"backupCount": 14,
|
|
43
|
+
},
|
|
44
|
+
"mail_admins": {
|
|
45
|
+
"level": "ERROR",
|
|
46
|
+
"class": "django.utils.log.AdminEmailHandler",
|
|
47
|
+
"include_html": True,
|
|
48
|
+
},
|
|
41
49
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
50
|
+
"formatters": {
|
|
51
|
+
"verbose": {
|
|
52
|
+
"format": "%(levelname)s %(process)d %(asctime)s %(name)s %(process)d %(thread)d %(message)s"
|
|
53
|
+
},
|
|
54
|
+
"simple": {
|
|
55
|
+
"format": "%(levelname)s %(process)d %(asctime)s %(name)s %(message)s"
|
|
56
|
+
},
|
|
57
|
+
"management": {"format": "%(message)s"},
|
|
44
58
|
},
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"()": SquelchStackTraces,
|
|
59
|
+
"filters": {
|
|
60
|
+
"squelch_traces": {
|
|
61
|
+
"()": SquelchStackTraces,
|
|
62
|
+
},
|
|
50
63
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
"loggers": {
|
|
65
|
+
"django.request": {
|
|
66
|
+
"handlers": ["mail_admins"],
|
|
67
|
+
"level": "ERROR",
|
|
68
|
+
"propagate": True,
|
|
69
|
+
},
|
|
70
|
+
"django": {
|
|
71
|
+
"handlers": ["file"],
|
|
72
|
+
"level": SLM_LOG_LEVEL,
|
|
73
|
+
"propagate": False,
|
|
74
|
+
},
|
|
75
|
+
"django.db.backends": {
|
|
76
|
+
"handlers": ["file"],
|
|
77
|
+
"level": "INFO", # super noisy
|
|
78
|
+
"propagate": False,
|
|
79
|
+
},
|
|
80
|
+
"django.template": {
|
|
81
|
+
"handlers": ["file"],
|
|
82
|
+
"filters": ["squelch_traces"],
|
|
83
|
+
"level": "INFO",
|
|
84
|
+
"propagate": False,
|
|
85
|
+
},
|
|
86
|
+
"django.utils.autoreload": {
|
|
87
|
+
"handlers": ["file"],
|
|
88
|
+
"level": "WARNING", # this logger got really noisy in django 2.2
|
|
89
|
+
"propagate": False,
|
|
90
|
+
},
|
|
91
|
+
"django_auth_ldap": {"level": SLM_LOG_LEVEL, "propagate": True},
|
|
62
92
|
},
|
|
63
|
-
"
|
|
64
|
-
"handlers": ["file"],
|
|
65
|
-
"level":
|
|
66
|
-
"propagate": False,
|
|
93
|
+
"root": {
|
|
94
|
+
"handlers": ["file", "mail_admins"],
|
|
95
|
+
"level": SLM_LOG_LEVEL, # set in deployment routine
|
|
67
96
|
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"propagate": False,
|
|
73
|
-
},
|
|
74
|
-
"django.utils.autoreload": {
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if DEBUG and not SLM_MANAGEMENT_MODE:
|
|
100
|
+
LOGGING["loggers"]["core.middleware.RequestLogger"] = {
|
|
75
101
|
"handlers": ["file"],
|
|
76
|
-
"level": "
|
|
102
|
+
"level": "DEBUG",
|
|
77
103
|
"propagate": False,
|
|
78
|
-
}
|
|
79
|
-
"django_auth_ldap": {"level": DEFAULT_LOG_LEVEL, "propagate": True},
|
|
80
|
-
},
|
|
81
|
-
"root": {
|
|
82
|
-
"handlers": ["file", "mail_admins"],
|
|
83
|
-
"level": DEFAULT_LOG_LEVEL, # set in deployment routine
|
|
84
|
-
},
|
|
85
|
-
}
|
|
104
|
+
}
|
|
86
105
|
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if SLM_MANAGEMENT_MODE:
|
|
95
|
-
LOGGING.setdefault("handlers", {})["console"] = {
|
|
96
|
-
"level": "INFO",
|
|
97
|
-
"class": "logging.StreamHandler",
|
|
98
|
-
"formatter": "management",
|
|
99
|
-
}
|
|
106
|
+
if SLM_MANAGEMENT_MODE:
|
|
107
|
+
LOGGING.setdefault("handlers", {})["console"] = {
|
|
108
|
+
"level": "INFO",
|
|
109
|
+
"class": "logging.StreamHandler",
|
|
110
|
+
"formatter": "management",
|
|
111
|
+
}
|
|
100
112
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
if "root" in LOGGING:
|
|
114
|
+
if "console" not in LOGGING["root"]["handlers"]:
|
|
115
|
+
LOGGING["root"]["handlers"].append("console")
|
|
104
116
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
for name, config in LOGGING.get("loggers", {}).items():
|
|
118
|
+
if "handlers" in config and "console" not in config["handlers"]:
|
|
119
|
+
config["handlers"].append("console")
|
|
108
120
|
|
|
109
|
-
# create logging dirs if necessary
|
|
110
|
-
for name, handler_spec in LOGGING["handlers"].items():
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
# create logging dirs if necessary
|
|
122
|
+
for name, handler_spec in LOGGING["handlers"].items():
|
|
123
|
+
filename = handler_spec.get("filename", None)
|
|
124
|
+
if filename:
|
|
125
|
+
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
slm/settings/platform/darwin.py
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# this is the default location Postgres.app with postgres will install the postgis libraries to
|
|
2
2
|
# if your system is different, or you use a different postgres package you will have to
|
|
3
3
|
# override these
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
6
|
+
from slm.settings import get_setting
|
|
7
|
+
|
|
8
|
+
if not get_setting("GDAL_LIBRARY_PATH"):
|
|
9
|
+
gdal_postgres_app_path = Path(
|
|
10
|
+
"/Applications/Postgres.app/Contents/Versions/latest/lib/libgdal.dylib"
|
|
11
|
+
)
|
|
12
|
+
if gdal_postgres_app_path.is_file():
|
|
13
|
+
GDAL_LIBRARY_PATH = str(gdal_postgres_app_path)
|
|
14
|
+
|
|
15
|
+
if not get_setting("GEOS_LIBRARY_PATH"):
|
|
16
|
+
geos_postgres_app_path = Path(
|
|
17
|
+
"/Applications/Postgres.app/Contents/Versions/latest/lib/libgeos_c.dylib"
|
|
18
|
+
)
|
|
19
|
+
if geos_postgres_app_path.is_file():
|
|
20
|
+
GEOS_LIBRARY_PATH = str(geos_postgres_app_path)
|