From b9de95c7e8432db987b142c76c06023258ab3915 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Thu, 3 Mar 2022 12:51:14 -0700 Subject: [PATCH 01/20] import capabilities from scos-actions. --- src/capabilities/__init__.py | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/capabilities/__init__.py b/src/capabilities/__init__.py index 5197dd4e..5e6fe417 100644 --- a/src/capabilities/__init__.py +++ b/src/capabilities/__init__.py @@ -1,22 +1,9 @@ import copy - +from scos_actions.capabilities import capabilities from sensor import settings from sensor.settings import SENSOR_DEFINITION_FILE -def load_from_json(fname): - import json - import logging - - logger = logging.getLogger(__name__) - - try: - with open(fname) as f: - return json.load(f) - except Exception: - logger.exception("Unable to load JSON file {}".format(fname)) - - def get_sigmf_location(): from status.models import Location @@ -32,17 +19,13 @@ def get_sigmf_location(): return None -_capabilities = {} -_capabilities["sensor"] = load_from_json(SENSOR_DEFINITION_FILE) -_capabilities["sensor"]["id"] = settings.FQDN - - def get_capabilities(): - capabilities = copy.deepcopy(_capabilities) + updated_capabilities = copy.deepcopy(capabilities) + updated_capabilities["sensor"]["id"] = settings.FQDN location = get_sigmf_location() if location: - capabilities["sensor"]["location"] = location + updated_capabilities["sensor"]["location"] = location else: - if "location" in capabilities['sensor']: - del capabilities["sensor"]["location"] - return capabilities + if "location" in updated_capabilities['sensor']: + del updated_capabilities["sensor"]["location"] + return updated_capabilities From 22dd8585bd6bd91f9a5c6fc21b23c58392555445 Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Tue, 8 Mar 2022 14:23:20 -0700 Subject: [PATCH 02/20] only import debug toolbar, extensions in debug mode; remove sentry, flake8; refactor requirements --- docker/Dockerfile-api | 4 +- src/requirements-dev.in | 23 ++++--- src/requirements-dev.txt | 131 +++++++++++++++------------------------ src/requirements.in | 36 +++++------ src/requirements.txt | 67 +++++++++----------- src/sensor/settings.py | 20 +++--- src/tox.ini | 6 -- 7 files changed, 118 insertions(+), 169 deletions(-) diff --git a/docker/Dockerfile-api b/docker/Dockerfile-api index 63116cde..d7da5b20 100644 --- a/docker/Dockerfile-api +++ b/docker/Dockerfile-api @@ -17,11 +17,13 @@ ENV PYTHONUNBUFFERED 1 RUN mkdir -p /src WORKDIR /src COPY ./src/requirements.txt /src +COPY ./src/requirements-dev.txt /src ARG DOCKER_GIT_CREDENTIALS RUN git config --global credential.helper store && echo "${DOCKER_GIT_CREDENTIALS}" > ~/.git-credentials RUN python3.7 -m pip install --upgrade pip -RUN python3.7 -m pip install --no-cache-dir -r requirements.txt +ARG DEBUG +RUN if [ "$DEBUG" = true ]; then python3.7 -m pip install --no-cache-dir -r requirements-dev.txt ; else python3.7 -m pip install --no-cache-dir -r requirements.txt ; fi COPY ./src /src COPY ./gunicorn /gunicorn diff --git a/src/requirements-dev.in b/src/requirements-dev.in index aa5cf4be..21123f7a 100644 --- a/src/requirements-dev.in +++ b/src/requirements-dev.in @@ -1,14 +1,13 @@ -rrequirements.txt -black==21.12b0 -cryptography~=36.0.1 -flake8==3.7.7 -flake8-bugbear==19.3.0 -isort==4.3.20 -pip-tools -pre-commit~=2.17.0 -pytest-cov==2.7.1 -pytest-django==4.1.0 -pytest-flake8==1.0.4 -seed-isort-config==1.9.1 -tox~=3.12.1 +black>=22.0, <23.0 +cryptography>=36.0, <37.0 +django-debug-toolbar>=3.2.1, <4.0 +django-extensions>=3.0, <4.0 +isort>=5.0, <6.0 +numpy>=1.0, <2.0 +pre-commit>=2.0, <3.0 +pytest-cov>=3.0, <4.0 +pytest-django>=4.0, <5.0 +seed-isort-config>=2.0, <3.0 +tox>=3.0, <4.0 diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index b7eefb8a..80df58f9 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -8,24 +8,23 @@ asgiref==3.5.0 # via # -r requirements.txt # django -aspy.refactor-imports==2.2.1 +aspy-refactor-imports==2.2.1 # via seed-isort-config attrs==21.4.0 # via # -r requirements.txt - # flake8-bugbear # jsonschema # pytest bcrypt==3.2.0 # via # -r requirements.txt # paramiko -black==21.12b0 +black==22.1.0 # via -r requirements-dev.in cached-property==1.5.2 # via # -r requirements.txt - # aspy.refactor-imports + # aspy-refactor-imports # docker-compose certifi==2021.10.8 # via @@ -39,14 +38,12 @@ cffi==1.15.0 # pynacl cfgv==3.3.1 # via pre-commit -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 # via # -r requirements.txt # requests -click==8.0.3 - # via - # black - # pip-tools +click==8.0.4 + # via black coreapi==2.3.3 # via # -r requirements.txt @@ -56,7 +53,7 @@ coreschema==0.0.4 # -r requirements.txt # coreapi # drf-yasg -coverage==6.3.1 +coverage[toml]==6.3.2 # via pytest-cov cryptography==36.0.1 # via @@ -65,37 +62,35 @@ cryptography==36.0.1 # paramiko distlib==0.3.4 # via virtualenv -distro==1.6.0 +distro==1.7.0 # via # -r requirements.txt # docker-compose -django==3.1.14 +django==3.2.12 # via # -r requirements.txt # django-debug-toolbar # django-extensions - # django-filter # django-session-timeout # djangorestframework # drf-yasg # jsonfield -django-debug-toolbar==3.0 - # via -r requirements.txt + # scos-usrp +django-debug-toolbar==3.2.4 + # via -r requirements-dev.in django-extensions==3.1.5 - # via -r requirements.txt -django-filter==2.4.0 - # via -r requirements.txt + # via -r requirements-dev.in django-session-timeout==0.1.0 # via -r requirements.txt djangorestframework==3.13.1 # via # -r requirements.txt # drf-yasg -docker[ssh]==4.4.4 +docker[ssh]==5.0.3 # via # -r requirements.txt # docker-compose -docker-compose==1.28.6 +docker-compose==1.29.2 # via -r requirements.txt dockerpty==0.4.1 # via @@ -107,38 +102,31 @@ docopt==0.6.2 # docker-compose drf-yasg==1.20.0 # via -r requirements.txt -entrypoints==0.3 - # via flake8 -environs==7.3.1 - # via -r requirements.txt -filelock==3.4.2 +environs==9.5.0 + # via + # -r requirements.txt + # scos-usrp +filelock==3.6.0 # via # tox # virtualenv -flake8==3.7.7 - # via - # -r requirements-dev.in - # flake8-bugbear - # pytest-flake8 -flake8-bugbear==19.3.0 - # via -r requirements-dev.in -gunicorn==19.9.0 +gunicorn==20.1.0 # via -r requirements.txt -identify==2.4.7 +identify==2.4.11 # via pre-commit idna==3.3 # via # -r requirements.txt # requests -importlib-metadata==4.10.1 +importlib-metadata==4.11.2 # via # -r requirements.txt # click # jsonschema - # pep517 # pluggy # pre-commit # pytest + # tox # virtualenv inflection==0.5.1 # via @@ -146,7 +134,7 @@ inflection==0.5.1 # drf-yasg iniconfig==1.1.1 # via pytest -isort==4.3.20 +isort==5.10.1 # via -r requirements-dev.in itypes==1.2.0 # via @@ -162,7 +150,7 @@ jsonschema==3.2.0 # via # -r requirements.txt # docker-compose -markupsafe==2.0.1 +markupsafe==2.1.0 # via # -r requirements.txt # jinja2 @@ -170,17 +158,17 @@ marshmallow==3.14.1 # via # -r requirements.txt # environs -mccabe==0.6.1 - # via flake8 mypy-extensions==0.4.3 # via black nodeenv==1.6.0 # via pre-commit numpy==1.21.5 # via + # -r requirements-dev.in # -r requirements.txt # scipy -oauthlib==3.1.1 + # scos-usrp +oauthlib==3.2.0 # via # -r requirements.txt # requests-oauthlib @@ -189,41 +177,34 @@ packaging==21.3 # -r requirements.txt # drf-yasg # pytest + # tox paramiko==2.9.2 # via # -r requirements.txt # docker pathspec==0.9.0 # via black -pep517==0.12.0 - # via pip-tools -pip-tools==6.4.0 - # via -r requirements-dev.in -platformdirs==2.4.1 +platformdirs==2.5.1 # via # black # virtualenv -pluggy==0.13.1 +pluggy==1.0.0 # via # pytest # tox pre-commit==2.17.0 # via -r requirements-dev.in -psycopg2-binary==2.8.5 +psycopg2-binary==2.9.3 # via -r requirements.txt py==1.11.0 # via # pytest # tox -pycodestyle==2.5.0 - # via flake8 pycparser==2.21 # via # -r requirements.txt # cffi -pyflakes==2.1.1 - # via flake8 -pyjwt==1.7.1 +pyjwt==2.3.0 # via -r requirements.txt pynacl==1.5.0 # via @@ -237,16 +218,13 @@ pyrsistent==0.18.1 # via # -r requirements.txt # jsonschema -pytest==6.2.5 +pytest==7.0.1 # via # pytest-cov # pytest-django - # pytest-flake8 -pytest-cov==2.7.1 - # via -r requirements-dev.in -pytest-django==4.1.0 +pytest-cov==3.0.0 # via -r requirements-dev.in -pytest-flake8==1.0.4 +pytest-django==4.5.2 # via -r requirements-dev.in python-dateutil==2.8.2 # via -r requirements.txt @@ -265,8 +243,6 @@ pyyaml==5.4.1 # -r requirements.txt # docker-compose # pre-commit -raven==6.10.0 - # via -r requirements.txt requests==2.27.1 # via # -r requirements.txt @@ -276,29 +252,29 @@ requests==2.27.1 # requests-futures # requests-mock # requests-oauthlib -requests-futures==0.9.9 +requests-futures==1.0.0 # via -r requirements.txt -requests-mock==1.6.0 +requests-mock==1.9.3 # via -r requirements.txt requests-oauthlib==1.3.1 # via -r requirements.txt -ruamel.yaml==0.16.13 +ruamel-yaml==0.17.21 # via # -r requirements.txt # drf-yasg -ruamel.yaml.clib==0.2.6 +ruamel-yaml-clib==0.2.6 # via # -r requirements.txt - # ruamel.yaml + # ruamel-yaml scipy==1.7.3 # via -r requirements.txt -scos-actions @ git+https://github.com/NTIA/scos-actions@bug_fix +scos_actions @ git+https://github.com/NTIA/scos-actions@refactor_dependencies # via # -r requirements.txt # scos-usrp -scos-usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies # via -r requirements.txt -seed-isort-config==1.9.1 +seed-isort-config==2.2.0 # via -r requirements-dev.in sigmf @ git+https://github.com/NTIA/SigMF.git@multi-recording-archive # via -r requirements.txt @@ -307,7 +283,6 @@ six==1.16.0 # -r requirements.txt # bcrypt # django-session-timeout - # docker # dockerpty # jsonschema # python-dateutil @@ -327,17 +302,17 @@ texttable==1.6.4 toml==0.10.2 # via # pre-commit - # pytest # tox -tomli==1.2.3 +tomli==2.0.1 # via # black - # pep517 -tox==3.12.1 + # coverage + # pytest +tox==3.24.5 # via -r requirements-dev.in typed-ast==1.5.2 # via black -typing-extensions==4.0.1 +typing-extensions==4.1.1 # via # -r requirements.txt # asgiref @@ -352,7 +327,7 @@ urllib3==1.26.8 # via # -r requirements.txt # requests -virtualenv==20.13.0 +virtualenv==20.13.3 # via # pre-commit # tox @@ -361,14 +336,10 @@ websocket-client==0.59.0 # -r requirements.txt # docker # docker-compose -wheel==0.37.1 - # via pip-tools zipp==3.7.0 # via # -r requirements.txt # importlib-metadata - # pep517 # The following packages are considered to be unsafe in a requirements file: -# pip # setuptools diff --git a/src/requirements.in b/src/requirements.in index 6df1f656..6022a94f 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -1,21 +1,15 @@ -Django~=3.1.14 -django-debug-toolbar==3.0 -django-extensions~=3.1.5 -django-filter~=2.4.0 -djangorestframework~=3.13.1 -django-session-timeout==0.1.0 -docker-compose~=1.28.5 -drf-yasg~=1.20.0 -environs==7.3.1 -gunicorn==19.9.0 -jsonfield==3.1.0 -numpy~=1.21.0 -oauthlib~=3.1.0 -psycopg2-binary==2.8.5 -pyjwt~=1.7.1 -raven==6.10.0 -requests-futures==0.9.9 -requests-mock==1.6.0 -requests_oauthlib~=1.3.0 -#scos_actions @ git+https://github.com/NTIA/scos-actions@bug_fix#egg=scos_actions -scos_usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies#egg=scos_usrp +django>=3.0, <4.0 +djangorestframework>=3.0, <4.0 +django-session-timeout>=0.1, <1.0 +docker-compose>=1.0, <2.0 +drf-yasg>=1.0, <2.0 +environs>=9.0, <10.0 +gunicorn>=20.0, <21.0 +jsonfield>=3.0, <4.0 +oauthlib>=3.0, <4.0 +psycopg2-binary>=2.0, <3.0 +pyjwt>=2.0, <3.0 +requests-futures>=0.9.9 +requests-mock>=1.0, <2.0 +requests_oauthlib>=1.0, <2.0 +scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies diff --git a/src/requirements.txt b/src/requirements.txt index ea93af3b..1a9d1109 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -19,7 +19,7 @@ cffi==1.15.0 # bcrypt # cryptography # pynacl -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 # via requests coreapi==2.3.3 # via drf-yasg @@ -29,33 +29,26 @@ coreschema==0.0.4 # drf-yasg cryptography==36.0.1 # via paramiko -distro==1.6.0 +distro==1.7.0 # via docker-compose -django==3.1.14 +django==3.2.12 # via # -r requirements.in - # django-debug-toolbar - # django-extensions - # django-filter # django-session-timeout # djangorestframework # drf-yasg # jsonfield -django-debug-toolbar==3.0 - # via -r requirements.in -django-extensions==3.1.5 - # via -r requirements.in -django-filter==2.4.0 - # via -r requirements.in + # scos-actions + # scos-usrp django-session-timeout==0.1.0 # via -r requirements.in djangorestframework==3.13.1 # via # -r requirements.in # drf-yasg -docker[ssh]==4.4.4 +docker[ssh]==5.0.3 # via docker-compose -docker-compose==1.28.6 +docker-compose==1.29.2 # via -r requirements.in dockerpty==0.4.1 # via docker-compose @@ -63,13 +56,15 @@ docopt==0.6.2 # via docker-compose drf-yasg==1.20.0 # via -r requirements.in -environs==7.3.1 - # via -r requirements.in -gunicorn==19.9.0 +environs==9.5.0 + # via + # -r requirements.in + # scos-usrp +gunicorn==20.1.0 # via -r requirements.in idna==3.3 # via requests -importlib-metadata==4.10.1 +importlib-metadata==4.11.2 # via jsonschema inflection==0.5.1 # via drf-yasg @@ -81,16 +76,17 @@ jsonfield==3.1.0 # via -r requirements.in jsonschema==3.2.0 # via docker-compose -markupsafe==2.0.1 +markupsafe==2.1.0 # via jinja2 marshmallow==3.14.1 # via environs numpy==1.21.5 # via - # -r requirements.in # scipy + # scos-actions + # scos-usrp # sigmf -oauthlib==3.1.1 +oauthlib==3.2.0 # via # -r requirements.in # requests-oauthlib @@ -98,11 +94,11 @@ packaging==21.3 # via drf-yasg paramiko==2.9.2 # via docker -psycopg2-binary==2.8.5 +psycopg2-binary==2.9.3 # via -r requirements.in pycparser==2.21 # via cffi -pyjwt==1.7.1 +pyjwt==2.3.0 # via -r requirements.in pynacl==1.5.0 # via paramiko @@ -122,8 +118,6 @@ pytz==2021.3 # djangorestframework pyyaml==5.4.1 # via docker-compose -raven==6.10.0 - # via -r requirements.in requests==2.27.1 # via # coreapi @@ -132,25 +126,23 @@ requests==2.27.1 # requests-futures # requests-mock # requests-oauthlib -requests-futures==0.9.9 +requests-futures==1.0.0 # via -r requirements.in -requests-mock==1.6.0 +requests-mock==1.9.3 # via -r requirements.in requests-oauthlib==1.3.1 # via -r requirements.in -ruamel.yaml==0.16.13 +ruamel-yaml==0.17.21 # via # drf-yasg # scos-actions -ruamel.yaml.clib==0.2.6 - # via - # ruamel.yaml - # scos-actions +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml scipy==1.7.3 # via scos-actions -scos-actions @ git+https://github.com/NTIA/scos-actions@bug_fix +scos_actions @ git+https://github.com/NTIA/scos-actions@refactor_dependencies # via scos-usrp -scos-usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies # via -r requirements.in sigmf @ git+https://github.com/NTIA/SigMF.git@multi-recording-archive # via scos-actions @@ -158,7 +150,6 @@ six==1.16.0 # via # bcrypt # django-session-timeout - # docker # dockerpty # jsonschema # python-dateutil @@ -166,12 +157,10 @@ six==1.16.0 # sigmf # websocket-client sqlparse==0.4.2 - # via - # django - # django-debug-toolbar + # via django texttable==1.6.4 # via docker-compose -typing-extensions==4.0.1 +typing-extensions==4.1.1 # via # asgiref # importlib-metadata diff --git a/src/sensor/settings.py b/src/sensor/settings.py index 4208e606..26acff2b 100644 --- a/src/sensor/settings.py +++ b/src/sensor/settings.py @@ -160,13 +160,9 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "django_extensions", - "django_filters", "rest_framework", "rest_framework.authtoken", "drf_yasg", # OpenAPI generator - "raven.contrib.django.raven_compat", - "debug_toolbar", # project-local apps "authentication.apps.AuthenticationConfig", "capabilities.apps.CapabilitiesConfig", @@ -179,7 +175,6 @@ ] MIDDLEWARE = [ - "debug_toolbar.middleware.DebugToolbarMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django_session_timeout.middleware.SessionTimeoutMiddleware", @@ -190,6 +185,16 @@ "django.middleware.clickjacking.XFrameOptionsMiddleware", ] +if DEBUG: + INSTALLED_APPS.extend( + [ + "debug_toolbar", + "django_extensions", + ] + ) + + MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") + ROOT_URLCONF = "sensor.urls" TEMPLATES = [ @@ -369,11 +374,6 @@ }, } -SENTRY_DSN = env("SENTRY_DSN", default="") -if SENTRY_DSN: - import raven - - RAVEN_CONFIG = {"dsn": SENTRY_DSN, "release": raven.fetch_git_sha(REPO_ROOT)} CALLBACK_SSL_VERIFICATION = env.bool("CALLBACK_SSL_VERIFICATION", default=True) # OAuth Password Flow Authentication diff --git a/src/tox.ini b/src/tox.ini index 6c663e70..75ae89fb 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -34,12 +34,6 @@ DJANGO_SETTINGS_MODULE = sensor.settings ; log_cli = true ; log_cli_level = DEBUG -[flake8] -max-line-length = 80 -max-complexity = 12 -select = C,E,F,W,B,B950 -ignore = E501 - [coverage:run] omit = .tox/* From e7d676ac1acfe9bab0a3e8258a1d781c0e5c925f Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Tue, 8 Mar 2022 14:26:21 -0700 Subject: [PATCH 03/20] update static files --- src/static/admin/css/autocomplete.css | 57 ++- src/static/admin/css/base.css | 272 ++++++---- src/static/admin/css/changelists.css | 92 ++-- src/static/admin/css/forms.css | 58 +-- src/static/admin/css/login.css | 33 +- src/static/admin/css/nav_sidebar.css | 21 +- src/static/admin/css/responsive.css | 33 +- src/static/admin/css/rtl.css | 24 +- src/static/admin/css/widgets.css | 76 +-- src/static/admin/js/SelectBox.js | 4 +- src/static/admin/js/actions.js | 313 +++++++----- src/static/admin/js/actions.min.js | 7 - .../admin/js/admin/DateTimeShortcuts.js | 6 +- .../admin/js/admin/RelatedObjectLookups.js | 8 +- src/static/admin/js/autocomplete.js | 5 +- src/static/admin/js/calendar.js | 14 + src/static/admin/js/cancel.js | 7 +- src/static/admin/js/collapse.min.js | 2 - src/static/admin/js/core.js | 7 + src/static/admin/js/inlines.min.js | 11 - src/static/admin/js/prepopulate.min.js | 1 - src/static/admin/js/urlify.js | 17 +- src/static/debug_toolbar/css/toolbar.css | 293 ++++++----- src/static/debug_toolbar/img/ajax-loader.gif | Bin 404 -> 0 bytes src/static/debug_toolbar/img/indicator.png | Bin 436 -> 0 bytes src/static/debug_toolbar/js/history.js | 51 ++ src/static/debug_toolbar/js/redirect.js | 2 +- src/static/debug_toolbar/js/timer.js | 75 +++ src/static/debug_toolbar/js/toolbar.js | 472 ++++++++---------- src/static/debug_toolbar/js/toolbar.timer.js | 41 -- src/static/debug_toolbar/js/utils.js | 107 ++++ 31 files changed, 1200 insertions(+), 909 deletions(-) delete mode 100644 src/static/admin/js/actions.min.js delete mode 100644 src/static/admin/js/collapse.min.js delete mode 100644 src/static/admin/js/inlines.min.js delete mode 100644 src/static/admin/js/prepopulate.min.js delete mode 100644 src/static/debug_toolbar/img/ajax-loader.gif delete mode 100644 src/static/debug_toolbar/img/indicator.png create mode 100644 src/static/debug_toolbar/js/history.js create mode 100644 src/static/debug_toolbar/js/timer.js delete mode 100644 src/static/debug_toolbar/js/toolbar.timer.js create mode 100644 src/static/debug_toolbar/js/utils.js diff --git a/src/static/admin/css/autocomplete.css b/src/static/admin/css/autocomplete.css index 3ef95d15..69c94e73 100644 --- a/src/static/admin/css/autocomplete.css +++ b/src/static/admin/css/autocomplete.css @@ -14,7 +14,7 @@ select.admin-autocomplete { .select2-container--admin-autocomplete.select2-container--focus .select2-selection, .select2-container--admin-autocomplete.select2-container--open .select2-selection { - border-color: #999; + border-color: var(--body-quiet-color); min-height: 30px; } @@ -29,13 +29,13 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--single { - background-color: #fff; - border: 1px solid #ccc; + background-color: var(--body-bg); + border: 1px solid var(--border-color); border-radius: 4px; } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered { - color: #444; + color: var(--body-fg); line-height: 30px; } @@ -46,7 +46,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder { - color: #999; + color: var(--body-quiet-color); } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow { @@ -80,7 +80,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single { - background-color: #eee; + background-color: var(--darkened-bg); cursor: default; } @@ -94,8 +94,8 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple { - background-color: white; - border: 1px solid #ccc; + background-color: var(--body-bg); + border: 1px solid var(--border-color); border-radius: 4px; cursor: text; } @@ -104,8 +104,10 @@ select.admin-autocomplete { box-sizing: border-box; list-style: none; margin: 0; - padding: 0 5px; + padding: 0 10px 5px 5px; width: 100%; + display: flex; + flex-wrap: wrap; } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li { @@ -113,7 +115,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder { - color: #999; + color: var(--body-quiet-color); margin-top: 5px; float: left; } @@ -123,11 +125,13 @@ select.admin-autocomplete { float: right; font-weight: bold; margin: 5px; + position: absolute; + right: 0; } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice { - background-color: #e4e4e4; - border: 1px solid #ccc; + background-color: var(--darkened-bg); + border: 1px solid var(--border-color); border-radius: 4px; cursor: default; float: left; @@ -137,7 +141,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove { - color: #999; + color: var(--body-quiet-color); cursor: pointer; display: inline-block; font-weight: bold; @@ -145,7 +149,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover { - color: #333; + color: var(--body-fg); } .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline { @@ -163,12 +167,12 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple { - border: solid #999 1px; + border: solid var(--body-quiet-color) 1px; outline: 0; } .select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple { - background-color: #eee; + background-color: var(--darkened-bg); cursor: default; } @@ -186,12 +190,20 @@ select.admin-autocomplete { border-bottom-right-radius: 0; } +.select2-container--admin-autocomplete .select2-search--dropdown { + background: var(--darkened-bg); +} + .select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field { - border: 1px solid #ccc; + background: var(--body-bg); + color: var(--body-fg); + border: 1px solid var(--border-color); + border-radius: 4px; } .select2-container--admin-autocomplete .select2-search--inline .select2-search__field { background: transparent; + color: var(--body-fg); border: none; outline: 0; box-shadow: none; @@ -201,6 +213,8 @@ select.admin-autocomplete { .select2-container--admin-autocomplete .select2-results > .select2-results__options { max-height: 200px; overflow-y: auto; + color: var(--body-fg); + background: var(--body-bg); } .select2-container--admin-autocomplete .select2-results__option[role=group] { @@ -208,11 +222,12 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] { - color: #999; + color: var(--body-quiet-color); } .select2-container--admin-autocomplete .select2-results__option[aria-selected=true] { - background-color: #ddd; + background-color: var(--selected-bg); + color: var(--body-fg); } .select2-container--admin-autocomplete .select2-results__option .select2-results__option { @@ -249,8 +264,8 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] { - background-color: #79aec8; - color: white; + background-color: var(--primary); + color: var(--primary-fg); } .select2-container--admin-autocomplete .select2-results__group { diff --git a/src/static/admin/css/base.css b/src/static/admin/css/base.css index c4285195..1cb3acdb 100644 --- a/src/static/admin/css/base.css +++ b/src/static/admin/css/base.css @@ -4,6 +4,93 @@ @import url(fonts.css); +/* VARIABLE DEFINITIONS */ +:root { + --primary: #79aec8; + --secondary: #417690; + --accent: #f5dd5d; + --primary-fg: #fff; + + --body-fg: #333; + --body-bg: #fff; + --body-quiet-color: #666; + --body-loud-color: #000; + + --header-color: #ffc; + --header-branding-color: var(--accent); + --header-bg: var(--secondary); + --header-link-color: var(--primary-fg); + + --breadcrumbs-fg: #c4dce8; + --breadcrumbs-link-fg: var(--body-bg); + --breadcrumbs-bg: var(--primary); + + --link-fg: #447e9b; + --link-hover-color: #036; + --link-selected-fg: #5b80b2; + + --hairline-color: #e8e8e8; + --border-color: #ccc; + + --error-fg: #ba2121; + + --message-success-bg: #dfd; + --message-warning-bg: #ffc; + --message-error-bg: #ffefef; + + --darkened-bg: #f8f8f8; /* A bit darker than --body-bg */ + --selected-bg: #e4e4e4; /* E.g. selected table cells */ + --selected-row: #ffc; + + --button-fg: #fff; + --button-bg: var(--primary); + --button-hover-bg: #609ab6; + --default-button-bg: var(--secondary); + --default-button-hover-bg: #205067; + --close-button-bg: #888; /* Previously #bbb, contrast 1.92 */ + --close-button-hover-bg: #747474; + --delete-button-bg: #ba2121; + --delete-button-hover-bg: #a41515; + + --object-tools-fg: var(--button-fg); + --object-tools-bg: var(--close-button-bg); + --object-tools-hover-bg: var(--close-button-hover-bg); +} + +@media (prefers-color-scheme: dark) { + :root { + --primary: #264b5d; + --primary-fg: #eee; + + --body-fg: #eeeeee; + --body-bg: #121212; + --body-quiet-color: #e0e0e0; + --body-loud-color: #ffffff; + + --breadcrumbs-link-fg: #e0e0e0; + --breadcrumbs-bg: var(--primary); + + --link-fg: #81d4fa; + --link-hover-color: #4ac1f7; + --link-selected-fg: #6f94c6; + + --hairline-color: #272727; + --border-color: #353535; + + --error-fg: #e35f5f; + --message-success-bg: #006b1b; + --message-warning-bg: #583305; + --message-error-bg: #570808; + + --darkened-bg: #212121; + --selected-bg: #1b1b1b; + --selected-row: #00363a; + + --close-button-bg: #333333; + --close-button-hover-bg: #666666; + } +} + html, body { height: 100%; } @@ -13,19 +100,20 @@ body { padding: 0; font-size: 14px; font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; - color: #333; - background: #fff; + color: var(--body-fg); + background: var(--body-bg); } /* LINKS */ a:link, a:visited { - color: #447e9b; + color: var(--link-fg); text-decoration: none; + transition: color 0.15s, background 0.15s; } a:focus, a:hover { - color: #036; + color: var(--link-hover-color); } a:focus { @@ -37,7 +125,7 @@ a img { } a.section:link, a.section:visited { - color: #fff; + color: var(--header-link-color); text-decoration: none; } @@ -64,7 +152,7 @@ h1 { margin: 0 0 20px; font-weight: 300; font-size: 20px; - color: #666; + color: var(--body-quiet-color); } h2 { @@ -80,7 +168,7 @@ h2.subhead { h3 { font-size: 14px; margin: .8em 0 .3em 0; - color: #666; + color: var(--body-quiet-color); font-weight: bold; } @@ -93,7 +181,7 @@ h4 { h5 { font-size: 10px; margin: 1.5em 0 .5em 0; - color: #666; + color: var(--body-quiet-color); text-transform: uppercase; letter-spacing: 1px; } @@ -131,7 +219,7 @@ fieldset { min-width: 0; padding: 0; border: none; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } blockquote { @@ -144,14 +232,14 @@ blockquote { code, pre { font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; - color: #666; + color: var(--body-quiet-color); font-size: 12px; overflow-x: auto; } pre.literal-block { margin: 10px; - background: #eee; + background: var(--darkened-bg); padding: 6px 8px; } @@ -161,8 +249,8 @@ code strong { hr { clear: both; - color: #eee; - background-color: #eee; + color: var(--hairline-color); + background-color: var(--hairline-color); height: 1px; border: none; margin: 0; @@ -183,7 +271,7 @@ hr { .help, p.help, form p.help, div.help, form div.help, div.help li { font-size: 11px; - color: #999; + color: var(--body-quiet-color); } div.help ul { @@ -199,7 +287,7 @@ p img, h1 img, h2 img, h3 img, h4 img, td img { } .quiet, a.quiet:link, a.quiet:visited { - color: #999; + color: var(--body-quiet-color); font-weight: normal; } @@ -211,20 +299,23 @@ p img, h1 img, h2 img, h3 img, h4 img, td img { white-space: nowrap; } +.hidden { + display: none; +} + /* TABLES */ table { border-collapse: collapse; - border-color: #ccc; + border-color: var(--border-color); } td, th { font-size: 13px; line-height: 16px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); vertical-align: top; padding: 8px; - font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; } th { @@ -234,37 +325,37 @@ th { thead th, tfoot td { - color: #666; + color: var(--body-quiet-color); padding: 5px 10px; font-size: 11px; - background: #fff; + background: var(--body-bg); border: none; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); } tfoot td { border-bottom: none; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } thead th.required { - color: #000; + color: var(--body-loud-color); } tr.alt { - background: #f6f6f6; + background: var(--darkened-bg); } tr:nth-child(odd), .row-form-errors { - background: #fff; + background: var(--body-bg); } tr:nth-child(even), tr:nth-child(even) .errorlist, tr:nth-child(odd) + .row-form-errors, tr:nth-child(odd) + .row-form-errors .errorlist { - background: #f9f9f9; + background: var(--darkened-bg); } /* SORTABLE TABLES */ @@ -273,15 +364,15 @@ thead th { padding: 5px 10px; line-height: normal; text-transform: uppercase; - background: #f6f6f6; + background: var(--darkened-bg); } thead th a:link, thead th a:visited { - color: #666; + color: var(--body-quiet-color); } thead th.sorted { - background: #eee; + background: var(--selected-bg); } thead th.sorted .text { @@ -300,7 +391,7 @@ table thead th .text a { } table thead th .text a:focus, table thead th .text a:hover { - background: #eee; + background: var(--selected-bg); } thead th.sorted a.sortremove { @@ -347,12 +438,12 @@ table thead th.sorted .sortoptions a.sortremove:after { left: 3px; font-weight: 200; font-size: 18px; - color: #999; + color: var(--body-quiet-color); } table thead th.sorted .sortoptions a.sortremove:focus:after, table thead th.sorted .sortoptions a.sortremove:hover:after { - color: #447e9b; + color: var(--link-fg); } table thead th.sorted .sortoptions a.sortremove:focus, @@ -399,16 +490,18 @@ textarea { input[type=text], input[type=password], input[type=email], input[type=url], input[type=number], input[type=tel], textarea, select, .vTextField { - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px; padding: 5px 6px; margin-top: 0; + color: var(--body-fg); + background-color: var(--body-bg); } input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input[type=url]:focus, input[type=number]:focus, input[type=tel]:focus, textarea:focus, select:focus, .vTextField:focus { - border-color: #999; + border-color: var(--body-quiet-color); } select { @@ -424,12 +517,13 @@ select[multiple] { /* FORM BUTTONS */ .button, input[type=submit], input[type=button], .submit-row input, a.button { - background: #79aec8; + background: var(--button-bg); padding: 10px 15px; border: none; border-radius: 4px; - color: #fff; + color: var(--button-fg); cursor: pointer; + transition: background 0.15s; } a.button { @@ -439,7 +533,7 @@ a.button { .button:active, input[type=submit]:active, input[type=button]:active, .button:focus, input[type=submit]:focus, input[type=button]:focus, .button:hover, input[type=submit]:hover, input[type=button]:hover { - background: #609ab6; + background: var(--button-hover-bg); } .button[disabled], input[type=submit][disabled], input[type=button][disabled] { @@ -450,13 +544,13 @@ a.button { float: right; border: none; font-weight: 400; - background: #417690; + background: var(--default-button-bg); } .button.default:active, input[type=submit].default:active, .button.default:focus, input[type=submit].default:focus, .button.default:hover, input[type=submit].default:hover { - background: #205067; + background: var(--default-button-hover-bg); } .button[disabled].default, @@ -471,7 +565,7 @@ input[type=button][disabled].default { .module { border: none; margin-bottom: 30px; - background: #fff; + background: var(--body-bg); } .module p, .module ul, .module h3, .module h4, .module dl, .module pre { @@ -497,8 +591,8 @@ input[type=button][disabled].default { font-weight: 400; font-size: 13px; text-align: left; - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--header-link-color); } .module caption, @@ -525,18 +619,18 @@ ul.messagelist li { font-size: 13px; padding: 10px 10px 10px 65px; margin: 0 0 10px 0; - background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat; + background: var(--message-success-bg) url(../img/icon-yes.svg) 40px 12px no-repeat; background-size: 16px auto; - color: #333; + color: var(--body-fg); } ul.messagelist li.warning { - background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat; + background: var(--message-warning-bg) url(../img/icon-alert.svg) 40px 14px no-repeat; background-size: 14px auto; } ul.messagelist li.error { - background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat; + background: var(--message-error-bg) url(../img/icon-no.svg) 40px 12px no-repeat; background-size: 16px auto; } @@ -546,24 +640,26 @@ ul.messagelist li.error { display: block; padding: 10px 12px; margin: 0 0 10px 0; - color: #ba2121; - border: 1px solid #ba2121; + color: var(--error-fg); + border: 1px solid var(--error-fg); border-radius: 4px; - background-color: #fff; + background-color: var(--body-bg); background-position: 5px 12px; + overflow-wrap: break-word; } ul.errorlist { margin: 0 0 4px; padding: 0; - color: #ba2121; - background: #fff; + color: var(--error-fg); + background: var(--body-bg); } ul.errorlist li { font-size: 13px; display: block; margin-bottom: 4px; + overflow-wrap: break-word; } ul.errorlist li:first-child { @@ -587,7 +683,7 @@ td ul.errorlist li { .form-row.errors { margin: 0; border: none; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); background: none; } @@ -597,7 +693,7 @@ td ul.errorlist li { .errors input, .errors select, .errors textarea, td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea { - border: 1px solid #ba2121; + border: 1px solid var(--error-fg); } .description { @@ -608,20 +704,19 @@ td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea { /* BREADCRUMBS */ div.breadcrumbs { - background: #79aec8; + background: var(--breadcrumbs-bg); padding: 10px 40px; border: none; - font-size: 14px; - color: #c4dce8; + color: var(--breadcrumbs-fg); text-align: left; } div.breadcrumbs a { - color: #fff; + color: var(--breadcrumbs-link-fg); } div.breadcrumbs a:focus, div.breadcrumbs a:hover { - color: #c4dce8; + color: var(--breadcrumbs-fg); } /* ACTION ICONS */ @@ -647,11 +742,11 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover { } a.deletelink:link, a.deletelink:visited { - color: #CC3434; + color: #CC3434; /* XXX Probably unused? */ } a.deletelink:focus, a.deletelink:hover { - color: #993333; + color: #993333; /* XXX Probably unused? */ text-decoration: none; } @@ -666,14 +761,6 @@ a.deletelink:focus, a.deletelink:hover { margin-top: -48px; } -.form-row .object-tools { - margin-top: 5px; - margin-bottom: 5px; - float: none; - height: 2em; - padding-left: 3.5em; -} - .object-tools li { display: block; float: left; @@ -689,29 +776,29 @@ a.deletelink:focus, a.deletelink:hover { display: block; float: left; padding: 3px 12px; - background: #999; + background: var(--object-tools-bg); + color: var(--object-tools-fg); font-weight: 400; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; - color: #fff; } .object-tools a:focus, .object-tools a:hover { - background-color: #417690; + background-color: var(--object-tools-hover-bg); } .object-tools a:focus{ text-decoration: none; } -.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink { +.object-tools a.viewsitelink, .object-tools a.addlink { background-repeat: no-repeat; background-position: right 7px center; padding-right: 26px; } -.object-tools a.viewsitelink, .object-tools a.golink { +.object-tools a.viewsitelink { background-image: url(../img/tooltag-arrowright.svg); } @@ -813,13 +900,13 @@ table#change-history tbody th { justify-content: space-between; align-items: center; padding: 10px 40px; - background: #417690; - color: #ffc; + background: var(--header-bg); + color: var(--header-color); overflow: hidden; } #header a:link, #header a:visited { - color: #fff; + color: var(--header-link-color); } #header a:focus , #header a:hover { @@ -835,11 +922,11 @@ table#change-history tbody th { margin: 0 20px 0 0; font-weight: 300; font-size: 24px; - color: #f5dd5d; + color: var(--accent); } #branding h1, #branding h1 a:link, #branding h1 a:visited { - color: #f5dd5d; + color: var(--accent); } #branding h2 { @@ -847,7 +934,7 @@ table#change-history tbody th { font-size: 14px; margin: -8px 0 8px 0; font-weight: normal; - color: #ffc; + color: var(--header-color); } #branding a:hover { @@ -871,14 +958,14 @@ table#change-history tbody th { #user-tools a:focus, #user-tools a:hover { text-decoration: none; - border-bottom-color: #79aec8; - color: #79aec8; + border-bottom-color: var(--primary); + color: var(--primary); } /* SIDEBAR */ #content-related { - background: #f8f8f8; + background: var(--darkened-bg); } #content-related .module { @@ -886,8 +973,7 @@ table#change-history tbody th { } #content-related h3 { - font-size: 14px; - color: #666; + color: var(--body-quiet-color); padding: 0 16px; margin: 0 0 16px; } @@ -916,22 +1002,22 @@ table#change-history tbody th { background: none; padding: 16px; margin-bottom: 16px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); font-size: 18px; - color: #333; + color: var(--body-fg); } .delete-confirmation form input[type="submit"] { - background: #ba2121; + background: var(--delete-button-bg); border-radius: 4px; padding: 10px 15px; - color: #fff; + color: var(--button-fg); } .delete-confirmation form input[type="submit"]:active, .delete-confirmation form input[type="submit"]:focus, .delete-confirmation form input[type="submit"]:hover { - background: #a41515; + background: var(--delete-button-hover-bg); } .delete-confirmation form .cancel-link { @@ -939,17 +1025,17 @@ table#change-history tbody th { vertical-align: middle; height: 15px; line-height: 15px; - background: #ddd; border-radius: 4px; padding: 10px 15px; - color: #333; + color: var(--button-fg); + background: var(--close-button-bg); margin: 0 0 0 10px; } .delete-confirmation form .cancel-link:active, .delete-confirmation form .cancel-link:focus, .delete-confirmation form .cancel-link:hover { - background: #ccc; + background: var(--close-button-hover-bg); } /* POPUP */ diff --git a/src/static/admin/css/changelists.css b/src/static/admin/css/changelists.css index a1642540..b4a15578 100644 --- a/src/static/admin/css/changelists.css +++ b/src/static/admin/css/changelists.css @@ -40,13 +40,13 @@ } #changelist .toplinks { - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--hairline-color); } #changelist .paginator { - color: #666; - border-bottom: 1px solid #eee; - background: #fff; + color: var(--body-quiet-color); + border-bottom: 1px solid var(--hairline-color); + background: var(--body-bg); overflow: hidden; } @@ -68,7 +68,7 @@ } #changelist table tfoot { - color: #666; + color: var(--body-quiet-color); } /* TOOLBAR */ @@ -76,22 +76,22 @@ #toolbar { padding: 8px 10px; margin-bottom: 15px; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; - background: #f8f8f8; - color: #666; + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); + background: var(--darkened-bg); + color: var(--body-quiet-color); } #toolbar form input { border-radius: 4px; font-size: 14px; padding: 5px; - color: #333; + color: var(--body-fg); } #toolbar #searchbar { height: 19px; - border: 1px solid #ccc; + border: 1px solid var(--border-color); padding: 2px 5px; margin: 0; vertical-align: top; @@ -100,24 +100,24 @@ } #toolbar #searchbar:focus { - border-color: #999; + border-color: var(--body-quiet-color); } #toolbar form input[type="submit"] { - border: 1px solid #ccc; + border: 1px solid var(--border-color); font-size: 13px; padding: 4px 8px; margin: 0; vertical-align: middle; - background: #fff; + background: var(--body-bg); box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; cursor: pointer; - color: #333; + color: var(--body-fg); } #toolbar form input[type="submit"]:focus, #toolbar form input[type="submit"]:hover { - border-color: #999; + border-color: var(--body-quiet-color); } #changelist-search img { @@ -130,8 +130,7 @@ #changelist-filter { flex: 0 0 240px; order: 1; - width: 240px; - background: #f8f8f8; + background: var(--darkened-bg); border-left: none; margin: 0 0 0 30px; } @@ -147,7 +146,6 @@ #changelist-filter h3 { font-weight: 400; - font-size: 14px; padding: 0 15px; margin-bottom: 10px; } @@ -155,7 +153,7 @@ #changelist-filter ul { margin: 5px 0; padding: 0 15px 15px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); } #changelist-filter ul:last-child { @@ -170,31 +168,31 @@ #changelist-filter a { display: block; - color: #999; + color: var(--body-quiet-color); text-overflow: ellipsis; overflow-x: hidden; } #changelist-filter li.selected { - border-left: 5px solid #eaeaea; + border-left: 5px solid var(--hairline-color); padding-left: 10px; margin-left: -15px; } #changelist-filter li.selected a { - color: #5b80b2; + color: var(--link-selected-fg); } #changelist-filter a:focus, #changelist-filter a:hover, #changelist-filter li.selected a:focus, #changelist-filter li.selected a:hover { - color: #036; + color: var(--link-hover-color); } #changelist-filter #changelist-filter-clear a { font-size: 13px; padding-bottom: 10px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); } /* DATE DRILLDOWN */ @@ -215,12 +213,12 @@ } .change-list ul.toplinks .date-back a { - color: #999; + color: var(--body-quiet-color); } .change-list ul.toplinks .date-back a:focus, .change-list ul.toplinks .date-back a:hover { - color: #036; + color: var(--link-hover-color); } /* PAGINATOR */ @@ -231,26 +229,26 @@ padding-bottom: 10px; line-height: 22px; margin: 0; - border-top: 1px solid #ddd; + border-top: 1px solid var(--hairline-color); width: 100%; } .paginator a:link, .paginator a:visited { padding: 2px 6px; - background: #79aec8; + background: var(--button-bg); text-decoration: none; - color: #fff; + color: var(--button-fg); } .paginator a.showall { border: none; background: none; - color: #5b80b2; + color: var(--link-fg); } .paginator a.showall:focus, .paginator a.showall:hover { background: none; - color: #036; + color: var(--link-hover-color); } .paginator .end { @@ -266,7 +264,7 @@ .paginator a:focus, .paginator a:hover { color: white; - background: #036; + background: var(--link-hover-color); } /* ACTIONS */ @@ -281,22 +279,22 @@ } #changelist table tbody tr.selected { - background-color: #FFFFCC; + background-color: var(--selected-row); } #changelist .actions { padding: 10px; - background: #fff; + background: var(--body-bg); border-top: none; border-bottom: none; line-height: 24px; - color: #999; + color: var(--body-quiet-color); width: 100%; } -#changelist .actions.selected { - background: #fffccf; - border-top: 1px solid #fffee8; +#changelist .actions.selected { /* XXX Probably unused? */ + background: var(--body-bg); + border-top: 1px solid var(--body-bg); border-bottom: 1px solid #edecd6; } @@ -306,7 +304,6 @@ #changelist .actions span.question { font-size: 13px; margin: 0 0.5em; - display: none; } #changelist .actions:last-child { @@ -316,9 +313,8 @@ #changelist .actions select { vertical-align: top; height: 24px; - background: none; - color: #000; - border: 1px solid #ccc; + color: var(--body-fg); + border: 1px solid var(--border-color); border-radius: 4px; font-size: 14px; padding: 0 0 0 4px; @@ -327,7 +323,7 @@ } #changelist .actions select:focus { - border-color: #999; + border-color: var(--body-quiet-color); } #changelist .actions label { @@ -338,18 +334,18 @@ #changelist .actions .button { font-size: 13px; - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px; - background: #fff; + background: var(--body-bg); box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; cursor: pointer; height: 24px; line-height: 1; padding: 4px 8px; margin: 0; - color: #333; + color: var(--body-fg); } #changelist .actions .button:focus, #changelist .actions .button:hover { - border-color: #999; + border-color: var(--body-quiet-color); } diff --git a/src/static/admin/css/forms.css b/src/static/admin/css/forms.css index 89d57482..89b22701 100644 --- a/src/static/admin/css/forms.css +++ b/src/static/admin/css/forms.css @@ -6,7 +6,7 @@ overflow: hidden; padding: 10px; font-size: 13px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .form-row img, .form-row input { @@ -22,21 +22,17 @@ form .form-row p { padding-left: 0; } -.hidden { - display: none; -} - /* FORM LABELS */ label { font-weight: normal; - color: #666; + color: var(--body-quiet-color); font-size: 13px; } .required label, label.required { font-weight: bold; - color: #333; + color: var(--body-fg); } /* RADIO BUTTONS */ @@ -219,24 +215,24 @@ fieldset.collapsed h2, fieldset.collapsed { } fieldset.collapsed { - border: 1px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; overflow: hidden; } fieldset.collapsed h2 { - background: #f8f8f8; - color: #666; + background: var(--darkened-bg); + color: var(--body-quiet-color); } fieldset .collapse-toggle { - color: #fff; + color: var(--header-link-color); } fieldset.collapsed .collapse-toggle { background: transparent; display: inline; - color: #447e9b; + color: var(--link-fg); } /* MONOSPACE TEXTAREAS */ @@ -250,8 +246,8 @@ fieldset.monospace textarea { .submit-row { padding: 12px 14px; margin: 0 0 20px; - background: #f8f8f8; - border: 1px solid #eee; + background: var(--darkened-bg); + border: 1px solid var(--hairline-color); border-radius: 4px; text-align: right; overflow: hidden; @@ -283,35 +279,35 @@ body.popup .submit-row { .submit-row a.deletelink { display: block; - background: #ba2121; + background: var(--delete-button-bg); border-radius: 4px; padding: 10px 15px; height: 15px; line-height: 15px; - color: #fff; + color: var(--button-fg); } .submit-row a.closelink { display: inline-block; - background: #bbbbbb; + background: var(--close-button-bg); border-radius: 4px; padding: 10px 15px; height: 15px; line-height: 15px; margin: 0 0 0 5px; - color: #fff; + color: var(--button-fg); } .submit-row a.deletelink:focus, .submit-row a.deletelink:hover, .submit-row a.deletelink:active { - background: #a41515; + background: var(--delete-button-hover-bg); } .submit-row a.closelink:focus, .submit-row a.closelink:hover, .submit-row a.closelink:active { - background: #aaaaaa; + background: var(--close-button-hover-bg); } /* CUSTOM FORM FIELDS */ @@ -390,12 +386,12 @@ body.popup .submit-row { .inline-related h3 { margin: 0; - color: #666; + color: var(--body-quiet-color); padding: 5px; font-size: 13px; - background: #f8f8f8; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + background: var(--darkened-bg); + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); } .inline-related h3 span.delete { @@ -409,7 +405,7 @@ body.popup .submit-row { .inline-related fieldset { margin: 0; - background: #fff; + background: var(--body-bg); border: none; width: 100%; } @@ -421,7 +417,7 @@ body.popup .submit-row { text-align: left; font-weight: bold; background: #bcd; - color: #fff; + color: var(--body-bg); } .inline-group .tabular fieldset.module { @@ -460,7 +456,7 @@ body.popup .submit-row { overflow: hidden; font-size: 9px; font-weight: bold; - color: #666; + color: var(--body-quiet-color); _width: 700px; } @@ -477,15 +473,15 @@ body.popup .submit-row { .inline-group div.add-row, .inline-group .tabular tr.add-row td { - color: #666; - background: #f8f8f8; + color: var(--body-quiet-color); + background: var(--darkened-bg); padding: 8px 10px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .inline-group .tabular tr.add-row td { padding: 8px 10px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .inline-group ul.tools a.add, diff --git a/src/static/admin/css/login.css b/src/static/admin/css/login.css index 062b36e0..10d9d220 100644 --- a/src/static/admin/css/login.css +++ b/src/static/admin/css/login.css @@ -1,7 +1,7 @@ /* LOGIN FORM */ .login { - background: #f8f8f8; + background: var(--darkened-bg); height: auto; } @@ -16,7 +16,7 @@ } .login #header h1 a { - color: #fff; + color: var(--header-link-color); } .login #content { @@ -24,8 +24,8 @@ } .login #container { - background: #fff; - border: 1px solid #eaeaea; + background: var(--body-bg); + border: 1px solid var(--hairline-color); border-radius: 4px; overflow: hidden; width: 28em; @@ -34,44 +34,25 @@ height: auto; } -.login #content-main { - width: 100%; -} - .login .form-row { padding: 4px 0; - float: left; - width: 100%; - border-bottom: none; } .login .form-row label { - padding-right: 0.5em; + display: block; line-height: 2em; - font-size: 1em; - clear: both; - color: #333; } .login .form-row #id_username, .login .form-row #id_password { - clear: both; padding: 8px; width: 100%; box-sizing: border-box; } -.login span.help { - font-size: 10px; - display: block; -} - .login .submit-row { - clear: both; - padding: 1em 0 0 9.4em; + padding: 1em 0 0 0; margin: 0; - border: none; - background: none; - text-align: left; + text-align: center; } .login .password-reset-link { diff --git a/src/static/admin/css/nav_sidebar.css b/src/static/admin/css/nav_sidebar.css index 784d0874..f3c2fd80 100644 --- a/src/static/admin/css/nav_sidebar.css +++ b/src/static/admin/css/nav_sidebar.css @@ -12,22 +12,23 @@ justify-content: center; flex: 0 0 23px; width: 23px; - border-right: 1px solid #eaeaea; - background-color: #ffffff; + border: 0; + border-right: 1px solid var(--hairline-color); + background-color: var(--body-bg); cursor: pointer; font-size: 20px; - color: #447e9b; + color: var(--link-fg); padding: 0; } [dir="rtl"] .toggle-nav-sidebar { - border-left: 1px solid #eaeaea; + border-left: 1px solid var(--hairline-color); border-right: 0; } .toggle-nav-sidebar:hover, .toggle-nav-sidebar:focus { - background-color: #f6f6f6; + background-color: var(--darkened-bg); } #nav-sidebar { @@ -36,13 +37,13 @@ left: -276px; margin-left: -276px; border-top: 1px solid transparent; - border-right: 1px solid #eaeaea; - background-color: #ffffff; + border-right: 1px solid var(--hairline-color); + background-color: var(--body-bg); overflow: auto; } [dir="rtl"] #nav-sidebar { - border-left: 1px solid #eaeaea; + border-left: 1px solid var(--hairline-color); border-right: 0; left: 0; margin-left: 0; @@ -91,12 +92,12 @@ #nav-sidebar .current-app .section:link, #nav-sidebar .current-app .section:visited { - color: #ffc; + color: var(--header-color); font-weight: bold; } #nav-sidebar .current-model { - background: #ffc; + background: var(--selected-row); } .main > #nav-sidebar + .content { diff --git a/src/static/admin/css/responsive.css b/src/static/admin/css/responsive.css index 053e8413..8c6dd810 100644 --- a/src/static/admin/css/responsive.css +++ b/src/static/admin/css/responsive.css @@ -140,7 +140,7 @@ input[type="submit"], button { } #changelist .actions select { - background: #fff; + background: var(--body-bg); } #changelist .actions .button { @@ -166,7 +166,7 @@ input[type="submit"], button { .filtered .actions, #changelist .paginator { - border-top-color: #eee; + border-top-color: var(--hairline-color); /* XXX Is this used at all? */ } #changelist .results + .paginator { @@ -213,7 +213,7 @@ input[type="submit"], button { fieldset .fieldBox + .fieldBox { margin-top: 10px; padding-top: 10px; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } textarea { @@ -399,11 +399,11 @@ input[type="submit"], button { .datetime .timezonewarning { display: block; font-size: 11px; - color: #999; + color: var(--body-quiet-color); } .datetimeshortcuts { - color: #ccc; + color: var(--border-color); /* XXX Redundant, .datetime span also sets #ccc */ } .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField { @@ -655,7 +655,7 @@ input[type="submit"], button { margin-bottom: -3px; } - form .aligned ul.radiolist li + li { + form .aligned ul.radiolist:not(.inline) li + li { margin-top: 5px; } @@ -740,7 +740,7 @@ input[type="submit"], button { /* Inlines */ .inline-group[data-inline-type="stacked"] .inline-related { - border: 2px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; margin-top: 15px; overflow: auto; @@ -750,18 +750,19 @@ input[type="submit"], button { box-sizing: border-box; } - .inline-group[data-inline-type="stacked"] .inline-related + .inline-related { - margin-top: 30px; - } - .inline-group[data-inline-type="stacked"] .inline-related .module { padding: 0 10px; } - .inline-group[data-inline-type="stacked"] .inline-related .module .form-row:last-child { + .inline-group[data-inline-type="stacked"] .inline-related .module .form-row { + border-top: 1px solid var(--hairline-color); border-bottom: none; } + .inline-group[data-inline-type="stacked"] .inline-related .module .form-row:first-child { + border-top: none; + } + .inline-group[data-inline-type="stacked"] .inline-related h3 { padding: 10px; border-top-width: 0; @@ -791,7 +792,7 @@ input[type="submit"], button { .inline-group[data-inline-type="stacked"] div.add-row { margin-top: 15px; - border: 1px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; } @@ -885,9 +886,7 @@ input[type="submit"], button { } .login .form-row label { - display: block; margin: 0 0 5px; - padding: 0; line-height: 1.2; } @@ -895,7 +894,7 @@ input[type="submit"], button { padding: 15px 0 0; } - .login br, .login .submit-row label { + .login br { display: none; } @@ -963,7 +962,7 @@ input[type="submit"], button { } .timelist a { - background: #fff; + background: var(--body-bg); padding: 4px; } diff --git a/src/static/admin/css/rtl.css b/src/static/admin/css/rtl.css index a40aad0c..0447f893 100644 --- a/src/static/admin/css/rtl.css +++ b/src/static/admin/css/rtl.css @@ -1,25 +1,3 @@ -body { - direction: rtl; -} - -/* LOGIN */ - -.login .form-row { - float: right; -} - -.login .form-row label { - float: right; - padding-left: 0.5em; - padding-right: 0; - text-align: left; -} - -.login .submit-row { - clear: both; - padding: 1em 9.4em 0 0; -} - /* GLOBAL */ th { @@ -119,7 +97,7 @@ thead th.sorted .text { border-left: none; padding-left: 10px; margin-left: 0; - border-right: 5px solid #eaeaea; + border-right: 5px solid var(--hairline-color); padding-right: 10px; margin-right: -15px; } diff --git a/src/static/admin/css/widgets.css b/src/static/admin/css/widgets.css index 14ef12db..c7d64566 100644 --- a/src/static/admin/css/widgets.css +++ b/src/static/admin/css/widgets.css @@ -22,26 +22,25 @@ } .selector-available h2, .selector-chosen h2 { - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px 4px 0 0; } .selector-chosen h2 { - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--header-link-color); } .selector .selector-available h2 { - background: #f8f8f8; - color: #666; + background: var(--darkened-bg); + color: var(--body-quiet-color); } .selector .selector-filter { - background: white; - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-width: 0 1px; padding: 8px; - color: #999; + color: var(--body-quiet-color); font-size: 10px; margin: 0; text-align: left; @@ -66,7 +65,7 @@ .selector ul.selector-chooser { float: left; width: 22px; - background-color: #eee; + background-color: var(--selected-bg); border-radius: 10px; margin: 10em 5px 0 5px; padding: 0; @@ -91,7 +90,7 @@ text-indent: -3000px; overflow: hidden; cursor: default; - opacity: 0.3; + opacity: 0.55; } .active.selector-add, .active.selector-remove { @@ -126,14 +125,14 @@ a.selector-chooseall, a.selector-clearall { overflow: hidden; font-weight: bold; line-height: 16px; - color: #666; + color: var(--body-quiet-color); text-decoration: none; - opacity: 0.3; + opacity: 0.55; } a.active.selector-chooseall:focus, a.active.selector-clearall:focus, a.active.selector-chooseall:hover, a.active.selector-clearall:hover { - color: #447e9b; + color: var(--link-fg); } a.active.selector-chooseall, a.active.selector-clearall { @@ -261,7 +260,7 @@ p.datetime { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-weight: bold; } @@ -269,7 +268,7 @@ p.datetime { white-space: nowrap; font-weight: normal; font-size: 11px; - color: #ccc; + color: var(--body-quiet-color); } .datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField { @@ -313,7 +312,7 @@ table p.datetime { .timezonewarning { font-size: 11px; - color: #999; + color: var(--body-quiet-color); } /* URL */ @@ -322,7 +321,7 @@ p.url { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-size: 11px; font-weight: bold; } @@ -337,7 +336,7 @@ p.file-upload { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-size: 11px; font-weight: bold; } @@ -355,7 +354,7 @@ p.file-upload { } span.clearable-file-input label { - color: #333; + color: var(--body-fg); font-size: 11px; display: inline; float: none; @@ -368,8 +367,9 @@ span.clearable-file-input label { font-size: 12px; width: 19em; text-align: center; - background: white; - border: 1px solid #ddd; + background: var(--body-bg); + color: var(--body-fg); + border: 1px solid var(--hairline-color); border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); overflow: hidden; @@ -397,20 +397,20 @@ span.clearable-file-input label { margin: 0; text-align: center; border-top: none; - background: #f5dd5d; font-weight: 700; font-size: 12px; color: #333; + background: var(--accent); } .calendar th { padding: 8px 5px; - background: #f8f8f8; - border-bottom: 1px solid #ddd; + background: var(--darkened-bg); + border-bottom: 1px solid var(--border-color); font-weight: 400; font-size: 12px; text-align: center; - color: #666; + color: var(--body-quiet-color); } .calendar td { @@ -418,17 +418,17 @@ span.clearable-file-input label { font-size: 12px; text-align: center; padding: 0; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); border-bottom: none; } .calendar td.selected a { - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--button-fg); } .calendar td.nonday { - background: #f8f8f8; + background: var(--darkened-bg); } .calendar td.today a { @@ -440,17 +440,17 @@ span.clearable-file-input label { font-weight: 400; padding: 6px; text-decoration: none; - color: #444; + color: var(--body-quiet-color); } .calendar td a:focus, .timelist a:focus, .calendar td a:hover, .timelist a:hover { - background: #79aec8; + background: var(--primary); color: white; } .calendar td a:active, .timelist a:active { - background: #417690; + background: var(--header-bg); color: white; } @@ -464,16 +464,16 @@ span.clearable-file-input label { .calendarnav a:link, #calendarnav a:visited, #calendarnav a:focus, #calendarnav a:hover { - color: #999; + color: var(--body-quiet-color); } .calendar-shortcuts { - background: white; + background: var(--body-bg); + color: var(--body-quiet-color); font-size: 11px; line-height: 11px; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); padding: 8px 0; - color: #ccc; } .calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { @@ -511,8 +511,8 @@ span.clearable-file-input label { padding: 4px 0; font-size: 12px; background: #eee; - border-top: 1px solid #ddd; - color: #333; + border-top: 1px solid var(--border-color); + color: var(--body-fg); } .calendar-cancel:focus, .calendar-cancel:hover { diff --git a/src/static/admin/js/SelectBox.js b/src/static/admin/js/SelectBox.js index 1927b4ce..ace6d9df 100644 --- a/src/static/admin/js/SelectBox.js +++ b/src/static/admin/js/SelectBox.js @@ -13,6 +13,7 @@ redisplay: function(id) { // Repopulate HTML select box from cache const box = document.getElementById(id); + const scroll_value_from_top = box.scrollTop; box.innerHTML = ''; for (const node of SelectBox.cache[id]) { if (node.displayed) { @@ -22,6 +23,7 @@ box.appendChild(new_option); } } + box.scrollTop = scroll_value_from_top; }, filter: function(id, text) { // Redisplay the HTML select box, displaying only the choices containing ALL @@ -31,7 +33,7 @@ node.displayed = 1; const node_text = node.text.toLowerCase(); for (const token of tokens) { - if (node_text.indexOf(token) === -1) { + if (!node_text.includes(token)) { node.displayed = 0; break; // Once the first token isn't found we're done } diff --git a/src/static/admin/js/actions.js b/src/static/admin/js/actions.js index dae69920..2830e911 100644 --- a/src/static/admin/js/actions.js +++ b/src/static/admin/js/actions.js @@ -1,154 +1,201 @@ /*global gettext, interpolate, ngettext*/ 'use strict'; { - const $ = django.jQuery; - let lastChecked; + function show(selector) { + document.querySelectorAll(selector).forEach(function(el) { + el.classList.remove('hidden'); + }); + } - $.fn.actions = function(opts) { - const options = $.extend({}, $.fn.actions.defaults, opts); - const actionCheckboxes = $(this); - let list_editable_changed = false; - const showQuestion = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).show(); - $(options.allContainer).hide(); - }, - showClear = function() { - $(options.acrossClears).show(); - $(options.acrossQuestions).hide(); - $(options.actionContainer).toggleClass(options.selectedClass); - $(options.allContainer).show(); - $(options.counterContainer).hide(); - }, - reset = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).hide(); - $(options.allContainer).hide(); - $(options.counterContainer).show(); - }, - clearAcross = function() { - reset(); - $(options.acrossInput).val(0); - $(options.actionContainer).removeClass(options.selectedClass); - }, - checker = function(checked) { - if (checked) { - showQuestion(); - } else { - reset(); - } - $(actionCheckboxes).prop("checked", checked) - .parent().parent().toggleClass(options.selectedClass, checked); - }, - updateCounter = function() { - const sel = $(actionCheckboxes).filter(":checked").length; - // data-actions-icnt is defined in the generated HTML - // and contains the total amount of objects in the queryset - const actions_icnt = $('.action-counter').data('actionsIcnt'); - $(options.counterContainer).html(interpolate( - ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { - sel: sel, - cnt: actions_icnt - }, true)); - $(options.allToggle).prop("checked", function() { - let value; - if (sel === actionCheckboxes.length) { - value = true; - showQuestion(); - } else { - value = false; - clearAcross(); - } - return value; - }); - }; - // Show counter by default - $(options.counterContainer).show(); - // Check state of checkboxes and reinit state if needed - $(this).filter(":checked").each(function(i) { - $(this).parent().parent().toggleClass(options.selectedClass); - updateCounter(); - if ($(options.acrossInput).val() === 1) { - showClear(); - } + function hide(selector) { + document.querySelectorAll(selector).forEach(function(el) { + el.classList.add('hidden'); + }); + } + + function showQuestion(options) { + hide(options.acrossClears); + show(options.acrossQuestions); + hide(options.allContainer); + } + + function showClear(options) { + show(options.acrossClears); + hide(options.acrossQuestions); + document.querySelector(options.actionContainer).classList.remove(options.selectedClass); + show(options.allContainer); + hide(options.counterContainer); + } + + function reset(options) { + hide(options.acrossClears); + hide(options.acrossQuestions); + hide(options.allContainer); + show(options.counterContainer); + } + + function clearAcross(options) { + reset(options); + const acrossInputs = document.querySelectorAll(options.acrossInput); + acrossInputs.forEach(function(acrossInput) { + acrossInput.value = 0; }); - $(options.allToggle).show().on('click', function() { - checker($(this).prop("checked")); - updateCounter(); + document.querySelector(options.actionContainer).classList.remove(options.selectedClass); + } + + function checker(actionCheckboxes, options, checked) { + if (checked) { + showQuestion(options); + } else { + reset(options); + } + actionCheckboxes.forEach(function(el) { + el.checked = checked; + el.closest('tr').classList.toggle(options.selectedClass, checked); }); - $("a", options.acrossQuestions).on('click', function(event) { - event.preventDefault(); - $(options.acrossInput).val(1); - showClear(); + } + + function updateCounter(actionCheckboxes, options) { + const sel = Array.from(actionCheckboxes).filter(function(el) { + return el.checked; + }).length; + const counter = document.querySelector(options.counterContainer); + // data-actions-icnt is defined in the generated HTML + // and contains the total amount of objects in the queryset + const actions_icnt = Number(counter.dataset.actionsIcnt); + counter.textContent = interpolate( + ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { + sel: sel, + cnt: actions_icnt + }, true); + const allToggle = document.getElementById(options.allToggleId); + allToggle.checked = sel === actionCheckboxes.length; + if (allToggle.checked) { + showQuestion(options); + } else { + clearAcross(options); + } + } + + const defaults = { + actionContainer: "div.actions", + counterContainer: "span.action-counter", + allContainer: "div.actions span.all", + acrossInput: "div.actions input.select-across", + acrossQuestions: "div.actions span.question", + acrossClears: "div.actions span.clear", + allToggleId: "action-toggle", + selectedClass: "selected" + }; + + window.Actions = function(actionCheckboxes, options) { + options = Object.assign({}, defaults, options); + let list_editable_changed = false; + let lastChecked = null; + let shiftPressed = false; + + document.addEventListener('keydown', (event) => { + shiftPressed = event.shiftKey; }); - $("a", options.acrossClears).on('click', function(event) { - event.preventDefault(); - $(options.allToggle).prop("checked", false); - clearAcross(); - checker(0); - updateCounter(); + + document.addEventListener('keyup', (event) => { + shiftPressed = event.shiftKey; }); - lastChecked = null; - $(actionCheckboxes).on('click', function(event) { - if (!event) { event = window.event; } - const target = event.target ? event.target : event.srcElement; - if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) { - let inrange = false; - $(lastChecked).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - $(actionCheckboxes).each(function() { - if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) { - inrange = (inrange) ? false : true; - } - if (inrange) { - $(this).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - } + + document.getElementById(options.allToggleId).addEventListener('click', function(event) { + checker(actionCheckboxes, options, this.checked); + updateCounter(actionCheckboxes, options); + }); + + document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) { + el.addEventListener('click', function(event) { + event.preventDefault(); + const acrossInputs = document.querySelectorAll(options.acrossInput); + acrossInputs.forEach(function(acrossInput) { + acrossInput.value = 1; }); - } - $(target).parent().parent().toggleClass(options.selectedClass, target.checked); - lastChecked = target; - updateCounter(); + showClear(options); + }); }); - $('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() { - list_editable_changed = true; + + document.querySelectorAll(options.acrossClears + " a").forEach(function(el) { + el.addEventListener('click', function(event) { + event.preventDefault(); + document.getElementById(options.allToggleId).checked = false; + clearAcross(options); + checker(actionCheckboxes, options, false); + updateCounter(actionCheckboxes, options); + }); }); - $('form#changelist-form button[name="index"]').on('click', function(event) { - if (list_editable_changed) { - return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); + + function affectedCheckboxes(target, withModifier) { + const multiSelect = (lastChecked && withModifier && lastChecked !== target); + if (!multiSelect) { + return [target]; } - }); - $('form#changelist-form input[name="_save"]').on('click', function(event) { - let action_changed = false; - $('select option:selected', options.actionContainer).each(function() { - if ($(this).val()) { - action_changed = true; + const checkboxes = Array.from(actionCheckboxes); + const targetIndex = checkboxes.findIndex(el => el === target); + const lastCheckedIndex = checkboxes.findIndex(el => el === lastChecked); + const startIndex = Math.min(targetIndex, lastCheckedIndex); + const endIndex = Math.max(targetIndex, lastCheckedIndex); + const filtered = checkboxes.filter((el, index) => (startIndex <= index) && (index <= endIndex)); + return filtered; + }; + + Array.from(document.getElementById('result_list').tBodies).forEach(function(el) { + el.addEventListener('change', function(event) { + const target = event.target; + if (target.classList.contains('action-select')) { + const checkboxes = affectedCheckboxes(target, shiftPressed); + checker(checkboxes, options, target.checked); + updateCounter(actionCheckboxes, options); + lastChecked = target; + } else { + list_editable_changed = true; } }); - if (action_changed) { - if (list_editable_changed) { - return confirm(gettext("You have selected an action, but you havenโ€™t saved your changes to individual fields yet. Please click OK to save. Youโ€™ll need to re-run the action.")); - } else { - return confirm(gettext("You have selected an action, and you havenโ€™t made any changes on individual fields. Youโ€™re probably looking for the Go button rather than the Save button.")); + }); + + document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() { + if (list_editable_changed) { + const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); + if (!confirmed) { + event.preventDefault(); } } }); + + const el = document.querySelector('#changelist-form input[name=_save]'); + // The button does not exist if no fields are editable. + if (el) { + el.addEventListener('click', function(event) { + if (document.querySelector('[name=action]').value) { + const text = list_editable_changed + ? gettext("You have selected an action, but you havenโ€™t saved your changes to individual fields yet. Please click OK to save. Youโ€™ll need to re-run the action.") + : gettext("You have selected an action, and you havenโ€™t made any changes on individual fields. Youโ€™re probably looking for the Go button rather than the Save button."); + if (!confirm(text)) { + event.preventDefault(); + } + } + }); + } }; - /* Setup plugin defaults */ - $.fn.actions.defaults = { - actionContainer: "div.actions", - counterContainer: "span.action-counter", - allContainer: "div.actions span.all", - acrossInput: "div.actions input.select-across", - acrossQuestions: "div.actions span.question", - acrossClears: "div.actions span.clear", - allToggle: "#action-toggle", - selectedClass: "selected" - }; - $(document).ready(function() { - const $actionsEls = $('tr input.action-select'); - if ($actionsEls.length > 0) { - $actionsEls.actions(); + + // Call function fn when the DOM is loaded and ready. If it is already + // loaded, call the function now. + // http://youmightnotneedjquery.com/#ready + function ready(fn) { + if (document.readyState !== 'loading') { + fn(); + } else { + document.addEventListener('DOMContentLoaded', fn); + } + } + + ready(function() { + const actionsEls = document.querySelectorAll('tr input.action-select'); + if (actionsEls.length > 0) { + Actions(actionsEls); } }); } diff --git a/src/static/admin/js/actions.min.js b/src/static/admin/js/actions.min.js deleted file mode 100644 index 29fd0d8c..00000000 --- a/src/static/admin/js/actions.min.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict';{const a=django.jQuery;let e;a.fn.actions=function(g){const b=a.extend({},a.fn.actions.defaults,g),f=a(this);let k=!1;const l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},n=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()}, -p=function(){n();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)},q=function(c){c?l():n();a(f).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){const c=a(f).filter(":checked").length,d=a(".action-counter").data("actionsIcnt");a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:d},!0));a(b.allToggle).prop("checked",function(){let a;c===f.length?(a=!0,l()):(a=!1,p());return a})}; -a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);h();1===a(b.acrossInput).val()&&m()});a(b.allToggle).show().on("click",function(){q(a(this).prop("checked"));h()});a("a",b.acrossQuestions).on("click",function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("a",b.acrossClears).on("click",function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);p();q(0);h()});e=null;a(f).on("click",function(c){c||(c=window.event); -const d=c.target?c.target:c.srcElement;if(e&&a.data(e)!==a.data(d)&&!0===c.shiftKey){let c=!1;a(e).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)===a.data(e)||a.data(this)===a.data(d))c=c?!1:!0;c&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);e=d;h()});a("form#changelist-form table#result_list tr").on("change","td:gt(0) :input", -function(){k=!0});a('form#changelist-form button[name="index"]').on("click",function(a){if(k)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').on("click",function(c){let d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return k?confirm(gettext("You have selected an action, but you haven\u2019t saved your changes to individual fields yet. Please click OK to save. You\u2019ll need to re-run the action.")): -confirm(gettext("You have selected an action, and you haven\u2019t made any changes on individual fields. You\u2019re probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"};a(document).ready(function(){const g= -a("tr input.action-select");0=h.val()-f.val()&&k.parent().hide();p(d.closest(".inline-group"));a.added&&a.added(d);b(document).trigger("formset:added",[d,a.prefix])}, -r=function(b){b.is("tr")?b.children(":last").append('
'+a.deleteText+"
"):b.is("ul")||b.is("ol")?b.append('
  • '+a.deleteText+"
  • "):b.children(":first").append(''+a.deleteText+"");b.find("a."+a.deleteCssClass).on("click",u.bind(this))},u=function(g){g.preventDefault();var d=b(g.target).closest("."+a.formCssClass);g=d.closest(".inline-group"); -var f=d.prev();f.length&&f.hasClass("row-form-errors")&&f.remove();d.remove();--n;a.removed&&a.removed(d);b(document).trigger("formset:removed",[d,a.prefix]);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(""===h.val()||0'+a.addText+"");k=l.find("tr:last a")}else e.filter(":last").after('"), -k=e.filter(":last").next().find("a");k.on("click",t)})();c=""===h.val()||0 tr.form-row",b(c).tabularFormset(c,a.options)}})})}; diff --git a/src/static/admin/js/prepopulate.min.js b/src/static/admin/js/prepopulate.min.js deleted file mode 100644 index 11ead499..00000000 --- a/src/static/admin/js/prepopulate.min.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';{const b=django.jQuery;b.fn.prepopulate=function(d,f,g){return this.each(function(){const a=b(this),h=function(){if(!a.data("_changed")){var e=[];b.each(d,function(a,c){c=b(c);0a, -#djDebug #djDebugToolbar li>div.djdt-contentless { +#djDebug #djDebugToolbar li > a, +#djDebug #djDebugToolbar li > div.djdt-contentless { font-weight: normal; font-style: normal; text-decoration: none; @@ -115,7 +160,7 @@ padding: 10px 10px 5px 25px; color: #fff; } -#djDebug #djDebugToolbar li>div.djdt-disabled { +#djDebug #djDebugToolbar li > div.djdt-disabled { font-style: italic; color: #999; } @@ -126,9 +171,17 @@ } #djDebug #djDebugToolbar li.djdt-active { - background: #333 no-repeat left center; - background-image: url(../img/indicator.png); - padding-left: 10px; + background: #333; +} + +#djDebug #djDebugToolbar li.djdt-active:before { + content: "โ–ถ"; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + color: #eee; + font-size: 150%; } #djDebug #djDebugToolbar li.djdt-active a:hover { @@ -145,7 +198,7 @@ #djDebug #djDebugToolbarHandle { position: fixed; - transform: rotate(-90deg); + transform: translateY(-100%) rotate(-90deg); transform-origin: right bottom; background-color: #fff; border: 1px solid #111; @@ -171,7 +224,7 @@ background-color: #111; border-color: #ffe761; cursor: move; - opacity: 1.0; + opacity: 1; } #djDebug #djShowToolBarD { @@ -186,14 +239,14 @@ #djDebug code { display: block; - font-family: Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace; + font-family: Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", + monospace; font-size: 12px; white-space: pre; overflow: auto; } #djDebug .djdt-panelContent { - display: none; position: fixed; margin: 0; top: 0; @@ -236,8 +289,22 @@ } #djDebug .djDebugPanelContent .djdt-loader { - display: block; margin: 80px auto; + border: 6px solid white; + border-radius: 50%; + border-top: 6px solid #ffe761; + width: 38px; + height: 38px; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } #djDebug .djDebugPanelContent .djdt-scroll { @@ -291,7 +358,7 @@ width: 12em; text-align: right; color: #666; - padding-right: .5em; + padding-right: 0.5em; } #djDebug .djTemplateContext { @@ -299,7 +366,6 @@ } #djDebug .djdt-panelContent .djDebugClose { - display: block; position: absolute; top: 4px; right: 15px; @@ -314,7 +380,6 @@ text-align: center; font-weight: 900; font-size: 20px; - text-decoration: none; box-sizing: content-box; } @@ -322,7 +387,8 @@ background: #c0695d; } -#djDebug .djdt-panelContent dt, #djDebug .djdt-panelContent dd { +#djDebug .djdt-panelContent dt, +#djDebug .djdt-panelContent dd { display: block; } @@ -347,20 +413,12 @@ border-radius: 3px; } - -#djDebug .djDebugSqlWrap { - position: relative; -} - #djDebug .djDebugCollapsed { - display: none; - text-decoration: none; color: #333; } #djDebug .djDebugUncollapsed { color: #333; - text-decoration: none; } #djDebug .djUnselected { @@ -372,18 +430,13 @@ } #djDebug .djDebugSql { - word-break: break-word; - z-index: 100000002; + overflow-wrap: anywhere; } #djDebug .djSQLDetailsDiv tbody th { text-align: left; } -#djDebug .djSqlExplain td { - white-space: pre; -} - #djDebug span.djDebugLineChart { background-color: #777; height: 3px; @@ -398,29 +451,68 @@ background-color: #900; } -#djDebug .highlight { color: #000; } -#djDebug .highlight .err { color: #000; } /* Error */ -#djDebug .highlight .g { color: #000; } /* Generic */ -#djDebug .highlight .k { color: #000; font-weight: bold } /* Keyword */ -#djDebug .highlight .o { color: #000; } /* Operator */ -#djDebug .highlight .n { color: #000; } /* Name */ -#djDebug .highlight .mi { color: #000; font-weight: bold } /* Literal.Number.Integer */ -#djDebug .highlight .l { color: #000; } /* Literal */ -#djDebug .highlight .x { color: #000; } /* Other */ -#djDebug .highlight .p { color: #000; } /* Punctuation */ -#djDebug .highlight .m { color: #000; font-weight: bold } /* Literal.Number */ -#djDebug .highlight .s { color: #333 } /* Literal.String */ -#djDebug .highlight .w { color: #888888 } /* Text.Whitespace */ -#djDebug .highlight .il { color: #000; font-weight: bold } /* Literal.Number.Integer.Long */ -#djDebug .highlight .na { color: #333 } /* Name.Attribute */ -#djDebug .highlight .nt { color: #000; font-weight: bold } /* Name.Tag */ -#djDebug .highlight .nv { color: #333 } /* Name.Variable */ -#djDebug .highlight .s2 { color: #333 } /* Literal.String.Double */ -#djDebug .highlight .cp { color: #333 } /* Comment.Preproc */ - -#djDebug .djdt-timeline { - width: 30%; +#djDebug .highlight { + color: #000; } +#djDebug .highlight .err { + color: #000; +} /* Error */ +#djDebug .highlight .g { + color: #000; +} /* Generic */ +#djDebug .highlight .k { + color: #000; + font-weight: bold; +} /* Keyword */ +#djDebug .highlight .o { + color: #000; +} /* Operator */ +#djDebug .highlight .n { + color: #000; +} /* Name */ +#djDebug .highlight .mi { + color: #000; + font-weight: bold; +} /* Literal.Number.Integer */ +#djDebug .highlight .l { + color: #000; +} /* Literal */ +#djDebug .highlight .x { + color: #000; +} /* Other */ +#djDebug .highlight .p { + color: #000; +} /* Punctuation */ +#djDebug .highlight .m { + color: #000; + font-weight: bold; +} /* Literal.Number */ +#djDebug .highlight .s { + color: #333; +} /* Literal.String */ +#djDebug .highlight .w { + color: #888888; +} /* Text.Whitespace */ +#djDebug .highlight .il { + color: #000; + font-weight: bold; +} /* Literal.Number.Integer.Long */ +#djDebug .highlight .na { + color: #333; +} /* Name.Attribute */ +#djDebug .highlight .nt { + color: #000; + font-weight: bold; +} /* Name.Tag */ +#djDebug .highlight .nv { + color: #333; +} /* Name.Variable */ +#djDebug .highlight .s2 { + color: #333; +} /* Literal.String.Double */ +#djDebug .highlight .cp { + color: #333; +} /* Comment.Preproc */ #djDebug svg.djDebugLineChart { width: 100%; @@ -438,30 +530,6 @@ stroke: #94b24d; } -#djDebug .djdt-panelContent ul.djdt-stats { - position: relative; - list-style-type: none; -} -#djDebug .djdt-panelContent ul.djdt-stats li { - width: 30%; - float: left; -} -#djDebug .djdt-panelContent ul.djdt-stats li strong.djdt-label { - display: block; -} -#djDebug .djdt-panelContent ul.djdt-stats li span.djdt-color { - height: 12px; - width: 3px; - display: inline-block; -} -#djDebug .djdt-panelContent ul.djdt-stats li span.djdt-info { - display: block; - padding-left: 5px; -} - -#djDebug .djdt-panelContent thead th { - white-space: nowrap; -} #djDebug .djDebugRowWarning .djdt-time { color: red; } @@ -473,24 +541,16 @@ min-width: 70px; white-space: nowrap; } -#djDebug .djdt-panelContent table .djdt-color { - width: 3px; -} -#djDebug .djdt-panelContent table .djdt-color span { - width: 3px; - height: 12px; - overflow: hidden; - padding: 0; +#djDebug .djdt-color:after { + content: "\00a0"; } #djDebug .djToggleSwitch { - text-decoration: none; + box-sizing: content-box; + padding: 0; border: 1px solid #999; - height: 12px; + border-radius: 0; width: 12px; - line-height: 12px; - text-align: center; color: #777; - display: inline-block; background: linear-gradient(to bottom, #fff, #dcdcdc); } #djDebug .djNoToggleSwitch { @@ -512,7 +572,8 @@ overflow: auto; padding: 2px 3px; margin-bottom: 3px; - font-family: Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace; + font-family: Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", + monospace; } #djDebug .djdt-stack span { color: #000; @@ -534,9 +595,15 @@ #djDebug .djdt-width-20 { width: 20%; } +#djDebug .djdt-width-30 { + width: 30%; +} #djDebug .djdt-width-60 { width: 60%; } +#djDebug .djdt-max-height-100 { + max-height: 100%; +} #djDebug .djdt-highlighted { background-color: lightgrey; } diff --git a/src/static/debug_toolbar/img/ajax-loader.gif b/src/static/debug_toolbar/img/ajax-loader.gif deleted file mode 100644 index a7c3f2bacbe630950c16f53e3c27dd840f2e8622..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 404 zcmZ?wbhEHb)Mnsj_{hNU?%lhzw6s;LRsl)He^Smxsfi`2DGKG8B^e5dS&0=n`H3ld znR#jX42nNl7`PZ17!?0=`?-b$J39ur8tEA@GXjNlfS3VfAOn+8OCQJSX*X{)e?H13 zeJ||u>DC8kZ4r?R)6{G4&Dyt|CtcU`diyE8Uq?3owA-;j!N^AxXd>8TMr31bj{uEP z;7?56IIHFOnwrlu67PNQNtrQY{;7pt&RzvNJJ_?&Ze06Pg>MsMs)$s6#HZT6Q+}5p z&C*E81%TGdiiN4(8Lw%U?wsno9F=b>WnG7*rn?>{@R)9v%+M)yIi1FiTcrX vD}VECG@HG3ZvWp+k0+)$$G&~0?NxTdRajt8g3>tw(O*m=3=Gzwa03GXAUBr* diff --git a/src/static/debug_toolbar/img/indicator.png b/src/static/debug_toolbar/img/indicator.png deleted file mode 100644 index 828a6c01c4bbcdcceade3d7987573dbc44582fc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 436 zcmV;l0ZaagP)H zGlmg%jNS+7hfxT2kG$mLbT;*kVFCsiiJ&cQgQBRlyggb>L7;^OYN(<@&mTxA{QIBwk9GMB#n(POaY8IlddcdLp diff --git a/src/static/debug_toolbar/js/history.js b/src/static/debug_toolbar/js/history.js new file mode 100644 index 00000000..cc14b2e4 --- /dev/null +++ b/src/static/debug_toolbar/js/history.js @@ -0,0 +1,51 @@ +import { $$, ajaxForm } from "./utils.js"; + +const djDebug = document.getElementById("djDebug"); + +$$.on(djDebug, "click", ".switchHistory", function (event) { + event.preventDefault(); + const newStoreId = this.dataset.storeId; + const tbody = this.closest("tbody"); + + const highlighted = tbody.querySelector(".djdt-highlighted"); + if (highlighted) { + highlighted.classList.remove("djdt-highlighted"); + } + this.closest("tr").classList.add("djdt-highlighted"); + + ajaxForm(this).then(function (data) { + djDebug.setAttribute("data-store-id", newStoreId); + // Check if response is empty, it could be due to an expired store_id. + if (Object.keys(data).length === 0) { + const container = document.getElementById("djdtHistoryRequests"); + container.querySelector( + 'button[data-store-id="' + newStoreId + '"]' + ).innerHTML = "Switch [EXPIRED]"; + } else { + Object.keys(data).forEach(function (panelId) { + const panel = document.getElementById(panelId); + if (panel) { + panel.outerHTML = data[panelId].content; + document.getElementById("djdt-" + panelId).outerHTML = + data[panelId].button; + } + }); + } + }); +}); + +$$.on(djDebug, "click", ".refreshHistory", function (event) { + event.preventDefault(); + const container = document.getElementById("djdtHistoryRequests"); + ajaxForm(this).then(function (data) { + // Remove existing rows first then re-populate with new data + container + .querySelectorAll("tr[data-store-id]") + .forEach(function (node) { + node.remove(); + }); + data.requests.forEach(function (request) { + container.innerHTML = request.content + container.innerHTML; + }); + }); +}); diff --git a/src/static/debug_toolbar/js/redirect.js b/src/static/debug_toolbar/js/redirect.js index f73d9e52..6976de4b 100644 --- a/src/static/debug_toolbar/js/redirect.js +++ b/src/static/debug_toolbar/js/redirect.js @@ -1 +1 @@ -document.getElementById('redirect_to').focus(); +document.getElementById("redirect_to").focus(); diff --git a/src/static/debug_toolbar/js/timer.js b/src/static/debug_toolbar/js/timer.js new file mode 100644 index 00000000..70d3fe5a --- /dev/null +++ b/src/static/debug_toolbar/js/timer.js @@ -0,0 +1,75 @@ +import { $$ } from "./utils.js"; + +function insertBrowserTiming() { + console.log(["inserted"]); + const timingOffset = performance.timing.navigationStart, + timingEnd = performance.timing.loadEventEnd, + totalTime = timingEnd - timingOffset; + function getLeft(stat) { + return ((performance.timing[stat] - timingOffset) / totalTime) * 100.0; + } + function getCSSWidth(stat, endStat) { + let width = + ((performance.timing[endStat] - performance.timing[stat]) / + totalTime) * + 100.0; + // Calculate relative percent (same as sql panel logic) + width = (100.0 * width) / (100.0 - getLeft(stat)); + return width < 1 ? "2px" : width + "%"; + } + function addRow(tbody, stat, endStat) { + const row = document.createElement("tr"); + if (endStat) { + // Render a start through end bar + row.innerHTML = + "" + + stat.replace("Start", "") + + "" + + '' + + "" + + (performance.timing[stat] - timingOffset) + + " (+" + + (performance.timing[endStat] - performance.timing[stat]) + + ")"; + row.querySelector("rect").setAttribute( + "width", + getCSSWidth(stat, endStat) + ); + } else { + // Render a point in time + row.innerHTML = + "" + + stat + + "" + + '' + + "" + + (performance.timing[stat] - timingOffset) + + ""; + row.querySelector("rect").setAttribute("width", 2); + } + row.querySelector("rect").setAttribute("x", getLeft(stat)); + tbody.appendChild(row); + } + + const browserTiming = document.getElementById("djDebugBrowserTiming"); + // Determine if the browser timing section has already been rendered. + if (browserTiming.classList.contains("djdt-hidden")) { + const tbody = document.getElementById("djDebugBrowserTimingTableBody"); + // This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param) + addRow(tbody, "domainLookupStart", "domainLookupEnd"); + addRow(tbody, "connectStart", "connectEnd"); + addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd + addRow(tbody, "responseStart", "responseEnd"); + addRow(tbody, "domLoading", "domComplete"); // Spans the events below + addRow(tbody, "domInteractive"); + addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd"); + addRow(tbody, "loadEventStart", "loadEventEnd"); + browserTiming.classList.remove("djdt-hidden"); + } +} + +const djDebug = document.getElementById("djDebug"); +// Insert the browser timing now since it's possible for this +// script to miss the initial panel load event. +insertBrowserTiming(); +$$.onPanelRender(djDebug, "TimerPanel", insertBrowserTiming); diff --git a/src/static/debug_toolbar/js/toolbar.js b/src/static/debug_toolbar/js/toolbar.js index 919529d3..c17ee3ea 100644 --- a/src/static/debug_toolbar/js/toolbar.js +++ b/src/static/debug_toolbar/js/toolbar.js @@ -1,231 +1,163 @@ -const $$ = { - on: function(root, eventName, selector, fn) { - root.addEventListener(eventName, function(event) { - const target = event.target.closest(selector); - if (root.contains(target)) { - fn.call(target, event); - } - }); - }, - show: function(element) { - element.style.display = 'block'; - }, - hide: function(element) { - element.style.display = 'none'; - }, - toggle: function(element, value) { - if (value) { - $$.show(element); - } else { - $$.hide(element); - } - }, - visible: function(element) { - const style = getComputedStyle(element); - return style.display !== 'none'; - }, - executeScripts: function(scripts) { - scripts.forEach(function(script) { - const el = document.createElement('script'); - el.type = 'module'; - el.src = script; - el.async = true; - document.head.appendChild(el); - }); - }, -}; +import { $$, ajax } from "./utils.js"; -const onKeyDown = function(event) { +function onKeyDown(event) { if (event.keyCode === 27) { djdt.hide_one_level(); } -}; - -const ajax = function(url, init) { - init = Object.assign({credentials: 'same-origin'}, init); - return fetch(url, init).then(function(response) { - if (response.ok) { - return response.json(); - } else { - const win = document.querySelector('#djDebugWindow'); - win.innerHTML = '
    ยป

    '+response.status+': '+response.statusText+'

    '; - $$.show(win); - return Promise.reject(); - } - }); -}; +} const djdt = { handleDragged: false, - init: function() { - const djDebug = document.querySelector('#djDebug'); + init() { + const djDebug = document.getElementById("djDebug"); $$.show(djDebug); - $$.on(djDebug.querySelector('#djDebugPanelList'), 'click', 'li a', function(event) { - event.preventDefault(); - if (!this.className) { - return; - } - const current = djDebug.querySelector('#' + this.className); - if ($$.visible(current)) { - djdt.hide_panels(); - } else { - djdt.hide_panels(); - - $$.show(current); - this.parentElement.classList.add('djdt-active'); - - const inner = current.querySelector('.djDebugPanelContent .djdt-scroll'), - store_id = djDebug.dataset.storeId; - if (store_id && inner.children.length === 0) { - let url = djDebug.dataset.renderPanelUrl; - const url_params = new URLSearchParams(); - url_params.append('store_id', store_id); - url_params.append('panel_id', this.className); - url += '?' + url_params.toString(); - ajax(url).then(function(data) { - inner.previousElementSibling.remove(); // Remove AJAX loader - inner.innerHTML = data.content; - $$.executeScripts(data.scripts); - }); + $$.on( + document.getElementById("djDebugPanelList"), + "click", + "li a", + function (event) { + event.preventDefault(); + if (!this.className) { + return; + } + const panelId = this.className; + const current = document.getElementById(panelId); + if ($$.visible(current)) { + djdt.hide_panels(); + } else { + djdt.hide_panels(); + + $$.show(current); + this.parentElement.classList.add("djdt-active"); + + const inner = current.querySelector( + ".djDebugPanelContent .djdt-scroll" + ), + store_id = djDebug.dataset.storeId; + if (store_id && inner.children.length === 0) { + const url = new URL( + djDebug.dataset.renderPanelUrl, + window.location + ); + url.searchParams.append("store_id", store_id); + url.searchParams.append("panel_id", panelId); + ajax(url).then(function (data) { + inner.previousElementSibling.remove(); // Remove AJAX loader + inner.innerHTML = data.content; + $$.executeScripts(data.scripts); + $$.applyStyles(inner); + djDebug.dispatchEvent( + new CustomEvent("djdt.panel.render", { + detail: { panelId: panelId }, + }) + ); + }); + } else { + djDebug.dispatchEvent( + new CustomEvent("djdt.panel.render", { + detail: { panelId: panelId }, + }) + ); + } } } - }); - $$.on(djDebug, 'click', 'a.djDebugClose', function(event) { - event.preventDefault(); + ); + $$.on(djDebug, "click", ".djDebugClose", function () { djdt.hide_one_level(); }); - $$.on(djDebug, 'click', '.djDebugPanelButton input[type=checkbox]', function() { - djdt.cookie.set(this.dataset.cookie, this.checked ? 'on' : 'off', { - path: '/', - expires: 10 - }); - }); + $$.on( + djDebug, + "click", + ".djDebugPanelButton input[type=checkbox]", + function () { + djdt.cookie.set( + this.dataset.cookie, + this.checked ? "on" : "off", + { + path: "/", + expires: 10, + } + ); + } + ); // Used by the SQL and template panels - $$.on(djDebug, 'click', '.remoteCall', function(event) { + $$.on(djDebug, "click", ".remoteCall", function (event) { event.preventDefault(); + let url; const ajax_data = {}; - if (this.tagName === 'BUTTON') { - const form = this.closest('form'); - ajax_data.url = this.formAction; - - if (form) { - ajax_data.body = new FormData(form); - ajax_data.method = form.method.toUpperCase(); - } - } - - if (this.tagName === 'A') { - ajax_data.url = this.href; + if (this.tagName === "BUTTON") { + const form = this.closest("form"); + url = this.formAction; + ajax_data.method = form.method.toUpperCase(); + ajax_data.body = new FormData(form); + } else if (this.tagName === "A") { + url = this.href; } - ajax(ajax_data.url, ajax_data).then(function(data) { - const win = djDebug.querySelector('#djDebugWindow'); + ajax(url, ajax_data).then(function (data) { + const win = document.getElementById("djDebugWindow"); win.innerHTML = data.content; $$.show(win); }); }); - // Used by the history panel - $$.on(djDebug, 'click', '.switchHistory', function(event) { - event.preventDefault(); - const ajax_data = {}; - const newStoreId = this.dataset.storeId; - const form = this.closest('form'); - const tbody = this.closest('tbody'); - - ajax_data.url = this.getAttribute('formaction'); - - if (form) { - ajax_data.body = new FormData(form); - ajax_data.method = form.getAttribute('method') || 'POST'; - } - - tbody.querySelector('.djdt-highlighted').classList.remove('djdt-highlighted'); - this.closest('tr').classList.add('djdt-highlighted'); - - ajax(ajax_data.url, ajax_data).then(function(data) { - djDebug.setAttribute('data-store-id', newStoreId); - Object.keys(data).map(function (panelId) { - if (djDebug.querySelector('#'+panelId)) { - djDebug.querySelector('#'+panelId).outerHTML = data[panelId].content; - djDebug.querySelector('.djdt-'+panelId).outerHTML = data[panelId].button; + // Used by the cache, profiling and SQL panels + $$.on(djDebug, "click", ".djToggleSwitch", function () { + const id = this.dataset.toggleId; + const toggleOpen = "+"; + const toggleClose = "-"; + const open_me = this.textContent === toggleOpen; + const name = this.dataset.toggleName; + const container = document.getElementById(name + "_" + id); + container + .querySelectorAll(".djDebugCollapsed") + .forEach(function (e) { + $$.toggle(e, open_me); + }); + container + .querySelectorAll(".djDebugUncollapsed") + .forEach(function (e) { + $$.toggle(e, !open_me); + }); + const self = this; + this.closest(".djDebugPanelContent") + .querySelectorAll(".djToggleDetails_" + id) + .forEach(function (e) { + if (open_me) { + e.classList.add("djSelected"); + e.classList.remove("djUnselected"); + self.textContent = toggleClose; + } else { + e.classList.remove("djSelected"); + e.classList.add("djUnselected"); + self.textContent = toggleOpen; + } + const switch_ = e.querySelector(".djToggleSwitch"); + if (switch_) { + switch_.textContent = self.textContent; } }); - }); }); - // Used by the history panel - $$.on(djDebug, 'click', '.refreshHistory', function(event) { - event.preventDefault(); - const ajax_data = {}; - const form = this.closest('form'); - const container = djDebug.querySelector('#djdtHistoryRequests'); - - ajax_data.url = this.getAttribute('formaction'); - - if (form) { - ajax_data.body = new FormData(form); - ajax_data.method = form.getAttribute('method') || 'POST'; - } - - ajax(ajax_data.url, ajax_data).then(function(data) { - if (data.requests.constructor === Array) { - data.requests.map(function(request) { - if (!container.querySelector('[data-store-id="'+request.id+'"]')) { - container.innerHTML = request.content + container.innerHTML; - } - }); - } - }); - }); - - // Used by the cache, profiling and SQL panels - $$.on(djDebug, 'click', 'a.djToggleSwitch', function(event) { - event.preventDefault(); - const self = this; - const id = this.dataset.toggleId; - const open_me = this.textContent === this.dataset.toggleOpen; - const name = this.dataset.toggleName; - const container = this.closest('.djDebugPanelContent').querySelector('#' + name + '_' + id); - container.querySelectorAll('.djDebugCollapsed').forEach(function(e) { - $$.toggle(e, open_me); - }); - container.querySelectorAll('.djDebugUncollapsed').forEach(function(e) { - $$.toggle(e, !open_me); + document + .getElementById("djHideToolBarButton") + .addEventListener("click", function (event) { + event.preventDefault(); + djdt.hide_toolbar(); }); - this.closest('.djDebugPanelContent').querySelectorAll('.djToggleDetails_' + id).forEach(function(e) { - if (open_me) { - e.classList.add('djSelected'); - e.classList.remove('djUnselected'); - self.textContent = self.dataset.toggleClose; - } else { - e.classList.remove('djSelected'); - e.classList.add('djUnselected'); - self.textContent = self.dataset.toggleOpen; - } - const switch_ = e.querySelector('.djToggleSwitch'); - if (switch_) { - switch_.textContent = self.textContent; + document + .getElementById("djShowToolBarButton") + .addEventListener("click", function () { + if (!djdt.handleDragged) { + djdt.show_toolbar(); } }); - }); - - djDebug.querySelector('#djHideToolBarButton').addEventListener('click', function(event) { - event.preventDefault(); - djdt.hide_toolbar(true); - }); - djDebug.querySelector('#djShowToolBarButton').addEventListener('click', function(event) { - event.preventDefault(); - if (!djdt.handleDragged) { - djdt.show_toolbar(); - } - }); let startPageY, baseY; - const handle = document.querySelector('#djDebugToolbarHandle'); - const onHandleMove = function(event) { + const handle = document.getElementById("djDebugToolbarHandle"); + function onHandleMove(event) { // Chrome can send spurious mousemove events, so don't do anything unless the // cursor really moved. Otherwise, it will be impossible to expand the toolbar // due to djdt.handleDragged being set to true. @@ -238,113 +170,129 @@ const djdt = { top = window.innerHeight - handle.offsetHeight; } - handle.style.top = top + 'px'; + handle.style.top = top + "px"; djdt.handleDragged = true; } - }; - djDebug.querySelector('#djShowToolBarButton').addEventListener('mousedown', function(event) { - event.preventDefault(); - startPageY = event.pageY; - baseY = handle.offsetTop - startPageY; - document.addEventListener('mousemove', onHandleMove); - }); - document.addEventListener('mouseup', function (event) { - document.removeEventListener('mousemove', onHandleMove); + } + document + .getElementById("djShowToolBarButton") + .addEventListener("mousedown", function (event) { + event.preventDefault(); + startPageY = event.pageY; + baseY = handle.offsetTop - startPageY; + document.addEventListener("mousemove", onHandleMove); + }); + document.addEventListener("mouseup", function (event) { + document.removeEventListener("mousemove", onHandleMove); if (djdt.handleDragged) { event.preventDefault(); - localStorage.setItem('djdt.top', handle.offsetTop); - setTimeout(function () { + localStorage.setItem("djdt.top", handle.offsetTop); + requestAnimationFrame(function () { djdt.handleDragged = false; - }, 10); + }); + djdt.ensure_handle_visibility(); } }); - const show = localStorage.getItem('djdt.show') || djDebug.dataset.defaultShow; - if (show === 'true') { + const show = + localStorage.getItem("djdt.show") || djDebug.dataset.defaultShow; + if (show === "true") { djdt.show_toolbar(); } else { djdt.hide_toolbar(); } }, - hide_panels: function() { - const djDebug = document.getElementById('djDebug'); - $$.hide(djDebug.querySelector('#djDebugWindow')); - djDebug.querySelectorAll('.djdt-panelContent').forEach(function(e) { + hide_panels() { + const djDebug = document.getElementById("djDebug"); + $$.hide(document.getElementById("djDebugWindow")); + djDebug.querySelectorAll(".djdt-panelContent").forEach(function (e) { $$.hide(e); }); - djDebug.querySelectorAll('#djDebugToolbar li').forEach(function(e) { - e.classList.remove('djdt-active'); + document.querySelectorAll("#djDebugToolbar li").forEach(function (e) { + e.classList.remove("djdt-active"); }); }, - hide_toolbar: function() { + ensure_handle_visibility() { + const handle = document.getElementById("djDebugToolbarHandle"); + // set handle position + const handleTop = Math.min( + localStorage.getItem("djdt.top") || 0, + window.innerHeight - handle.offsetWidth + ); + handle.style.top = handleTop + "px"; + }, + hide_toolbar() { djdt.hide_panels(); - const djDebug = document.getElementById('djDebug'); - $$.hide(djDebug.querySelector('#djDebugToolbar')); + $$.hide(document.getElementById("djDebugToolbar")); - const handle = document.querySelector('#djDebugToolbarHandle'); + const handle = document.getElementById("djDebugToolbarHandle"); $$.show(handle); - // set handle position - let handleTop = localStorage.getItem('djdt.top'); - if (handleTop) { - handleTop = Math.min(handleTop, window.innerHeight - handle.offsetHeight); - handle.style.top = handleTop + 'px'; - } + djdt.ensure_handle_visibility(); + window.addEventListener("resize", djdt.ensure_handle_visibility); + document.removeEventListener("keydown", onKeyDown); - document.removeEventListener('keydown', onKeyDown); - - localStorage.setItem('djdt.show', 'false'); + localStorage.setItem("djdt.show", "false"); }, - hide_one_level: function() { - const djDebug = document.getElementById('djDebug'); - if ($$.visible(djDebug.querySelector('#djDebugWindow'))) { - $$.hide(djDebug.querySelector('#djDebugWindow')); - } else if (djDebug.querySelector('#djDebugToolbar li.djdt-active')) { - djdt.hide_panels(); + hide_one_level() { + const win = document.getElementById("djDebugWindow"); + if ($$.visible(win)) { + $$.hide(win); } else { - djdt.hide_toolbar(true); + const toolbar = document.getElementById("djDebugToolbar"); + if (toolbar.querySelector("li.djdt-active")) { + djdt.hide_panels(); + } else { + djdt.hide_toolbar(); + } } }, - show_toolbar: function() { - document.addEventListener('keydown', onKeyDown); - const djDebug = document.getElementById('djDebug'); - $$.hide(djDebug.querySelector('#djDebugToolbarHandle')); - $$.show(djDebug.querySelector('#djDebugToolbar')); - localStorage.setItem('djdt.show', 'true'); + show_toolbar() { + document.addEventListener("keydown", onKeyDown); + $$.hide(document.getElementById("djDebugToolbarHandle")); + $$.show(document.getElementById("djDebugToolbar")); + localStorage.setItem("djdt.show", "true"); + window.removeEventListener("resize", djdt.ensure_handle_visibility); }, cookie: { - get: function(key){ - if (document.cookie.indexOf(key) === -1) { + get(key) { + if (!document.cookie.includes(key)) { return null; } - const cookieArray = document.cookie.split('; '), - cookies = {}; + const cookieArray = document.cookie.split("; "), + cookies = {}; - cookieArray.forEach(function(e){ - const parts = e.split('='); - cookies[ parts[0] ] = parts[1]; + cookieArray.forEach(function (e) { + const parts = e.split("="); + cookies[parts[0]] = parts[1]; }); - return cookies[ key ]; + return cookies[key]; }, - set: function(key, value, options){ + set(key, value, options) { options = options || {}; - if (typeof options.expires === 'number') { - const days = options.expires, t = options.expires = new Date(); + if (typeof options.expires === "number") { + const days = options.expires, + t = (options.expires = new Date()); t.setDate(t.getDate() + days); } document.cookie = [ - encodeURIComponent(key) + '=' + String(value), - options.expires ? '; expires=' + options.expires.toUTCString() : '', - options.path ? '; path=' + options.path : '', - options.domain ? '; domain=' + options.domain : '', - options.secure ? '; secure' : '' - ].join(''); + encodeURIComponent(key) + "=" + String(value), + options.expires + ? "; expires=" + options.expires.toUTCString() + : "", + options.path ? "; path=" + options.path : "", + options.domain ? "; domain=" + options.domain : "", + options.secure ? "; secure" : "", + "sameSite" in options + ? "; sameSite=" + options.samesite + : "; sameSite=Lax", + ].join(""); return value; - } + }, }, }; window.djdt = { @@ -355,8 +303,8 @@ window.djdt = { cookie: djdt.cookie, }; -if (document.readyState !== 'loading') { +if (document.readyState !== "loading") { djdt.init(); } else { - document.addEventListener('DOMContentLoaded', djdt.init); + document.addEventListener("DOMContentLoaded", djdt.init); } diff --git a/src/static/debug_toolbar/js/toolbar.timer.js b/src/static/debug_toolbar/js/toolbar.timer.js deleted file mode 100644 index 51799411..00000000 --- a/src/static/debug_toolbar/js/toolbar.timer.js +++ /dev/null @@ -1,41 +0,0 @@ -const timingOffset = performance.timing.navigationStart, - timingEnd = performance.timing.loadEventEnd, - totalTime = timingEnd - timingOffset; -function getLeft(stat) { - return ((performance.timing[stat] - timingOffset) / (totalTime)) * 100.0; -} -function getCSSWidth(stat, endStat) { - let width = ((performance.timing[endStat] - performance.timing[stat]) / (totalTime)) * 100.0; - // Calculate relative percent (same as sql panel logic) - width = 100.0 * width / (100.0 - getLeft(stat)); - return (width < 1) ? "2px" : width + "%"; -} -function addRow(stat, endStat) { - const row = document.createElement('tr'); - if (endStat) { - // Render a start through end bar - row.innerHTML = '' + stat.replace('Start', '') + '' + - '' + - '' + (performance.timing[stat] - timingOffset) + ' (+' + (performance.timing[endStat] - performance.timing[stat]) + ')'; - row.querySelector('rect').setAttribute('width', getCSSWidth(stat, endStat)); - } else { - // Render a point in time - row.innerHTML = '' + stat + '' + - '' + - '' + (performance.timing[stat] - timingOffset) + ''; - row.querySelector('rect').setAttribute('width', 2); - } - row.querySelector('rect').setAttribute('x', getLeft(stat)); - document.querySelector('#djDebugBrowserTimingTableBody').appendChild(row); -} - -// This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param) -addRow('domainLookupStart', 'domainLookupEnd'); -addRow('connectStart', 'connectEnd'); -addRow('requestStart', 'responseEnd'); // There is no requestEnd -addRow('responseStart', 'responseEnd'); -addRow('domLoading', 'domComplete'); // Spans the events below -addRow('domInteractive'); -addRow('domContentLoadedEventStart', 'domContentLoadedEventEnd'); -addRow('loadEventStart', 'loadEventEnd'); -document.querySelector('#djDebugBrowserTiming').classList.remove('djdt-hidden'); diff --git a/src/static/debug_toolbar/js/utils.js b/src/static/debug_toolbar/js/utils.js new file mode 100644 index 00000000..da810aad --- /dev/null +++ b/src/static/debug_toolbar/js/utils.js @@ -0,0 +1,107 @@ +const $$ = { + on(root, eventName, selector, fn) { + root.addEventListener(eventName, function (event) { + const target = event.target.closest(selector); + if (root.contains(target)) { + fn.call(target, event); + } + }); + }, + onPanelRender(root, panelId, fn) { + /* + This is a helper function to attach a handler for a `djdt.panel.render` + event of a specific panel. + + root: The container element that the listener should be attached to. + panelId: The Id of the panel. + fn: A function to execute when the event is triggered. + */ + root.addEventListener("djdt.panel.render", function (event) { + if (event.detail.panelId === panelId) { + fn.call(event); + } + }); + }, + show(element) { + element.classList.remove("djdt-hidden"); + }, + hide(element) { + element.classList.add("djdt-hidden"); + }, + toggle(element, value) { + if (value) { + $$.show(element); + } else { + $$.hide(element); + } + }, + visible(element) { + return !element.classList.contains("djdt-hidden"); + }, + executeScripts(scripts) { + scripts.forEach(function (script) { + const el = document.createElement("script"); + el.type = "module"; + el.src = script; + el.async = true; + document.head.appendChild(el); + }); + }, + applyStyles(container) { + /* + * Given a container element, apply styles set via data-djdt-styles attribute. + * The format is data-djdt-styles="styleName1:value;styleName2:value2" + * The style names should use the CSSStyleDeclaration camel cased names. + */ + container + .querySelectorAll("[data-djdt-styles]") + .forEach(function (element) { + const styles = element.dataset.djdtStyles || ""; + styles.split(";").forEach(function (styleText) { + const styleKeyPair = styleText.split(":"); + if (styleKeyPair.length === 2) { + const name = styleKeyPair[0].trim(); + const value = styleKeyPair[1].trim(); + element.style[name] = value; + } + }); + }); + }, +}; + +function ajax(url, init) { + init = Object.assign({ credentials: "same-origin" }, init); + return fetch(url, init) + .then(function (response) { + if (response.ok) { + return response.json(); + } + return Promise.reject( + new Error(response.status + ": " + response.statusText) + ); + }) + .catch(function (error) { + const win = document.getElementById("djDebugWindow"); + win.innerHTML = + '

    ' + + error.message + + "

    "; + $$.show(win); + throw error; + }); +} + +function ajaxForm(element) { + const form = element.closest("form"); + const url = new URL(form.action); + const formData = new FormData(form); + for (const [name, value] of formData.entries()) { + url.searchParams.append(name, value); + } + const ajaxData = { + method: form.method.toUpperCase(), + }; + return ajax(url, ajaxData); +} + +export { $$, ajax, ajaxForm }; From 89454bc7cd115d4782ac43962195bfe382d13508 Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Wed, 9 Mar 2022 14:30:59 -0700 Subject: [PATCH 04/20] fix requirements for merge, DEBUG in docker build --- docker-compose.yml | 1 + docker/Dockerfile-api | 1 - src/requirements-dev.txt | 2 -- src/requirements.in | 1 - src/requirements.txt | 2 -- 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 483e869b..4c1d7487 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,7 @@ services: dockerfile: docker/Dockerfile-api args: - BASE_IMAGE + - DEBUG - DOCKER_GIT_CREDENTIALS environment: - ADMIN_EMAIL diff --git a/docker/Dockerfile-api b/docker/Dockerfile-api index d7da5b20..c92d2705 100644 --- a/docker/Dockerfile-api +++ b/docker/Dockerfile-api @@ -39,7 +39,6 @@ RUN chmod +x /entrypoints/api_entrypoint.sh COPY ./configs /configs # Args are passed in via docker-compose during build time -ARG DEBUG ARG DOMAINS ARG IPS ARG SECRET_KEY diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 59b78e09..d04da6ec 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -251,8 +251,6 @@ requests==2.27.1 # docker-compose # requests-mock # requests-oauthlib -requests-futures==1.0.0 - # via -r requirements.txt requests-mock==1.9.3 # via -r requirements.txt requests-oauthlib==1.3.1 diff --git a/src/requirements.in b/src/requirements.in index 6022a94f..c7b91666 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -9,7 +9,6 @@ jsonfield>=3.0, <4.0 oauthlib>=3.0, <4.0 psycopg2-binary>=2.0, <3.0 pyjwt>=2.0, <3.0 -requests-futures>=0.9.9 requests-mock>=1.0, <2.0 requests_oauthlib>=1.0, <2.0 scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies diff --git a/src/requirements.txt b/src/requirements.txt index b667907e..16bd41a0 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -125,8 +125,6 @@ requests==2.27.1 # docker-compose # requests-mock # requests-oauthlib -requests-futures==1.0.0 - # via -r requirements.in requests-mock==1.9.3 # via -r requirements.in requests-oauthlib==1.3.1 From f2a2f339c27f85323728babebe50786588a39258 Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 10 Mar 2022 11:39:01 -0700 Subject: [PATCH 05/20] Added preselector related settings. --- configs/preselector_config.json | 1 + src/requirements-dev.txt | 9 +++++++-- src/requirements.in | 3 +-- src/requirements.txt | 7 +++++-- src/sensor/settings.py | 5 +++++ 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 configs/preselector_config.json diff --git a/configs/preselector_config.json b/configs/preselector_config.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/configs/preselector_config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index a29b909d..a7eecaec 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -148,6 +148,8 @@ iniconfig==1.1.1 # via pytest isort==4.3.20 # via -r requirements-dev.in +its-preselector @ git+https://github.com/NTIA/Preselector@0.0.1 + # via -r requirements.txt itypes==1.2.0 # via # -r requirements.txt @@ -180,6 +182,7 @@ numpy==1.21.5 # via # -r requirements.txt # scipy + # sigmf oauthlib==3.1.1 # via # -r requirements.txt @@ -273,6 +276,7 @@ requests==2.27.1 # coreapi # docker # docker-compose + # its-preselector # requests-mock # requests-oauthlib requests-mock==1.6.0 @@ -289,11 +293,11 @@ ruamel-yaml-clib==0.2.6 # ruamel-yaml scipy==1.7.3 # via -r requirements.txt -scos_actions @ git+https://github.com/NTIA/scos-actions@bug_fix +scos_actions @ git+https://github.com/NTIA/scos-actions@preselector-bug_fix # via # -r requirements.txt # scos-usrp -scos_usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@preselector_test # via -r requirements.txt seed-isort-config==1.9.1 # via -r requirements-dev.in @@ -309,6 +313,7 @@ six==1.16.0 # jsonschema # python-dateutil # requests-mock + # sigmf # tox # virtualenv # websocket-client diff --git a/src/requirements.in b/src/requirements.in index 53c40b6b..0385134b 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -16,5 +16,4 @@ pyjwt~=1.7.1 raven==6.10.0 requests-mock==1.6.0 requests_oauthlib~=1.3.0 -#scos_actions @ git+https://github.com/NTIA/scos-actions@bug_fix#egg=scos_actions -scos_usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies#egg=scos_usrp +scos_usrp @ git+https://github.com/NTIA/scos-usrp@preselector_test#egg=scos_usrp diff --git a/src/requirements.txt b/src/requirements.txt index 80a48767..8f7fae36 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -73,6 +73,8 @@ importlib-metadata==4.11.2 # via jsonschema inflection==0.5.1 # via drf-yasg +its-preselector @ git+https://github.com/NTIA/Preselector@0.0.1 + # via scos-actions itypes==1.2.0 # via coreapi jinja2==3.0.3 @@ -129,6 +131,7 @@ requests==2.27.1 # coreapi # docker # docker-compose + # its-preselector # requests-mock # requests-oauthlib requests-mock==1.6.0 @@ -145,9 +148,9 @@ ruamel-yaml-clib==0.2.6 # scos-actions scipy==1.7.3 # via scos-actions -scos_actions @ git+https://github.com/NTIA/scos-actions@bug_fix +scos_actions @ git+https://github.com/NTIA/scos-actions@preselector-bug_fix # via scos-usrp -scos_usrp @ git+https://github.com/NTIA/scos-usrp@fix_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@preselector_test # via -r requirements.in sigmf @ git+https://github.com/NTIA/SigMF.git@multi-recording-archive # via scos-actions diff --git a/src/sensor/settings.py b/src/sensor/settings.py index 4208e606..5ce9b73d 100644 --- a/src/sensor/settings.py +++ b/src/sensor/settings.py @@ -69,6 +69,7 @@ if path.exists(path.join(CONFIG_DIR, "sensor_definition.json")): SENSOR_DEFINITION_FILE = path.join(CONFIG_DIR, "sensor_definition.json") MEDIA_ROOT = path.join(REPO_ROOT, "files") +PRESELECTOR_CONFIG=path.join(CONFIG_DIR, "preselector_config.json") # Cleanup any existing healtcheck files try: @@ -399,3 +400,7 @@ PATH_TO_JWT_PUBLIC_KEY = path.join(CERTS_DIR, PATH_TO_JWT_PUBLIC_KEY) # Required role from JWT token to access API REQUIRED_ROLE = "ROLE_MANAGER" + +PRESELECTOR_CONFIG = env.str('PRESELECTOR_CONFIG', default=path.join(CONFIG_DIR, 'preselector_config.json')) +PRESELECTOR_MODULE = env.str('PRESELECTOR_MODULE', default='its_preselector.web_relay_preselector') +PRESELECTOR_CLASS = env.str('PRESELECTOR_CLASS', default='WebRelayPreselector') From 5b1d8689e4bced4cb3f6a148881d8c3044453dd1 Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Thu, 10 Mar 2022 13:11:38 -0700 Subject: [PATCH 06/20] fix jwt tests --- src/authentication/tests/test_jwt_auth.py | 78 ++++++++--------------- 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/src/authentication/tests/test_jwt_auth.py b/src/authentication/tests/test_jwt_auth.py index 8ad16ea6..313e6f1d 100644 --- a/src/authentication/tests/test_jwt_auth.py +++ b/src/authentication/tests/test_jwt_auth.py @@ -69,9 +69,8 @@ def test_no_token_unauthorized(live_server): def test_token_no_roles_unauthorized(live_server, jwt_keys): token_payload = get_token_payload(authorities=[]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "User missing required role" @@ -80,9 +79,8 @@ def test_token_no_roles_unauthorized(live_server, jwt_keys): def test_token_role_manager_accepted(live_server, jwt_keys): token_payload = get_token_payload() encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 @@ -106,9 +104,8 @@ def test_token_expired_1_day_forbidden(live_server, jwt_keys): current_datetime = datetime.now() token_payload = get_token_payload(exp=(current_datetime - one_day).timestamp()) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "Token is expired!" @@ -119,9 +116,8 @@ def test_bad_private_key_forbidden(live_server): encoded = jwt.encode( token_payload, str(BAD_PRIVATE_KEY.decode("utf-8")), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "Unable to verify token!" @@ -136,9 +132,8 @@ def test_bad_public_key_forbidden(settings, live_server, jwt_keys): encoded = jwt.encode( token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "Unable to verify token!" @@ -148,9 +143,8 @@ def test_token_expired_1_min_forbidden(live_server, jwt_keys): current_datetime = datetime.now() token_payload = get_token_payload(exp=(current_datetime - one_min).timestamp()) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "Token is expired!" @@ -160,9 +154,8 @@ def test_token_expires_in_1_min_accepted(live_server, jwt_keys): current_datetime = datetime.now() token_payload = get_token_payload(exp=(current_datetime + one_min).timestamp()) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 @@ -170,9 +163,8 @@ def test_token_expires_in_1_min_accepted(live_server, jwt_keys): def test_token_role_user_forbidden(live_server, jwt_keys): token_payload = get_token_payload(authorities=["ROLE_USER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 assert response.json()["detail"] == "User missing required role" @@ -182,9 +174,8 @@ def test_token_role_user_required_role_accepted(settings, live_server, jwt_keys) settings.REQUIRED_ROLE = "ROLE_USER" token_payload = get_token_payload(authorities=["ROLE_USER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 @@ -194,9 +185,8 @@ def test_token_multiple_roles_accepted(live_server, jwt_keys): authorities=["ROLE_MANAGER", "ROLE_USER", "ROLE_ITS"] ) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 @@ -206,9 +196,8 @@ def test_token_mulitple_roles_forbidden(live_server, jwt_keys): authorities=["ROLE_SENSOR", "ROLE_USER", "ROLE_ITS"] ) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 403 @@ -216,9 +205,8 @@ def test_token_mulitple_roles_forbidden(live_server, jwt_keys): def test_urls_unauthorized(live_server, jwt_keys): token_payload = get_token_payload(authorities=["ROLE_USER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - headers = get_headers(utf8_bytes) + headers = get_headers(encoded) capabilities = reverse("capabilities", kwargs=V1) response = client.get(f"{live_server.url}{capabilities}", headers=headers) @@ -253,9 +241,8 @@ def test_urls_unauthorized(live_server, jwt_keys): def test_urls_authorized(live_server, jwt_keys): token_payload = get_token_payload(authorities=["ROLE_MANAGER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - headers = get_headers(utf8_bytes) + headers = get_headers(encoded) capabilities = reverse("capabilities", kwargs=V1) response = client.get(f"{live_server.url}{capabilities}", headers=headers) @@ -292,9 +279,8 @@ def test_user_cannot_view_user_detail(live_server, jwt_keys): encoded = jwt.encode( sensor01_token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 sensor02_token_payload = get_token_payload(authorities=["ROLE_USER"]) @@ -302,7 +288,6 @@ def test_user_cannot_view_user_detail(live_server, jwt_keys): encoded = jwt.encode( sensor02_token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() sensor01_user = User.objects.get(username=sensor01_token_payload["user_name"]) @@ -310,7 +295,7 @@ def test_user_cannot_view_user_detail(live_server, jwt_keys): kws.update(V1) user_detail = reverse("user-detail", kwargs=kws) response = client.get( - f"{live_server.url}{user_detail}", headers=get_headers(utf8_bytes) + f"{live_server.url}{user_detail}", headers=get_headers(encoded) ) assert response.status_code == 403 @@ -321,14 +306,12 @@ def test_user_cannot_view_user_detail_role_change(live_server, jwt_keys): encoded = jwt.encode( sensor01_token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 token_payload = get_token_payload(authorities=["ROLE_USER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() sensor01_user = User.objects.get(username=sensor01_token_payload["user_name"]) @@ -336,7 +319,7 @@ def test_user_cannot_view_user_detail_role_change(live_server, jwt_keys): kws.update(V1) user_detail = reverse("user-detail", kwargs=kws) response = client.get( - f"{live_server.url}{user_detail}", headers=get_headers(utf8_bytes) + f"{live_server.url}{user_detail}", headers=get_headers(encoded) ) assert response.status_code == 403 @@ -345,9 +328,8 @@ def test_user_cannot_view_user_detail_role_change(live_server, jwt_keys): def test_admin_can_view_user_detail(live_server, jwt_keys): token_payload = get_token_payload(authorities=["ROLE_MANAGER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - headers = get_headers(utf8_bytes) + headers = get_headers(encoded) response = client.get(f"{live_server.url}", headers=headers) assert response.status_code == 200 @@ -365,9 +347,8 @@ def test_admin_can_view_other_user_detail(live_server, jwt_keys): encoded = jwt.encode( sensor01_token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - response = client.get(f"{live_server.url}", headers=get_headers(utf8_bytes)) + response = client.get(f"{live_server.url}", headers=get_headers(encoded)) assert response.status_code == 200 sensor02_token_payload = get_token_payload(authorities=["ROLE_MANAGER"]) @@ -375,7 +356,6 @@ def test_admin_can_view_other_user_detail(live_server, jwt_keys): encoded = jwt.encode( sensor02_token_payload, str(jwt_keys.private_key), algorithm="RS256" ) - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() sensor01_user = User.objects.get(username=sensor01_token_payload["user_name"]) @@ -383,7 +363,7 @@ def test_admin_can_view_other_user_detail(live_server, jwt_keys): kws.update(V1) user_detail = reverse("user-detail", kwargs=kws) response = client.get( - f"{live_server.url}{user_detail}", headers=get_headers(utf8_bytes) + f"{live_server.url}{user_detail}", headers=get_headers(encoded) ) assert response.status_code == 200 @@ -392,9 +372,8 @@ def test_admin_can_view_other_user_detail(live_server, jwt_keys): def test_token_hidden(live_server, jwt_keys): token_payload = get_token_payload(authorities=["ROLE_MANAGER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") client = RequestsClient() - headers = get_headers(utf8_bytes) + headers = get_headers(encoded) response = client.get(f"{live_server.url}", headers=headers) assert response.status_code == 200 @@ -416,10 +395,9 @@ def test_change_token_role_bad_signature(live_server, jwt_keys): """Make sure token modified after it was signed is rejected""" token_payload = get_token_payload(authorities=["ROLE_USER"]) encoded = jwt.encode(token_payload, str(jwt_keys.private_key), algorithm="RS256") - utf8_bytes = encoded.decode("utf-8") - first_period = utf8_bytes.find(".") - second_period = utf8_bytes.find(".", first_period + 1) - payload = utf8_bytes[first_period + 1 : second_period] + first_period = encoded.find(".") + second_period = encoded.find(".", first_period + 1) + payload = encoded[first_period + 1 : second_period] payload_bytes = payload.encode("utf-8") # must be multiple of 4 for b64decode for i in range(len(payload_bytes) % 4): @@ -431,8 +409,8 @@ def test_change_token_role_bad_signature(live_server, jwt_keys): payload_data["authorities"] = ["ROLE_MANAGER"] payload_data["userDetails"]["authorities"] = [{"authority": "ROLE_MANAGER"}] payload_str = json.dumps(payload_data) - encoded = base64.b64encode(payload_str.encode("utf-8")) - modified_payload = encoded.decode("utf-8") + modified_payload = base64.b64encode(payload_str.encode("utf-8")) + modified_payload = modified_payload.decode("utf-8") # remove padding if modified_payload.endswith("="): last_padded_index = len(modified_payload) - 1 @@ -442,11 +420,11 @@ def test_change_token_role_bad_signature(live_server, jwt_keys): break modified_payload = modified_payload[: last_padded_index + 1] modified_token = ( - utf8_bytes[:first_period] + encoded[:first_period] + "." + modified_payload + "." - + utf8_bytes[second_period + 1 :] + + encoded[second_period + 1 :] ) client = RequestsClient() response = client.get(f"{live_server.url}", headers=get_headers(modified_token)) From d7e9e64ada888370eb5a8001f71fe186e3dc8335 Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 10 Mar 2022 17:51:40 -0700 Subject: [PATCH 07/20] Rely on scos_actions for capabilities and don't copy so any updates propagate back to scos_actions. Added db_location_updated_handler to update capabilities object when db location is updated. Removes sensor def from action calls. --- src/capabilities/__init__.py | 31 ------------------------- src/capabilities/views.py | 10 ++++---- src/handlers/apps.py | 5 ++++ src/handlers/location_handler.py | 13 ++++++++++- src/handlers/tests/__init__.py | 0 src/handlers/tests/test_handlers.py | 36 +++++++++++++++++++++++++++++ src/scheduler/scheduler.py | 4 +--- src/scheduler/tests/utils.py | 4 ++-- 8 files changed, 61 insertions(+), 42 deletions(-) create mode 100644 src/handlers/tests/__init__.py create mode 100644 src/handlers/tests/test_handlers.py diff --git a/src/capabilities/__init__.py b/src/capabilities/__init__.py index 5e6fe417..e69de29b 100644 --- a/src/capabilities/__init__.py +++ b/src/capabilities/__init__.py @@ -1,31 +0,0 @@ -import copy -from scos_actions.capabilities import capabilities -from sensor import settings -from sensor.settings import SENSOR_DEFINITION_FILE - - -def get_sigmf_location(): - from status.models import Location - - try: - db_location = Location.objects.get(active=True) - return { - "x": db_location.longitude, - "y": db_location.latitude, - "z": db_location.height, - "description": db_location.description, - } - except Location.DoesNotExist: - return None - - -def get_capabilities(): - updated_capabilities = copy.deepcopy(capabilities) - updated_capabilities["sensor"]["id"] = settings.FQDN - location = get_sigmf_location() - if location: - updated_capabilities["sensor"]["location"] = location - else: - if "location" in updated_capabilities['sensor']: - del updated_capabilities["sensor"]["location"] - return updated_capabilities diff --git a/src/capabilities/views.py b/src/capabilities/views.py index 13d7d031..980e5eb0 100644 --- a/src/capabilities/views.py +++ b/src/capabilities/views.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- - +import copy import logging from rest_framework.decorators import api_view from rest_framework.response import Response import actions -from capabilities import get_capabilities +from scos_actions.capabilities import capabilities logger = logging.getLogger(__name__) @@ -29,6 +29,6 @@ def get_actions(include_admin_actions=False): def capabilities_view(request, version, format=None): """The capabilites of the sensor.""" filtered_actions = get_actions(include_admin_actions=request.user.is_staff) - capabilities = get_capabilities() - capabilities["actions"] = filtered_actions - return Response(capabilities) + filtered_capabilities = copy.deepcopy(capabilities) + filtered_capabilities["actions"] = filtered_actions + return Response(filtered_capabilities) diff --git a/src/handlers/apps.py b/src/handlers/apps.py index 6b18cd97..054d2d1f 100644 --- a/src/handlers/apps.py +++ b/src/handlers/apps.py @@ -18,6 +18,7 @@ class HandlersConfig(AppConfig): def ready(self): from handlers.location_handler import location_action_completed_callback + from handlers.location_handler import db_location_updated from handlers.measurement_handler import measurement_action_completed_callback from handlers.monitor_handler import monitor_action_completed_callback @@ -29,6 +30,10 @@ def ready(self): logger.debug( "location_action_completed_callback registered to location_action_completed" ) + post_save.connect(db_location_updated) + logger.debug( + "db_location_updated registered to post_save" + ) monitor_action_completed.connect(monitor_action_completed_callback) logger.debug( diff --git a/src/handlers/location_handler.py b/src/handlers/location_handler.py index 55d5d8c5..2e7163b7 100644 --- a/src/handlers/location_handler.py +++ b/src/handlers/location_handler.py @@ -1,5 +1,5 @@ from status.models import GPS_LOCATION_DESCRIPTION, Location - +from scos_actions.capabilities import capabilities def location_action_completed_callback(sender, **kwargs): """Update database and capabilities when GPS is synced or database is updated""" @@ -25,3 +25,14 @@ def location_action_completed_callback(sender, **kwargs): location.height = height location.active = True location.save() + + +def db_location_updated(sender, **kwargs): + instance = kwargs["instance"] + if isinstance(instance, Location): + if 'location' not in capabilities['sensor']: + capabilities['sensor']['location'] = {} + capabilities['sensor']['location']['x'] = instance.longitude + capabilities['sensor']['location']['y'] = instance.latitude + capabilities['sensor']['location']['z'] = instance.height + capabilities['sensor']['location']['description'] = instance.description diff --git a/src/handlers/tests/__init__.py b/src/handlers/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py new file mode 100644 index 00000000..f9329589 --- /dev/null +++ b/src/handlers/tests/test_handlers.py @@ -0,0 +1,36 @@ +import pytest +from django import conf +from status.models import Location +from scos_actions.capabilities import capabilities + +@pytest.mark.django_db +def test_db_location_update_handler(): + capabilities['sensor'] ={} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height=10 + location.longitude = 100 + location.latitude = -1 + location.description = 'test' + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' + +def test_db_location_update_handler_no_description(): + capabilities['sensor'] ={} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height=10 + location.longitude = 100 + location.latitude = -1 + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == '' + + diff --git a/src/scheduler/scheduler.py b/src/scheduler/scheduler.py index 39a83da2..58bf066e 100644 --- a/src/scheduler/scheduler.py +++ b/src/scheduler/scheduler.py @@ -8,7 +8,6 @@ import requests from django.utils import timezone from authentication import oauth -from capabilities import get_capabilities from schedule.models import ScheduleEntry from sensor import settings from tasks.consts import MAX_DETAIL_LEN @@ -150,9 +149,8 @@ def _call_task_action(self): try: logger.debug("running task {}/{}".format(entry_name, task_id)) - capabilities = get_capabilities() detail = self.task.action_caller( - schedule_entry_json, task_id, capabilities["sensor"] + schedule_entry_json, task_id ) self.delayfn(0) # let other threads run status = "success" diff --git a/src/scheduler/tests/utils.py b/src/scheduler/tests/utils.py index a82628ac..969f2578 100644 --- a/src/scheduler/tests/utils.py +++ b/src/scheduler/tests/utils.py @@ -100,7 +100,7 @@ def create_action(): """ flag = threading.Event() - def cb(schedule_entry_json, task_id, sensor_definition): + def cb(schedule_entry_json, task_id): flag.set() return "set flag" @@ -115,7 +115,7 @@ def cb(schedule_entry_json, task_id, sensor_definition): def create_bad_action(): - def bad_action(schedule_entry_json, task_id, sensor_definition): + def bad_action(schedule_entry_json, task_id): raise Exception(BAD_ACTION_STR) actions.by_name["bad_action"] = bad_action From 80f55f16afaef8bbb7aa043ac13e951f00901cc1 Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 17 Mar 2022 15:03:31 -0600 Subject: [PATCH 08/20] Adds db_location_deleted handler and registers it to the post_save signal to update capabilities if the active location is deleted. --- src/handlers/apps.py | 6 ++++++ src/handlers/location_handler.py | 8 ++++++++ src/handlers/tests/test_handlers.py | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/handlers/apps.py b/src/handlers/apps.py index 054d2d1f..1f262407 100644 --- a/src/handlers/apps.py +++ b/src/handlers/apps.py @@ -19,6 +19,7 @@ class HandlersConfig(AppConfig): def ready(self): from handlers.location_handler import location_action_completed_callback from handlers.location_handler import db_location_updated + from handlers.location_handler import db_location_deleted from handlers.measurement_handler import measurement_action_completed_callback from handlers.monitor_handler import monitor_action_completed_callback @@ -35,6 +36,11 @@ def ready(self): "db_location_updated registered to post_save" ) + post_delete.connect(db_location_deleted) + logger.debug( + "db_location_deleted registered to poste_delete" + ) + monitor_action_completed.connect(monitor_action_completed_callback) logger.debug( "monitor_action_completed_callback registered to monitor_action_completed" diff --git a/src/handlers/location_handler.py b/src/handlers/location_handler.py index 2e7163b7..8033a213 100644 --- a/src/handlers/location_handler.py +++ b/src/handlers/location_handler.py @@ -1,6 +1,7 @@ from status.models import GPS_LOCATION_DESCRIPTION, Location from scos_actions.capabilities import capabilities + def location_action_completed_callback(sender, **kwargs): """Update database and capabilities when GPS is synced or database is updated""" @@ -36,3 +37,10 @@ def db_location_updated(sender, **kwargs): capabilities['sensor']['location']['y'] = instance.latitude capabilities['sensor']['location']['z'] = instance.height capabilities['sensor']['location']['description'] = instance.description + + +def db_location_deleted(sender, **kwargs): + instance = kwargs["instance"] + if isinstance(instance, Location): + if 'location' in capabilities['sensor'] and instance.active: + capabilities['sensor']['location'] = None diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py index f9329589..f98a5dbb 100644 --- a/src/handlers/tests/test_handlers.py +++ b/src/handlers/tests/test_handlers.py @@ -19,6 +19,7 @@ def test_db_location_update_handler(): assert capabilities['sensor']['location']['z'] == 10 assert capabilities['sensor']['location']['description'] == 'test' +@pytest.mark.django_db def test_db_location_update_handler_no_description(): capabilities['sensor'] ={} capabilities['sensor']['location'] = {} @@ -34,3 +35,21 @@ def test_db_location_update_handler_no_description(): assert capabilities['sensor']['location']['description'] == '' +@pytest.mark.django_db +def test_db_location_deleted_handler(): + capabilities['sensor'] ={} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height=10 + location.longitude = 100 + location.latitude = -1 + location.description = 'test' + location.active = True + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' + location.delete() + assert capabilities['sensor']['location'] is None From 58930e2a9d41a3c8f5d643c25a71c87d47955c87 Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 17 Mar 2022 15:07:05 -0600 Subject: [PATCH 09/20] Adds test to ensure deletion of inactive locations doesn't update capabilities in memory. --- src/handlers/tests/test_handlers.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py index f98a5dbb..1a8eb538 100644 --- a/src/handlers/tests/test_handlers.py +++ b/src/handlers/tests/test_handlers.py @@ -53,3 +53,25 @@ def test_db_location_deleted_handler(): assert capabilities['sensor']['location']['description'] == 'test' location.delete() assert capabilities['sensor']['location'] is None + +@pytest.mark.django_db +def test_db_location_deleted_inactive_handler(): + capabilities['sensor'] ={} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height=10 + location.longitude = 100 + location.latitude = -1 + location.description = 'test' + location.active = False + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' + location.delete() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' From f0b370ef95407c66fdbb7beb252ce7e296366a6d Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 17 Mar 2022 15:52:28 -0600 Subject: [PATCH 10/20] Only update capabilities when saved or deleted if it is the active instance. --- src/handlers/location_handler.py | 2 +- src/handlers/tests/test_handlers.py | 40 +++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/handlers/location_handler.py b/src/handlers/location_handler.py index 8033a213..db4ccbda 100644 --- a/src/handlers/location_handler.py +++ b/src/handlers/location_handler.py @@ -30,7 +30,7 @@ def location_action_completed_callback(sender, **kwargs): def db_location_updated(sender, **kwargs): instance = kwargs["instance"] - if isinstance(instance, Location): + if isinstance(instance, Location) and instance.active: if 'location' not in capabilities['sensor']: capabilities['sensor']['location'] = {} capabilities['sensor']['location']['x'] = instance.longitude diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py index 1a8eb538..15620d8f 100644 --- a/src/handlers/tests/test_handlers.py +++ b/src/handlers/tests/test_handlers.py @@ -1,31 +1,49 @@ import pytest from django import conf -from status.models import Location +from status.models import Location from scos_actions.capabilities import capabilities + @pytest.mark.django_db def test_db_location_update_handler(): - capabilities['sensor'] ={} + capabilities['sensor'] = {} capabilities['sensor']['location'] = {} location = Location() location.gps = False - location.height=10 + location.height = 10 location.longitude = 100 location.latitude = -1 location.description = 'test' + location.active = True location.save() assert capabilities['sensor']['location']['x'] == 100 assert capabilities['sensor']['location']['y'] == -1 assert capabilities['sensor']['location']['z'] == 10 assert capabilities['sensor']['location']['description'] == 'test' + +@pytest.mark.django_db +def test_db_location_update_handler_not_active(): + capabilities['sensor'] = {} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height = 10 + location.longitude = 100 + location.latitude = -1 + location.active = False + location.description = 'test' + location.save() + assert len(capabilities['sensor']['location']) == 0 + + @pytest.mark.django_db def test_db_location_update_handler_no_description(): - capabilities['sensor'] ={} + capabilities['sensor'] = {} capabilities['sensor']['location'] = {} location = Location() location.gps = False - location.height=10 + location.height = 10 location.longitude = 100 location.latitude = -1 location.save() @@ -37,11 +55,11 @@ def test_db_location_update_handler_no_description(): @pytest.mark.django_db def test_db_location_deleted_handler(): - capabilities['sensor'] ={} + capabilities['sensor'] = {} capabilities['sensor']['location'] = {} location = Location() location.gps = False - location.height=10 + location.height = 10 location.longitude = 100 location.latitude = -1 location.description = 'test' @@ -54,22 +72,24 @@ def test_db_location_deleted_handler(): location.delete() assert capabilities['sensor']['location'] is None + @pytest.mark.django_db def test_db_location_deleted_inactive_handler(): - capabilities['sensor'] ={} + capabilities['sensor'] = {} capabilities['sensor']['location'] = {} location = Location() location.gps = False - location.height=10 + location.height = 10 location.longitude = 100 location.latitude = -1 location.description = 'test' - location.active = False + location.active = True location.save() assert capabilities['sensor']['location']['x'] == 100 assert capabilities['sensor']['location']['y'] == -1 assert capabilities['sensor']['location']['z'] == 10 assert capabilities['sensor']['location']['description'] == 'test' + location.active = False location.delete() assert capabilities['sensor']['location']['x'] == 100 assert capabilities['sensor']['location']['y'] == -1 From 8e401abe2b0ae24b8f2b806aeee0123b8916442d Mon Sep 17 00:00:00 2001 From: dboulware Date: Fri, 18 Mar 2022 13:02:56 -0600 Subject: [PATCH 11/20] Added handling and test for saving location when the current location is None. --- src/handlers/location_handler.py | 2 +- src/handlers/tests/test_handlers.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/handlers/location_handler.py b/src/handlers/location_handler.py index db4ccbda..74dccf38 100644 --- a/src/handlers/location_handler.py +++ b/src/handlers/location_handler.py @@ -31,7 +31,7 @@ def location_action_completed_callback(sender, **kwargs): def db_location_updated(sender, **kwargs): instance = kwargs["instance"] if isinstance(instance, Location) and instance.active: - if 'location' not in capabilities['sensor']: + if 'location' not in capabilities['sensor'] or capabilities['sensor']['location'] is None: capabilities['sensor']['location'] = {} capabilities['sensor']['location']['x'] = instance.longitude capabilities['sensor']['location']['y'] = instance.latitude diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py index 15620d8f..94e827e7 100644 --- a/src/handlers/tests/test_handlers.py +++ b/src/handlers/tests/test_handlers.py @@ -22,6 +22,24 @@ def test_db_location_update_handler(): assert capabilities['sensor']['location']['description'] == 'test' +@pytest.mark.django_db +def test_db_location_update_handler_current_location_none(): + capabilities['sensor'] = {} + capabilities['sensor']['location'] = None + location = Location() + location.gps = False + location.height = 10 + location.longitude = 100 + location.latitude = -1 + location.description = 'test' + location.active = True + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' + + @pytest.mark.django_db def test_db_location_update_handler_not_active(): capabilities['sensor'] = {} @@ -95,3 +113,27 @@ def test_db_location_deleted_inactive_handler(): assert capabilities['sensor']['location']['y'] == -1 assert capabilities['sensor']['location']['z'] == 10 assert capabilities['sensor']['location']['description'] == 'test' + + +@pytest.mark.django_db +def test_db_location_deleted_inactive_handler(): + capabilities['sensor'] = {} + capabilities['sensor']['location'] = {} + location = Location() + location.gps = False + location.height = 10 + location.longitude = 100 + location.latitude = -1 + location.description = 'test' + location.active = True + location.save() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' + location.active = False + location.delete() + assert capabilities['sensor']['location']['x'] == 100 + assert capabilities['sensor']['location']['y'] == -1 + assert capabilities['sensor']['location']['z'] == 10 + assert capabilities['sensor']['location']['description'] == 'test' From 8d2d09761d5136e6eeee3fa6ebede495fad5da38 Mon Sep 17 00:00:00 2001 From: dboulware Date: Fri, 18 Mar 2022 13:07:56 -0600 Subject: [PATCH 12/20] update gps in location db_location_updated. --- src/handlers/location_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/handlers/location_handler.py b/src/handlers/location_handler.py index 74dccf38..2c657c46 100644 --- a/src/handlers/location_handler.py +++ b/src/handlers/location_handler.py @@ -36,6 +36,7 @@ def db_location_updated(sender, **kwargs): capabilities['sensor']['location']['x'] = instance.longitude capabilities['sensor']['location']['y'] = instance.latitude capabilities['sensor']['location']['z'] = instance.height + capabilities['sensor']['location']['gps'] = instance.gps capabilities['sensor']['location']['description'] = instance.description From ae27a197737562a04b94ad5a5c923995743065cc Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Thu, 24 Mar 2022 11:20:29 -0600 Subject: [PATCH 13/20] update requirements --- src/requirements-dev.txt | 28 +++++++++++++++------------- src/requirements.txt | 23 +++++++++++++---------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index d04da6ec..24fb17eb 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -8,7 +8,7 @@ asgiref==3.5.0 # via # -r requirements.txt # django -aspy-refactor-imports==2.2.1 +aspy-refactor-imports==3.0.1 # via seed-isort-config attrs==21.4.0 # via @@ -55,7 +55,7 @@ coreschema==0.0.4 # drf-yasg coverage[toml]==6.3.2 # via pytest-cov -cryptography==36.0.1 +cryptography==36.0.2 # via # -r requirements-dev.in # -r requirements.txt @@ -112,13 +112,13 @@ filelock==3.6.0 # virtualenv gunicorn==20.1.0 # via -r requirements.txt -identify==2.4.11 +identify==2.4.12 # via pre-commit idna==3.3 # via # -r requirements.txt # requests -importlib-metadata==4.11.2 +importlib-metadata==4.11.3 # via # -r requirements.txt # click @@ -140,7 +140,7 @@ itypes==1.2.0 # via # -r requirements.txt # coreapi -jinja2==3.0.3 +jinja2==3.1.0 # via # -r requirements.txt # coreschema @@ -150,11 +150,11 @@ jsonschema==3.2.0 # via # -r requirements.txt # docker-compose -markupsafe==2.1.0 +markupsafe==2.1.1 # via # -r requirements.txt # jinja2 -marshmallow==3.14.1 +marshmallow==3.15.0 # via # -r requirements.txt # environs @@ -176,9 +176,10 @@ packaging==21.3 # via # -r requirements.txt # drf-yasg + # marshmallow # pytest # tox -paramiko==2.9.2 +paramiko==2.10.3 # via # -r requirements.txt # docker @@ -218,7 +219,7 @@ pyrsistent==0.18.1 # via # -r requirements.txt # jsonschema -pytest==7.0.1 +pytest==7.1.1 # via # pytest-cov # pytest-django @@ -233,7 +234,7 @@ python-dotenv==0.19.2 # -r requirements.txt # docker-compose # environs -pytz==2021.3 +pytz==2022.1 # via # -r requirements.txt # django @@ -265,7 +266,7 @@ ruamel-yaml-clib==0.2.6 # ruamel-yaml scipy==1.7.3 # via -r requirements.txt -scos_actions @ git+https://github.com/NTIA/scos-actions@refactor_dependencies +scos_actions @ git+https://github.com/NTIA/scos-actions@0.1.0 # via # -r requirements.txt # scos-usrp @@ -282,6 +283,7 @@ six==1.16.0 # django-session-timeout # dockerpty # jsonschema + # paramiko # python-dateutil # requests-mock # tox @@ -320,11 +322,11 @@ uritemplate==4.1.1 # -r requirements.txt # coreapi # drf-yasg -urllib3==1.26.8 +urllib3==1.26.9 # via # -r requirements.txt # requests -virtualenv==20.13.3 +virtualenv==20.13.4 # via # pre-commit # tox diff --git a/src/requirements.txt b/src/requirements.txt index 16bd41a0..b92638b4 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -27,7 +27,7 @@ coreschema==0.0.4 # via # coreapi # drf-yasg -cryptography==36.0.1 +cryptography==36.0.2 # via paramiko distro==1.7.0 # via docker-compose @@ -64,21 +64,21 @@ gunicorn==20.1.0 # via -r requirements.in idna==3.3 # via requests -importlib-metadata==4.11.2 +importlib-metadata==4.11.3 # via jsonschema inflection==0.5.1 # via drf-yasg itypes==1.2.0 # via coreapi -jinja2==3.0.3 +jinja2==3.1.0 # via coreschema jsonfield==3.1.0 # via -r requirements.in jsonschema==3.2.0 # via docker-compose -markupsafe==2.1.0 +markupsafe==2.1.1 # via jinja2 -marshmallow==3.14.1 +marshmallow==3.15.0 # via environs numpy==1.21.5 # via @@ -91,8 +91,10 @@ oauthlib==3.2.0 # -r requirements.in # requests-oauthlib packaging==21.3 - # via drf-yasg -paramiko==2.9.2 + # via + # drf-yasg + # marshmallow +paramiko==2.10.3 # via docker psycopg2-binary==2.9.3 # via -r requirements.in @@ -112,7 +114,7 @@ python-dotenv==0.19.2 # via # docker-compose # environs -pytz==2021.3 +pytz==2022.1 # via # django # djangorestframework @@ -137,7 +139,7 @@ ruamel-yaml-clib==0.2.6 # via ruamel-yaml scipy==1.7.3 # via scos-actions -scos_actions @ git+https://github.com/NTIA/scos-actions@refactor_dependencies +scos_actions @ git+https://github.com/NTIA/scos-actions@0.1.0 # via scos-usrp scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies # via -r requirements.in @@ -149,6 +151,7 @@ six==1.16.0 # django-session-timeout # dockerpty # jsonschema + # paramiko # python-dateutil # requests-mock # sigmf @@ -165,7 +168,7 @@ uritemplate==4.1.1 # via # coreapi # drf-yasg -urllib3==1.26.8 +urllib3==1.26.9 # via requests websocket-client==0.59.0 # via From 41695142038a49dadb24339213cd1233b5241118 Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Wed, 6 Apr 2022 15:05:27 -0600 Subject: [PATCH 14/20] update requirements, fix dependabot alerts --- src/requirements-dev.txt | 14 +++++++------- src/requirements.in | 2 +- src/requirements.txt | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 24fb17eb..69cc7d9c 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -19,7 +19,7 @@ bcrypt==3.2.0 # via # -r requirements.txt # paramiko -black==22.1.0 +black==22.3.0 # via -r requirements-dev.in cached-property==1.5.2 # via @@ -42,7 +42,7 @@ charset-normalizer==2.0.12 # via # -r requirements.txt # requests -click==8.0.4 +click==8.1.2 # via black coreapi==2.3.3 # via @@ -140,7 +140,7 @@ itypes==1.2.0 # via # -r requirements.txt # coreapi -jinja2==3.1.0 +jinja2==3.1.1 # via # -r requirements.txt # coreschema @@ -193,7 +193,7 @@ pluggy==1.0.0 # via # pytest # tox -pre-commit==2.17.0 +pre-commit==2.18.1 # via -r requirements-dev.in psycopg2-binary==2.9.3 # via -r requirements.txt @@ -229,7 +229,7 @@ pytest-django==4.5.2 # via -r requirements-dev.in python-dateutil==2.8.2 # via -r requirements.txt -python-dotenv==0.19.2 +python-dotenv==0.20.0 # via # -r requirements.txt # docker-compose @@ -326,7 +326,7 @@ urllib3==1.26.9 # via # -r requirements.txt # requests -virtualenv==20.13.4 +virtualenv==20.14.0 # via # pre-commit # tox @@ -335,7 +335,7 @@ websocket-client==0.59.0 # -r requirements.txt # docker # docker-compose -zipp==3.7.0 +zipp==3.8.0 # via # -r requirements.txt # importlib-metadata diff --git a/src/requirements.in b/src/requirements.in index c7b91666..f7c7e434 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -1,4 +1,4 @@ -django>=3.0, <4.0 +django>=3.1.13, <4.0 djangorestframework>=3.0, <4.0 django-session-timeout>=0.1, <1.0 docker-compose>=1.0, <2.0 diff --git a/src/requirements.txt b/src/requirements.txt index b92638b4..8c680cbb 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -70,7 +70,7 @@ inflection==0.5.1 # via drf-yasg itypes==1.2.0 # via coreapi -jinja2==3.1.0 +jinja2==3.1.1 # via coreschema jsonfield==3.1.0 # via -r requirements.in @@ -110,7 +110,7 @@ pyrsistent==0.18.1 # via jsonschema python-dateutil==2.8.2 # via scos-actions -python-dotenv==0.19.2 +python-dotenv==0.20.0 # via # docker-compose # environs @@ -174,7 +174,7 @@ websocket-client==0.59.0 # via # docker # docker-compose -zipp==3.7.0 +zipp==3.8.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: From f593a2d9a52bfb4cc8887704699063e55c2e2ecc Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 7 Apr 2022 07:46:44 -0600 Subject: [PATCH 15/20] Added scos-actions dependency and updated usrp dependency. --- src/requirements.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/requirements.in b/src/requirements.in index 0385134b..778aebb4 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -16,4 +16,5 @@ pyjwt~=1.7.1 raven==6.10.0 requests-mock==1.6.0 requests_oauthlib~=1.3.0 -scos_usrp @ git+https://github.com/NTIA/scos-usrp@preselector_test#egg=scos_usrp +scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0#egg=scos_actions +scos_usrp @ git+https://github.com/NTIA/scos-usrp#egg=scos_usrp From de91ba8542c13e0f261e0d6a04e1331ebcd749c0 Mon Sep 17 00:00:00 2001 From: dboulware Date: Thu, 7 Apr 2022 11:24:02 -0600 Subject: [PATCH 16/20] requirements. --- src/requirements-dev.txt | 10 +--------- src/requirements.in | 1 + src/requirements.txt | 25 ++++++------------------- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 69cc7d9c..8273fabf 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -166,7 +166,6 @@ numpy==1.21.5 # via # -r requirements-dev.in # -r requirements.txt - # scipy # scos-usrp oauthlib==3.2.0 # via @@ -227,8 +226,6 @@ pytest-cov==3.0.0 # via -r requirements-dev.in pytest-django==4.5.2 # via -r requirements-dev.in -python-dateutil==2.8.2 - # via -r requirements.txt python-dotenv==0.20.0 # via # -r requirements.txt @@ -264,9 +261,7 @@ ruamel-yaml-clib==0.2.6 # via # -r requirements.txt # ruamel-yaml -scipy==1.7.3 - # via -r requirements.txt -scos_actions @ git+https://github.com/NTIA/scos-actions@0.1.0 +scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0 # via # -r requirements.txt # scos-usrp @@ -274,8 +269,6 @@ scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies # via -r requirements.txt seed-isort-config==2.2.0 # via -r requirements-dev.in -sigmf @ git+https://github.com/NTIA/SigMF.git@multi-recording-archive - # via -r requirements.txt six==1.16.0 # via # -r requirements.txt @@ -284,7 +277,6 @@ six==1.16.0 # dockerpty # jsonschema # paramiko - # python-dateutil # requests-mock # tox # virtualenv diff --git a/src/requirements.in b/src/requirements.in index f7c7e434..049746d0 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -11,4 +11,5 @@ psycopg2-binary>=2.0, <3.0 pyjwt>=2.0, <3.0 requests-mock>=1.0, <2.0 requests_oauthlib>=1.0, <2.0 +scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0#egg=scos_actions scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies diff --git a/src/requirements.txt b/src/requirements.txt index 8c680cbb..0b26a98f 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -38,7 +38,6 @@ django==3.2.12 # djangorestframework # drf-yasg # jsonfield - # scos-actions # scos-usrp django-session-timeout==0.1.0 # via -r requirements.in @@ -81,11 +80,7 @@ markupsafe==2.1.1 marshmallow==3.15.0 # via environs numpy==1.21.5 - # via - # scipy - # scos-actions - # scos-usrp - # sigmf + # via scos-usrp oauthlib==3.2.0 # via # -r requirements.in @@ -108,8 +103,6 @@ pyparsing==3.0.7 # via packaging pyrsistent==0.18.1 # via jsonschema -python-dateutil==2.8.2 - # via scos-actions python-dotenv==0.20.0 # via # docker-compose @@ -132,19 +125,15 @@ requests-mock==1.9.3 requests-oauthlib==1.3.1 # via -r requirements.in ruamel-yaml==0.17.21 - # via - # drf-yasg - # scos-actions + # via drf-yasg ruamel-yaml-clib==0.2.6 # via ruamel-yaml -scipy==1.7.3 - # via scos-actions -scos_actions @ git+https://github.com/NTIA/scos-actions@0.1.0 - # via scos-usrp +scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0 + # via + # -r requirements.in + # scos-usrp scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies # via -r requirements.in -sigmf @ git+https://github.com/NTIA/SigMF.git@multi-recording-archive - # via scos-actions six==1.16.0 # via # bcrypt @@ -152,9 +141,7 @@ six==1.16.0 # dockerpty # jsonschema # paramiko - # python-dateutil # requests-mock - # sigmf # websocket-client sqlparse==0.4.2 # via django From dcb1f55857cbe69e46af7e04217eb08b2ca893f8 Mon Sep 17 00:00:00 2001 From: Justin Haze Date: Mon, 11 Apr 2022 13:48:26 -0600 Subject: [PATCH 17/20] fix logger action --- src/actions/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/logger.py b/src/actions/logger.py index d5c6c09a..cb25896b 100644 --- a/src/actions/logger.py +++ b/src/actions/logger.py @@ -23,7 +23,7 @@ class Logger(Action): def __init__(self, loglvl=LOGLVL_INFO): self.loglvl = loglvl - def __call__(self, schedule_entry_json, task_id, sensor_definition): + def __call__(self, schedule_entry_json, task_id): msg = "running test {name}/{tid}" schedule_entry_name = schedule_entry_json["name"] logger.log( From 95bfb399e0458f8c2a9977869e14421c5a25ef8e Mon Sep 17 00:00:00 2001 From: dboulware Date: Tue, 19 Apr 2022 08:15:03 -0600 Subject: [PATCH 18/20] Updated dependency of scos-usrp to 0.1.0 release. --- src/requirements-dev.txt | 2 +- src/requirements.in | 2 +- src/requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 8273fabf..a07f99ab 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -265,7 +265,7 @@ scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0 # via # -r requirements.txt # scos-usrp -scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@0.1.0 # via -r requirements.txt seed-isort-config==2.2.0 # via -r requirements-dev.in diff --git a/src/requirements.in b/src/requirements.in index 049746d0..6eb9b422 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -12,4 +12,4 @@ pyjwt>=2.0, <3.0 requests-mock>=1.0, <2.0 requests_oauthlib>=1.0, <2.0 scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0#egg=scos_actions -scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@0.1.0 \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt index 0b26a98f..e5131b62 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -132,7 +132,7 @@ scos_actions @ git+https://github.com/NTIA/scos-actions@1.0.0 # via # -r requirements.in # scos-usrp -scos_usrp @ git+https://github.com/NTIA/scos-usrp@refactor_dependencies +scos_usrp @ git+https://github.com/NTIA/scos-usrp@0.1.0 # via -r requirements.in six==1.16.0 # via From 41d13a0615a297e0d5d5f92dd1fd60597dc2bae2 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Wed, 20 Apr 2022 15:11:29 -0600 Subject: [PATCH 19/20] Deleted duplicate test. --- src/handlers/tests/test_handlers.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/handlers/tests/test_handlers.py b/src/handlers/tests/test_handlers.py index 94e827e7..8171dfca 100644 --- a/src/handlers/tests/test_handlers.py +++ b/src/handlers/tests/test_handlers.py @@ -115,25 +115,3 @@ def test_db_location_deleted_inactive_handler(): assert capabilities['sensor']['location']['description'] == 'test' -@pytest.mark.django_db -def test_db_location_deleted_inactive_handler(): - capabilities['sensor'] = {} - capabilities['sensor']['location'] = {} - location = Location() - location.gps = False - location.height = 10 - location.longitude = 100 - location.latitude = -1 - location.description = 'test' - location.active = True - location.save() - assert capabilities['sensor']['location']['x'] == 100 - assert capabilities['sensor']['location']['y'] == -1 - assert capabilities['sensor']['location']['z'] == 10 - assert capabilities['sensor']['location']['description'] == 'test' - location.active = False - location.delete() - assert capabilities['sensor']['location']['x'] == 100 - assert capabilities['sensor']['location']['y'] == -1 - assert capabilities['sensor']['location']['z'] == 10 - assert capabilities['sensor']['location']['description'] == 'test' From 8971e858985349a78a7d45ac958c5490fb2d1426 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Wed, 20 Apr 2022 15:13:09 -0600 Subject: [PATCH 20/20] fix typo --- src/handlers/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/apps.py b/src/handlers/apps.py index 1f262407..fdfc95f9 100644 --- a/src/handlers/apps.py +++ b/src/handlers/apps.py @@ -38,7 +38,7 @@ def ready(self): post_delete.connect(db_location_deleted) logger.debug( - "db_location_deleted registered to poste_delete" + "db_location_deleted registered to post_delete" ) monitor_action_completed.connect(monitor_action_completed_callback)