From 7b2e6edb600340ba60be34d4098d1179ba9483e3 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Fri, 3 Nov 2023 14:59:37 -0700 Subject: [PATCH 01/21] initial commit --- README.md | 2 +- manage.py | 22 + poetry.lock | 1658 +++++++++++++++++ pyproject.toml | 17 + tom_tns/__init__.py | 0 tom_tns/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 141 bytes tom_tns/__pycache__/admin.cpython-310.pyc | Bin 0 -> 182 bytes tom_tns/__pycache__/apps.cpython-310.pyc | Bin 0 -> 421 bytes tom_tns/__pycache__/forms.cpython-310.pyc | Bin 0 -> 7945 bytes tom_tns/__pycache__/models.cpython-310.pyc | Bin 0 -> 179 bytes tom_tns/__pycache__/urls.cpython-310.pyc | Bin 0 -> 326 bytes tom_tns/__pycache__/views.cpython-310.pyc | Bin 0 -> 2628 bytes tom_tns/admin.py | 3 + tom_tns/apps.py | 6 + tom_tns/forms.py | 357 ++++ tom_tns/migrations/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 152 bytes tom_tns/models.py | 3 + tom_tns/templates/tom_tns/tns_report.html | 14 + tom_tns/tests.py | 3 + tom_tns/urls.py | 9 + tom_tns/views.py | 60 + tom_tns_base/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 146 bytes .../__pycache__/settings.cpython-310.pyc | Bin 0 -> 4767 bytes tom_tns_base/__pycache__/urls.cpython-310.pyc | Bin 0 -> 949 bytes tom_tns_base/__pycache__/wsgi.cpython-310.pyc | Bin 0 -> 559 bytes tom_tns_base/asgi.py | 16 + tom_tns_base/settings.py | 274 +++ tom_tns_base/urls.py | 23 + tom_tns_base/wsgi.py | 16 + 31 files changed, 2482 insertions(+), 1 deletion(-) create mode 100755 manage.py create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 tom_tns/__init__.py create mode 100644 tom_tns/__pycache__/__init__.cpython-310.pyc create mode 100644 tom_tns/__pycache__/admin.cpython-310.pyc create mode 100644 tom_tns/__pycache__/apps.cpython-310.pyc create mode 100644 tom_tns/__pycache__/forms.cpython-310.pyc create mode 100644 tom_tns/__pycache__/models.cpython-310.pyc create mode 100644 tom_tns/__pycache__/urls.cpython-310.pyc create mode 100644 tom_tns/__pycache__/views.cpython-310.pyc create mode 100644 tom_tns/admin.py create mode 100644 tom_tns/apps.py create mode 100644 tom_tns/forms.py create mode 100644 tom_tns/migrations/__init__.py create mode 100644 tom_tns/migrations/__pycache__/__init__.cpython-310.pyc create mode 100644 tom_tns/models.py create mode 100644 tom_tns/templates/tom_tns/tns_report.html create mode 100644 tom_tns/tests.py create mode 100644 tom_tns/urls.py create mode 100644 tom_tns/views.py create mode 100644 tom_tns_base/__init__.py create mode 100644 tom_tns_base/__pycache__/__init__.cpython-310.pyc create mode 100644 tom_tns_base/__pycache__/settings.cpython-310.pyc create mode 100644 tom_tns_base/__pycache__/urls.cpython-310.pyc create mode 100644 tom_tns_base/__pycache__/wsgi.cpython-310.pyc create mode 100644 tom_tns_base/asgi.py create mode 100644 tom_tns_base/settings.py create mode 100644 tom_tns_base/urls.py create mode 100644 tom_tns_base/wsgi.py diff --git a/README.md b/README.md index b8ad17c..c5e9115 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# tns_report +# tom_tns TOMtoolkit module for reporting transients to the TNS diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..33a49e5 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tom_tns_base.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..f2b9010 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1658 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "asdf" +version = "2.15.0" +description = "Python implementation of the ASDF Standard" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf-2.15.0-py3-none-any.whl", hash = "sha256:11fae326d1f3a39a2c0c4cd42d4fcb789ef46f9ebe00c79564f34b066f05027a"}, + {file = "asdf-2.15.0.tar.gz", hash = "sha256:686f1c91ebf987d41f915cfb6aa70940d7ad17f87ede0be70463147ad2314587"}, +] + +[package.dependencies] +asdf-standard = ">=1.0.1" +asdf-transform-schemas = ">=0.3" +asdf-unit-schemas = ">=0.1" +importlib-metadata = ">=4.11.4" +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} +jmespath = ">=0.6.2" +jsonschema = ">=4.0.1,<4.18" +numpy = [ + {version = ">=1.20", markers = "python_version >= \"3.9\""}, + {version = ">=1.20,<1.25", markers = "python_version < \"3.9\""}, +] +packaging = ">=19" +pyyaml = ">=5.4.1" +semantic-version = ">=2.8" + +[package.extras] +all = ["lz4 (>=0.10)"] +docs = ["sphinx-asdf (>=0.1.4)", "tomli"] +tests = ["astropy (>=5.0.4)", "fsspec[http] (>=2022.8.2)", "gwcs (>=0.18.3)", "lz4 (>=0.10)", "psutil", "pytest (>=6)", "pytest-doctestplus", "pytest-openfiles", "pytest-remotedata"] + +[[package]] +name = "asdf-astropy" +version = "0.4.0" +description = "ASDF serialization support for astropy" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf-astropy-0.4.0.tar.gz", hash = "sha256:462b674601fe2c4a2f45c92bed3e21effd0d58addc7ec4335c545118bc582eb6"}, + {file = "asdf_astropy-0.4.0-py3-none-any.whl", hash = "sha256:561d47978e7012cd0488ff3386fea76102048cc8d3e75e470c1a39971eef86af"}, +] + +[package.dependencies] +asdf = ">=2.13" +asdf-coordinates-schemas = ">=0.1" +asdf-transform-schemas = ">=0.2.2" +astropy = ">=5.0.4" +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} +numpy = ">=1.20" +packaging = ">=19" + +[package.extras] +docs = ["docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf", "sphinx-astropy", "sphinx-automodapi", "tomli"] +test = ["coverage", "pytest-astropy", "scipy"] + +[[package]] +name = "asdf-coordinates-schemas" +version = "0.2.0" +description = "ASDF schemas for coordinates" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf_coordinates_schemas-0.2.0-py3-none-any.whl", hash = "sha256:5450a70f0b548cd7d90599d0b2b98285219f2376431809373eb461ce7192f207"}, + {file = "asdf_coordinates_schemas-0.2.0.tar.gz", hash = "sha256:e3f9a50872e13749a7eec2dc3ccb4af93280f5e5e20a8d70a3d83073de8dd5f4"}, +] + +[package.dependencies] +asdf = ">=2.12.1" + +[package.extras] +docs = ["astropy (>=5.0.4)", "docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf (>=0.1.3)", "sphinx-astropy", "sphinx-rtd-theme", "tomli"] +test = ["pytest"] + +[[package]] +name = "asdf-standard" +version = "1.0.3" +description = "The ASDF Standard schemas" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf_standard-1.0.3-py3-none-any.whl", hash = "sha256:1c628379c75f0663b6376a7e681d31b1b54391053e53447c9921fb04c26d41da"}, + {file = "asdf_standard-1.0.3.tar.gz", hash = "sha256:afd8ff9a70e7b17f6bcc64eb92a544867d5d4fe1f0076719142fdf62b96cfd44"}, +] + +[package.dependencies] +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} + +[package.extras] +docs = ["docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf (>=0.1.3)", "sphinx-astropy", "sphinx-rtd-theme", "toml"] +test = ["asdf (>=2.8.0)", "astropy (>=5.0.4)", "gwcs", "packaging (>=16.0)", "pytest", "pyyaml"] + +[[package]] +name = "asdf-transform-schemas" +version = "0.4.0" +description = "ASDF schemas for transforms" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf_transform_schemas-0.4.0-py3-none-any.whl", hash = "sha256:fed6cab061ee759c97a30bfe4ec06fdea7af72268b355301082e97994e4313e8"}, + {file = "asdf_transform_schemas-0.4.0.tar.gz", hash = "sha256:de7fdc3fee35fb957fc32957877a0e9d7dd4d2e851bd631a7259f11c2bd294ca"}, +] + +[package.dependencies] +asdf-standard = ">=1.0.1" +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} + +[package.extras] +docs = ["astropy (>=5.0.4)", "docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf (>=0.1.3)", "sphinx-astropy", "sphinx-rtd-theme", "tomli"] +test = ["asdf (>=2.8.0)", "asdf-astropy", "pytest", "scipy"] + +[[package]] +name = "asdf-unit-schemas" +version = "0.1.0" +description = "ASDF schemas for units" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf_unit_schemas-0.1.0-py3-none-any.whl", hash = "sha256:0e104b53c23a9e15541cfa5d101613d2724a9124fc56301324512659afb470d5"}, + {file = "asdf_unit_schemas-0.1.0.tar.gz", hash = "sha256:42b78d67213efe4ffd4529fb0e58d9c7a0dab5cbf8839b230f1bc0a446bff999"}, +] + +[package.dependencies] +asdf-standard = ">=1.0.1" +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} + +[package.extras] +docs = ["astropy (>=5.0.4)", "docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf (>=0.1.3)", "sphinx-astropy", "sphinx-rtd-theme", "tomli"] +test = ["asdf (>=2.8.0)", "asdf-astropy", "pytest", "scipy"] + +[[package]] +name = "asdf-wcs-schemas" +version = "0.2.0" +description = "ASDF WCS schemas" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asdf_wcs_schemas-0.2.0-py3-none-any.whl", hash = "sha256:695e7dfc97d5b384341506188550e1c31d06f7f0dad9af9b243f03a46bc54a42"}, + {file = "asdf_wcs_schemas-0.2.0.tar.gz", hash = "sha256:0a13420a5d1996cf3025e43e83d7beeac0917d9e58a9eeec4ef83edcdfe8c742"}, +] + +[package.dependencies] +asdf-standard = ">=1.0.1" +asdf-transform-schemas = ">=0.3.0" +asdf-unit-schemas = ">=0.1.0" +importlib-resources = {version = ">=3", markers = "python_version < \"3.9\""} + +[package.extras] +docs = ["astropy (>=5.0.4)", "docutils", "graphviz", "matplotlib", "sphinx", "sphinx-asdf (>=0.1.3)", "sphinx-astropy", "sphinx-rtd-theme", "tomli"] +test = ["asdf (>=2.8.0)", "asdf-astropy", "pytest (>=4.6.0)", "pytest-openfiles (>=0.5.0)"] + +[[package]] +name = "asgiref" +version = "3.7.2" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, + {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "astroplan" +version = "0.9.1" +description = "Observation planning package for astronomers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "astroplan-0.9.1.tar.gz", hash = "sha256:d98c5ea58f6131de391aa66c78e0b4d77649359b29dbc8fdee9385e0408c2f4b"}, +] + +[package.dependencies] +astropy = ">=4" +numpy = ">=1.17" +pytz = "*" +six = "*" + +[package.extras] +all = ["astroquery", "matplotlib (>=1.4)"] +docs = ["astroquery", "matplotlib (>=1.4)", "sphinx-astropy[confv2]", "sphinx-rtd-theme"] +plotting = ["astroquery", "matplotlib (>=1.4)"] +test = ["pytest-astropy", "pytest-mpl"] + +[[package]] +name = "astropy" +version = "5.2.2" +description = "Astronomy and astrophysics core library" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "astropy-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66522e897daf3766775c00ef5c63b69beb0eb359e1f45d18745d0f0ca7f29cc1"}, + {file = "astropy-5.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0ccf6f16cf7e520247ecc9d1a66dd4c3927fd60622203bdd1d06655ad81fa18f"}, + {file = "astropy-5.2.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3d0c37da922cdcb81e74437118fabd64171cbfefa06c7ea697a270e82a8164f2"}, + {file = "astropy-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04464e664a22382626ce9750ebe943b80a718dc8347134b9d138b63a2029f67a"}, + {file = "astropy-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f60cea0fa7cb6ebbd90373e48c07f5d459e95dfd6363f50e316e2db7755bead"}, + {file = "astropy-5.2.2-cp310-cp310-win32.whl", hash = "sha256:6c3abb2fa8ebaaad77875a02e664c1011f35bd0c0ef7d35a39b03c859de1129a"}, + {file = "astropy-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:185ade8c33cea34ba791b282e937686d98b4e205d4f343e686a4666efab2f6e7"}, + {file = "astropy-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f61c612e90e3dd3c075e99a61dedd53331c4577016c1d571aab00b95ca1731ab"}, + {file = "astropy-5.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3881e933ea870a27e5d6896443401fbf51e3b7e57c6356f333553f5ff0070c72"}, + {file = "astropy-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f210b5b4062030388437b9aca4bbf68f9063b2b27184006814a09fab41ac270e"}, + {file = "astropy-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e14b5a22f24ae5cf0404f21a4de135e26ca3c9cf55aefc5b0264a9ce24b53b0b"}, + {file = "astropy-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6768b3a670cdfff6c2416b3d7d1e4231839608299b32367e8b095959fc6733a6"}, + {file = "astropy-5.2.2-cp311-cp311-win32.whl", hash = "sha256:0aad85604cad40189b13d66bb46fb2a95df1a9095992071b31c3fa35b476fdbc"}, + {file = "astropy-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:ac944158794a88789a007892ad91db35da14f689da1ab37c33c8de770a27f717"}, + {file = "astropy-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6703860deecd384bba2d2e338f77a0e7b46672812d27ed15f95e8faaa89fcd35"}, + {file = "astropy-5.2.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:124ef2a9f9b1cdbc1a5d514f7e57538253bb67ad031215f5f5405fc4cd31a4cd"}, + {file = "astropy-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:800501cc626aef0780dfb66156619699e98cb48854ed710f1ae3708aaab79f6e"}, + {file = "astropy-5.2.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22396592aa9b1653d37d552d3c52a8bb27ef072d077fad43b64faf841b1dcbf3"}, + {file = "astropy-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093782b1f0177c3dd2c04181ec016d8e569bd9e862b48236e40b14e2a7399170"}, + {file = "astropy-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c664f9194a4a3cece6215f651a9bc22c3cbd1f52dd450bd4d94eaf36f13c06c"}, + {file = "astropy-5.2.2-cp38-cp38-win32.whl", hash = "sha256:35ce00bb3dbc8bf7c842a0635354a5023cb64ae9c1925aa9b54629cf7fed2abe"}, + {file = "astropy-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:8304b590b20f9c161db85d5eb65d4c6323b3370a17c96ae163b18a0071cbd68a"}, + {file = "astropy-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:855748c2f1aedee5d770dfec8334109f1bcd1c1cee97f5915d3e888f43c04acf"}, + {file = "astropy-5.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ef9acc55c5fd70c7c78370389e79fb044321e531ac1facb7bddeef89d3132e3"}, + {file = "astropy-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f30b5d153b9d119783b96b948a3e0c4eb668820c06d2e8ba72f6ea989e4af5c1"}, + {file = "astropy-5.2.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:530e6911a54a42e9f15b1a75dc3c699be3946c0b6ffdcfdcf4e14ae5fcfcd236"}, + {file = "astropy-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae3b383ac84fe6765e275f897f4010cc6afe6933607b7468561414dffdc4d915"}, + {file = "astropy-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b00a4cd49f8264a338b0020717bff104fbcca800bd50bf0a415d952078258a39"}, + {file = "astropy-5.2.2-cp39-cp39-win32.whl", hash = "sha256:b7167b9965ebd78b7c9da7e98a943381b25e23d041bd304ec2e35e8ec811cefc"}, + {file = "astropy-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:df81b8f23c5e906d799b47d2d8462707c745df38cafae0cd6674ef09e9a41789"}, + {file = "astropy-5.2.2.tar.gz", hash = "sha256:e6a9e34716bda5945788353c63f0644721ee7e5447d16b1cdcb58c48a96b0d9c"}, +] + +[package.dependencies] +numpy = ">=1.20" +packaging = ">=19.0" +pyerfa = ">=2.0" +PyYAML = ">=3.13" + +[package.extras] +all = ["asdf (>=2.10.0)", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "fsspec[http] (>=2022.8.2)", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "mpmath", "pandas", "pyarrow (>=5.0.0)", "pytest (>=7.0)", "pytz", "s3fs (>=2022.8.2)", "scipy (>=1.5)", "sortedcontainers", "typing-extensions (>=3.10.0.1)"] +docs = ["Jinja2 (>=3.0)", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "pytest (>=7.0)", "scipy (>=1.3)", "sphinx", "sphinx-astropy (>=1.6)", "sphinx-changelog (>=1.2.0)"] +recommended = ["matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "scipy (>=1.5)"] +test = ["pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist"] +test-all = ["coverage[toml]", "ipython (>=4.2)", "objgraph", "pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "sgp4 (>=2.3)", "skyfield (>=1.20)"] + +[[package]] +name = "astroquery" +version = "0.4.6" +description = "Functions and classes to access online astronomical data resources" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "astroquery-0.4.6-py3-none-any.whl", hash = "sha256:e1bc4996af7500370837d31491bd4ee7f0c954c78d24cd54fb1cceb755469094"}, + {file = "astroquery-0.4.6.tar.gz", hash = "sha256:307ca554cb734a0ca9a22f86f5effe7e413af913ae65e1578972d847b1fe13ee"}, +] + +[package.dependencies] +astropy = ">=4.0" +beautifulsoup4 = ">=4.3.2" +html5lib = ">=0.999" +keyring = ">=4.0" +numpy = ">=1.16" +pyvo = ">=1.1" +requests = ">=2.4.3" + +[package.extras] +all = ["aplpy", "astropy-healpix", "boto3", "mocpy (>=0.5.2)", "pyregion", "regions"] +docs = ["scipy", "sphinx-astropy (>=1.5)"] +test = ["flask", "jinja2", "matplotlib", "pytest-astropy", "pytest-dependency"] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +description = "Backport of the standard library zoneinfo module" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[package.extras] +tzdata = ["tzdata"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "crispy-bootstrap4" +version = "2022.1" +description = "Bootstrap4 template pack for django-crispy-forms" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "crispy-bootstrap4-2022.1.tar.gz", hash = "sha256:f18386c4d96180c1bb1212487d6e8f5a3dd1a9bb58d3032183973d4d130b55f9"}, + {file = "crispy_bootstrap4-2022.1-py3-none-any.whl", hash = "sha256:5241ab1dc2188c95560aa786439bcbedec7416e6b0f5a52dded82380810367ec"}, +] + +[package.dependencies] +django = ">=3.2" +django-crispy-forms = ">=1.14.0" + +[[package]] +name = "cryptography" +version = "41.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797"}, + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d"}, + {file = "cryptography-41.0.5-cp37-abi3-win32.whl", hash = "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936"}, + {file = "cryptography-41.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84"}, + {file = "cryptography-41.0.5.tar.gz", hash = "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "django" +version = "4.2.7" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Django-4.2.7-py3-none-any.whl", hash = "sha256:e1d37c51ad26186de355cbcec16613ebdabfa9689bbade9c538835205a8abbe9"}, + {file = "Django-4.2.7.tar.gz", hash = "sha256:8e0f1c2c2786b5c0e39fe1afce24c926040fad47c8ea8ad30aaf1188df29fc41"}, +] + +[package.dependencies] +asgiref = ">=3.6.0,<4" +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-bootstrap4" +version = "23.2" +description = "Bootstrap 4 for Django" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_bootstrap4-23.2-py3-none-any.whl", hash = "sha256:6c895cdda58de1360bf82cc9e31014ad158dad680198ac8366ca33c19efa972f"}, + {file = "django_bootstrap4-23.2.tar.gz", hash = "sha256:6b5080a4bce80fa666ac5ce5d4c77ad6fc3c0984d4257a0060d4077f515f62e5"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.8.0" +django = ">=3.2" + +[[package]] +name = "django-contrib-comments" +version = "2.2.0" +description = "The code formerly known as django.contrib.comments." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "django-contrib-comments-2.2.0.tar.gz", hash = "sha256:48de00f15677e016a216aeff205d6e00e4391c9a5702136c64119c472b7356da"}, + {file = "django_contrib_comments-2.2.0-py3-none-any.whl", hash = "sha256:2ca79060bbc8fc5b636981ef6e50f35ab83649af75fc1be47bf770636be3271c"}, +] + +[package.dependencies] +Django = ">=2.2" + +[[package]] +name = "django-crispy-forms" +version = "2.1" +description = "Best way to have Django DRY forms" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-crispy-forms-2.1.tar.gz", hash = "sha256:4d7ec431933ad4d4b5c5a6de4a584d24613c347db9ac168723c9aaf63af4bb96"}, + {file = "django_crispy_forms-2.1-py3-none-any.whl", hash = "sha256:d592044771412ae1bd539cc377203aa61d4eebe77fcbc07fbc8f12d3746d4f6b"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-filter" +version = "23.3" +description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-filter-23.3.tar.gz", hash = "sha256:015fe155582e1805b40629344e4a6cf3cc40450827d294d040b4b8c1749a9fa6"}, + {file = "django_filter-23.3-py3-none-any.whl", hash = "sha256:65bc5d1d8f4fff3aaf74cb5da537b6620e9214fb4b3180f6c560776b1b6dccd0"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-gravatar2" +version = "1.4.4" +description = "Essential Gravatar support for Django. Features helper methods, templatetags and a full test suite!" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "django-gravatar2-1.4.4.tar.gz", hash = "sha256:c813280967511ced93eea0359f60e5369c35b3311efe565c3e5d4ab35c10c9ee"}, + {file = "django_gravatar2-1.4.4-py2.py3-none-any.whl", hash = "sha256:545a6c2c5c624c7635dec29c7bc0be1a2cb89c9b8821af8616ae9838827cc35b"}, +] + +[[package]] +name = "django-guardian" +version = "2.4.0" +description = "Implementation of per object permissions for Django." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "django-guardian-2.4.0.tar.gz", hash = "sha256:c58a68ae76922d33e6bdc0e69af1892097838de56e93e78a8361090bcd9f89a0"}, + {file = "django_guardian-2.4.0-py3-none-any.whl", hash = "sha256:440ca61358427e575323648b25f8384739e54c38b3d655c81d75e0cd0d61b697"}, +] + +[package.dependencies] +Django = ">=2.2" + +[[package]] +name = "djangorestframework" +version = "3.14.0" +description = "Web APIs for Django, made easy." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, + {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, +] + +[package.dependencies] +django = ">=3.0" +pytz = "*" + +[[package]] +name = "fits2image" +version = "0.4.6" +description = "Common libraries for the conversion and scaling of fits images" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fits2image-0.4.6-py3-none-any.whl", hash = "sha256:79a1fdba40de10b1378c03463ba7f2ec67b6b14c3f2b320945584919108ebe1e"}, + {file = "fits2image-0.4.6.tar.gz", hash = "sha256:1721ee540a725e3d29a3835d121209581e447f54d639b7050b65b0019b90c76f"}, +] + +[package.dependencies] +astropy = "*" +numpy = "*" +Pillow = "*" + +[[package]] +name = "gwcs" +version = "0.18.3" +description = "Generalized World Coordinate System" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "gwcs-0.18.3-py3-none-any.whl", hash = "sha256:e9b9f92049870573c3d1c2d7a9e84a8b4136da673c9004a1b827db5b93d54967"}, + {file = "gwcs-0.18.3.tar.gz", hash = "sha256:a466a057624c6a0507468b00c7991d3eef848c64f41d6ad11344c8fa6a99fe55"}, +] + +[package.dependencies] +asdf = ">=2.8.1" +asdf-astropy = ">=0.2.0" +asdf-wcs-schemas = "*" +astropy = ">=5.1" +numpy = "*" +scipy = "*" + +[package.extras] +docs = ["sphinx", "sphinx-asdf", "sphinx-astropy", "sphinx-automodapi", "sphinx-rtd-theme", "stsci-rtd-theme"] +test = ["ci-watson (>=0.3.0)", "codecov", "pytest (>=4.6.0)", "pytest-astropy"] + +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["chardet (>=2.2)", "genshi", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.8.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "importlib-resources" +version = "6.1.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.1.0-py3-none-any.whl", hash = "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83"}, + {file = "importlib_resources-6.1.0.tar.gz", hash = "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] + +[[package]] +name = "jaraco-classes" +version = "3.3.0" +description = "Utility functions for Python class constructs" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jaraco.classes-3.3.0-py3-none-any.whl", hash = "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb"}, + {file = "jaraco.classes-3.3.0.tar.gz", hash = "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "keyring" +version = "24.2.0" +description = "Store and access your passwords safely." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "keyring-24.2.0-py3-none-any.whl", hash = "sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6"}, + {file = "keyring-24.2.0.tar.gz", hash = "sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +completion = ["shtab"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[[package]] +name = "markdown" +version = "3.5.1" +description = "Python implementation of John Gruber's Markdown." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, + {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "more-itertools" +version = "10.1.0" +description = "More routines for operating on iterables, beyond itertools" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.1.0.tar.gz", hash = "sha256:626c369fa0eb37bac0291bce8259b332fd59ac792fa5497b59837309cd5b114a"}, + {file = "more_itertools-10.1.0-py3-none-any.whl", hash = "sha256:64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6"}, +] + +[[package]] +name = "ndcube" +version = "2.1.3" +description = "A package for multi-dimensional contiguous and non-contiguous coordinate aware arrays." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ndcube-2.1.3-py3-none-any.whl", hash = "sha256:ef5a7569910cd3618320609262bd3de807d9b2d599d2d16038f63beac6ee6a6b"}, + {file = "ndcube-2.1.3.tar.gz", hash = "sha256:9f815dcf325c194458099daf7f4d3e66a49a547163a3e461fb1b0b83f3113416"}, +] + +[package.dependencies] +astropy = ">=4.2" +gwcs = ">=0.15" +numpy = ">1.17" + +[package.extras] +all = ["matplotlib (>=3.2)", "mpl-animators (>=1.0)", "reproject (>=0.7.1)"] +dev = ["dask", "matplotlib", "matplotlib (>=3.2)", "mpl-animators (>=1.0)", "pytest", "pytest-astropy", "pytest-doctestplus (>=0.9.0)", "pytest-mpl (>=0.12)", "reproject (>=0.7.1)", "scipy", "sphinx", "sphinx-automodapi", "sphinx-changelog (>=1.1.0)", "sphinx-gallery", "sphinxext-opengraph", "sunpy (>=4.0.0)", "sunpy-sphinx-theme"] +docs = ["matplotlib", "pytest-doctestplus (>=0.9.0)", "sphinx", "sphinx-automodapi", "sphinx-changelog (>=1.1.0)", "sphinx-gallery", "sphinxext-opengraph", "sunpy (>=4.0.0)", "sunpy-sphinx-theme"] +plotting = ["matplotlib (>=3.2)", "mpl-animators (>=1.0)"] +reproject = ["reproject (>=0.7.1)"] +tests = ["dask", "pytest", "pytest-astropy", "pytest-mpl (>=0.12)", "scipy", "sunpy (>=4.0.0)"] + +[[package]] +name = "numpy" +version = "1.24.4" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pillow" +version = "10.1.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, + {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, + {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, + {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, + {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, + {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, + {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, + {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + +[[package]] +name = "plotly" +version = "5.18.0" +description = "An open-source, interactive data visualization library for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "plotly-5.18.0-py3-none-any.whl", hash = "sha256:23aa8ea2f4fb364a20d34ad38235524bd9d691bf5299e800bca608c31e8db8de"}, + {file = "plotly-5.18.0.tar.gz", hash = "sha256:360a31e6fbb49d12b007036eb6929521343d6bee2236f8459915821baefa2cbb"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pyerfa" +version = "2.0.0.3" +description = "Python bindings for ERFA" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyerfa-2.0.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:676515861ca3f0cb9d7e693389233e7126413a5ba93a0cc4d36b8ca933951e8d"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a438865894d226247dcfcb60d683ae075a52716504537052371b2b73458fe4fc"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73bf7d23f069d47632a2feeb1e73454b10392c4f3c16116017a6983f1f0e9b2b"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:780b0f90adf500b8ba24e9d509a690576a7e8287e354cfb90227c5963690d3fc"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5447bb45ddedde3052693c86b941a4908f5dbeb4a697bda45b5b89de92cfb74a"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c24e7960c6cdd3fa3f4dba5f3444a106ad48c94ff0b19eebaee06a142c18c52"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-win32.whl", hash = "sha256:170a83bd0243da518119b846f296cf33fa03f1f884a88578c1a38560182cf64e"}, + {file = "pyerfa-2.0.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:51aa6e0faa4aa9ad8f0eef1c47fec76c5bebc0da7023a436089bdd6e5cfd625f"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fa9fceeb78057bfff7ae3aa6cdad3f1b193722de22bdbb75319256f4a9e2f76"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a8a2029fc62ff2369d01219f66a5ce6aed35ef33eddb06118b6c27e8573a9ed8"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da888da2c8db5a78273fbf0af4e74f04e2d312d371c3c021cf6c3b14fa60fe3b"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7354753addba5261ec1cbf1ba45784ed3a5c42da565ecc6e0aa36b7a17fa4689"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b55f7278c1dd362648d7956e1a5365ade5fed2fe5541b721b3ceb5271128892"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:23e5efcf96ed7161d74f79ca261d255e1f36988843d22cd97d8f60fe9c868d44"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-win32.whl", hash = "sha256:f0e9d0b122c454bcad5dbd0c3283b200783031d3f99ca9c550f49a7a7d4c41ea"}, + {file = "pyerfa-2.0.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:09af83540e23a7d61a8368b0514b3daa4ed967e1e52d0add4f501f58c500dd7f"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a07444fd53a5dd18d7955f86f8d9b1be9a68ceb143e1145c0019a310c913c04"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf7364e475cff1f973e2fcf6962de9df9642c8802b010e29b2c592ae337e3c5"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8458421166f6ffe2e259aaf4aaa6e802d6539649a40e3194a81d30dccdc167a"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96ea688341176ae6220cc4743cda655549d71e3e3b60c5a99d02d5912d0ddf55"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d56f6b5a0a3ed7b80d630041829463a872946df277259b5453298842d42a54a4"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-win32.whl", hash = "sha256:3ecb598924ddb4ea2b06efc6f1e55ca70897ed178a690e2eaa1e290448466c7c"}, + {file = "pyerfa-2.0.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1033fdb890ec70d3a511e20a464afc8abbea2180108f27b14d8f1d1addc38cbe"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d8c0dbb17119e52def33f9d6dbf2deaf2113ed3e657b6ff692df9b6a3598397"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8a1edd2cbe4ead3bf9a51e578d5d83bdd7ab3b3ccb69e09b89a4c42aa5b35ffb"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04c3b715c924b6f972dd440a94a701a16a07700bc8ba9e88b1df765bdc36ad0"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d01c341c45b860ee5c7585ef003118c8015e9d65c30668d2f5bf657e1dcdd68"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24d89ead30edc6038408336ad9b696683e74c4eef550708fca6afef3ecd5b010"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b8c5e74d48a505a014e855cd4c7be11604901d94fd6f34b685f6720b7b20ed8"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-win32.whl", hash = "sha256:2ccba04de166d81bdd3adcf10428d908ce2f3a56ed1c2767d740fec12680edbd"}, + {file = "pyerfa-2.0.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3df87743e27588c5bd5e1f3a886629b3277fdd418059ca048420d33169376775"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88aa1acedf298d255cc4b0740ee11a3b303b71763dba2f039d48abf0a95cf9df"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06d4f08e96867b1fc3ae9a9e4b38693ed0806463288efc41473ad16e14774504"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1819e0d95ff8dead80614f8063919d82b2dbb55437b6c0109d3393c1ab55954"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f1097ac2ee8c15a2a636cdfb99340d708574d66f4610456bd457d1e6b852f4"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36f42ee01a62c6cbba58103e6f8e600b21ad3a71262dccf03d476efb4a20ea71"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ecd6167b48bb8f1922fae7b49554616f2e7382748a4320ad46ebd7e2cc62f3d"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-win32.whl", hash = "sha256:7f9eabfefa5317ce58fe22480102902f10f270fc64a5636c010f7c0b7e0fb032"}, + {file = "pyerfa-2.0.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:4ea7ca03ecc440224c2bed8fb136fadf6cf8aea8ba67d717f635116f30c8cc8c"}, + {file = "pyerfa-2.0.0.3.tar.gz", hash = "sha256:d77fbbfa58350c194ccb99e5d93aa05d3c2b14d5aad8b662d93c6ad9fff41f39"}, +] + +[package.dependencies] +numpy = ">=1.17" + +[package.extras] +docs = ["sphinx-astropy (>=1.3)"] +test = ["pytest", "pytest-doctestplus (>=0.7)"] + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[package]] +name = "pyvo" +version = "1.4.2" +description = "Astropy affiliated package for accessing Virtual Observatory data and services" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyvo-1.4.2-py3-none-any.whl", hash = "sha256:74142901862b2c70b2eb0505355fe4ce4cf6907d290bb2a67b4f80c087b7a217"}, + {file = "pyvo-1.4.2.tar.gz", hash = "sha256:66fe298865acfd725bc5f18750772d7f1b8899b8e5bb1775721520bed57d95cb"}, +] + +[package.dependencies] +astropy = ">=4.1" +requests = "*" + +[package.extras] +all = ["pillow"] +docs = ["sphinx-astropy"] +test = ["pytest-astropy", "pytest-doctestplus (>=0.13)", "requests-mock"] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "scipy" +version = "1.9.3" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, + {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, + {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, + {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, + {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, + {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, + {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, +] + +[package.dependencies] +numpy = ">=1.18.5,<1.26.0" + +[package.extras] +dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] +test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "semantic-version" +version = "2.10.0" +description = "A library implementing the 'SemVer' scheme." +category = "main" +optional = false +python-versions = ">=2.7" +files = [ + {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, + {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, +] + +[package.extras] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "specutils" +version = "1.12.0" +description = "Package for spectroscopic astronomical data" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "specutils-1.12.0-py3-none-any.whl", hash = "sha256:bcec31b46d8c4969580b45297a96444810f451fdde9a0eaf82e18b9a807d442c"}, + {file = "specutils-1.12.0.tar.gz", hash = "sha256:1b0bb6adcb6500e662dfbf54d334dc6bf20cc9fa7e8e47ce05d2b3703000d882"}, +] + +[package.dependencies] +asdf = ">=2.14.4" +asdf-astropy = ">=0.3" +astropy = ">=5.1" +gwcs = ">=0.18" +ndcube = ">=2.0" +numpy = ">=1.19" +scipy = ">=1.3" + +[package.extras] +docs = ["graphviz", "matplotlib", "sphinx-astropy"] +jwst = ["stdatamodels (>=1.1.0)"] +test = ["coverage", "graphviz", "matplotlib", "pytest-astropy", "pytest-cov", "spectral-cube"] + +[[package]] +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "tenacity" +version = "8.2.3" +description = "Retry code until it succeeds" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "tomtoolkit" +version = "2.14.6" +description = "The TOM Toolkit and base modules" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "tomtoolkit-2.14.6-py3-none-any.whl", hash = "sha256:d46eaef334651143214c670e1bb9b8aaeb43ddb3208e621120744002496b08af"}, + {file = "tomtoolkit-2.14.6.tar.gz", hash = "sha256:002f68f69f847bd4ded12887e12d06408089bdcb186f83f2148c6c0f35c33aee"}, +] + +[package.dependencies] +astroplan = ">=0.8,<1.0" +astropy = ">=5.0" +astroquery = ">=0.4.2" +beautifulsoup4 = ">=4.9,<5.0" +crispy-bootstrap4 = ">=2022.0,<2023.0" +django = ">=3.1,<5" +django-bootstrap4 = ">=3,<24" +django-contrib-comments = ">=2.0,<3.0" +django-crispy-forms = ">=2.0,<3.0" +django-extensions = ">=3.1,<4.0" +django-filter = ">=21,<24" +django-gravatar2 = ">=1.4,<2.0" +django-guardian = ">=2.3,<3.0" +djangorestframework = ">=3.12,<4.0" +fits2image = ">=0.4.2" +Markdown = ">=3.4,<4.0" +numpy = ">=1.20,<2.0" +pillow = ">=9.2,<11.0" +plotly = ">=5.0,<6.0" +python-dateutil = ">=2.8,<3.0" +requests = ">=2.25,<3.0" +specutils = ">=1.8,<2.0" + +[package.extras] +docs = ["recommonmark (>=0.7,<1.0)", "sphinx (>=4,<8)", "tom-antares", "tom-scimma"] +test = ["factory-boy (>=3.2.1,<3.4.0)"] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8.1" +content-hash = "0dade5a763e3cd6cdc3f0bd32d465996bfc4d896eb884a988b3cd8910db079f8" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6889f7c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "tom-tns" +version = "0.1.0" +description = "TOMtoolkit module for reporting transients to the TNS" +authors = ["Joseph Chatelain "] +license = "GPL-3.0-or-later" +readme = "README.md" +packages = [{include = "tom_tns"}] + +[tool.poetry.dependencies] +python = "^3.8.1" +tomtoolkit = "^2.14" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tom_tns/__init__.py b/tom_tns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tom_tns/__pycache__/__init__.cpython-310.pyc b/tom_tns/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77c6e9c285d35d661fc7fbbf48c1c0775de1b6db GIT binary patch literal 141 zcmd1j<>g`kf-4n{sUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6v2KO;XkRX-~^ zBe5hkCowZmKRvTVza&35z9g?0PRGY*=4F<|$LkeT-r}&y%}*)KNwou+RLleg`kf-4n{srEqnF^Gc(44TX@fuanW zjJH@5Q*tx&{4|-O_)@YG^V0M6lJoOQiZYXmKnAR2C}IXuVB(jqenx(7s(w~-Mq){7 tPGV-BetKq!eo20Ad`Vt0oCcexS5SG2!zMRBr8FnijuB`=F~~Fl1^|=KDwhBN literal 0 HcmV?d00001 diff --git a/tom_tns/__pycache__/apps.cpython-310.pyc b/tom_tns/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e4ef559766f29f7b442722e0df5146aaf727b4e GIT binary patch literal 421 zcmYjNy-ve05I)B#g#Ik3D-vrYl)L~!El99+p;H#i^qe%66NfsHfE8YV$KZXKyfX0$ zOq`nvdeVLO|J~`#WHJPj&!@S1C;R1tLkTIEk=k#~@4t+_3gMt9trO0>0I!~+)zQOimR;f6v`bEmyO7UHp!dS5w3mCH`I zLK20@cO0RweZbZ~V+TJM>Dp8(-K^HKRa%yHT9j?NU-f5Y)7YeW-g27rud+u35riya lyWAq?ZnQt&_ih8fz<2+z>b()=Ty)vreC3~HJ>eLp{1=T%W|jZ| literal 0 HcmV?d00001 diff --git a/tom_tns/__pycache__/forms.cpython-310.pyc b/tom_tns/__pycache__/forms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac3069150c5999c88a2607080ac142e790dd826a GIT binary patch literal 7945 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 NQ_GlIQNA`i_P^Td>%ss4 literal 0 HcmV?d00001 diff --git a/tom_tns/__pycache__/models.cpython-310.pyc b/tom_tns/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0269da6d1480942c3e7c98accca832950ab3d0ff GIT binary patch literal 179 zcmd1j<>g`kf-4n{sg^+cF^Gc(44TX@fuanW zjJMcw^HWlDiv2X1ZgHk$CFZ5)>!lS5Z<4p2~7jOM!+T)k0OHLv6qU6T*BfmNy`3&-K}EZ!FSN3;1liDlRiQ( zc2jeD80>2jFLHP9ufp8q!@r;fQ026mBt&oJgW*OM%li5tt`} zzNT-8r+oH8d)&@=wnkO{1xi%mkW9=EfW_TveQT_`AH`G1yiuLkZkJgx8S9(f{EsYQ z7)6>Xv8u9A@D0Hy#N3J literal 0 HcmV?d00001 diff --git a/tom_tns/__pycache__/views.cpython-310.pyc b/tom_tns/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0633ab46d00d9024c123cfa1c65e5c9e9d2658ef GIT binary patch literal 2628 zcmb7GNpBoQ6z=M6ww@(U;;g_R5Mm^lL?D3(QIyD8u#`+9kHet^YIVA5?4I;8sj7Bt zYkW$ioWg}8AlVX^{0)9aT{&^i4GD?&YR0PoiEj1ldR4Eg-}=2Sn$5ZeSN8E8{#VVi zen;l$RRQKEJaQKZvlvOOj8H;wY^QeSP$zS#n|ahTb581K6txaD`6_p>U&K05-h4 ze1G-MO1SjR!{wzrL%oP|%Y)(S%Hs!j2CE127jb^+{;lC~`R?-4t<~j+gZ(x5p1&H2 zHLhZU2F))A-Yz^#@Vs3Fl2}vg`^tCr4%xA%_LTf+KeLC{8%wlc5v;cRSG6-rn0E6h z<7U>7KR>;)US#~rI9>-2q*0PzSxeLvRb-*c<-vOc93}gcEnUCPl32w>Ho1Y>^412j zXo=JC`d0--6IKsD*>gl2Ca9AnOq@ElBo>gx!x}g^1LS|6s-5^hP7MxDg~7249g3H{ z34~jeFpJuZP=~wJV>b0)StG(6=Dw`b3iFu%(xTN_qS6|qXdU?aD~JhgK=L(o146o= zVcXa0UAQpWx(eLfU5|2>a@iFoU6OpQ8+DZla#s}~!n?4$Z#He8I|xNC!`X)Zy2{dj z^b2}T_JJttg*7J3g|r~Qk{=)`ls$IF?$jE4j9h?%eNCn|bFN!cNRys7(Dvl%8zRoZ zUhU6xF%j$e+LPsk3*CVn4C5kKd{c!i0vYtCt`twkP#01=u&({5&%q7}3Fk_lj>Nl0 zWWMVS(X}v)(@4rNoaKXw_azX*9F{(i1#+IaWc%_F|MgFP`IzkpOZ%lDe*;E=loG7WXG90yNx|dgzygbtQ~ji zj+@}C3i!$$w^;Q8_>9%&T-|Vu8MkY*V2|v0Q%|)wEb&0Kryi_(BPdB09LO|5#-IAC z12UoNOns2?jZ70{FdnUeiG(;0=4>y1(k=4t6TTj$qi!+k4sYFid~4+)7PW}9!+4DF zgunw)s!!Gn$QZ7~7C=W0;}oi`38$UniR5A{w3a8TWSqLAwA|Dl7orff zj!bCA+8-q;982p-E|n-Vo~zy|6L@hMbQA9&!61tdkbDS4R|J1na;dai!WVTDqc2B;O~6vAeFS3ITT@8}?4VLmf!oohVjB8mI(8=(Y7TcC@D2Sj(~l5!ntI4+em@uuyyG7>C_@_vRt zBvK&K(dN7#nCMAkw1B@gOJF+|*Z}B)5i$Kl_Q7i$R5G3KGZgt8Ma-Ybi9Ze~AY;yv iR5GqWFX-y^nfq^Gc1Uc@0hB-h_jx-YEeDhTqVqRel7trk literal 0 HcmV?d00001 diff --git a/tom_tns/admin.py b/tom_tns/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/tom_tns/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/tom_tns/apps.py b/tom_tns/apps.py new file mode 100644 index 0000000..83bd424 --- /dev/null +++ b/tom_tns/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TomTnsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'tom_tns' diff --git a/tom_tns/forms.py b/tom_tns/forms.py new file mode 100644 index 0000000..214e4ea --- /dev/null +++ b/tom_tns/forms.py @@ -0,0 +1,357 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Row, Column, Submit, HTML +from crispy_forms.bootstrap import AppendedText +from datetime import datetime +import json +import os + + +TNS_FILTER_CHOICES = [ + (0, "Other"), + (1, "Clear"), + (10, "U-Johnson"), + (11, "B-Johnson"), + (12, "V-Johnson"), + (13, "R-Cousins"), + (14, "I-Cousins"), + (15, "J-Bessel"), + (16, "H-Bessel"), + (17, "K-Bessel"), + (18, "L"), + (19, "M"), + (20, "u-Sloan"), + (21, "g-Sloan"), + (22, "r-Sloan"), + (23, "i-Sloan"), + (24, "z-Sloan"), + (25, "y-P1"), + (26, "w-P1"), +] + +TNS_INSTRUMENT_CHOICES = [ + (0, "Other"), +] + +TNS_CLASSIFICATION_CHOICES = [ + (0, "Other"), + (1, "SN"), + (2, "SN I"), + (3, "SN Ia"), + (4, "SN Ib"), + (5, "SN Ic"), + (6, "SN Ib/c"), + (7, "SN Ic-BL"), + (9, "SN Ibn"), + (10, "SN II"), + (11, "SN IIP"), + (12, "SN IIL"), + (13, "SN IIn"), + (14, "SN IIb"), + (15, "SN I-faint"), + (16, "SN I-rapid"), + (18, "SLSN-I"), + (19, "SLSN-II"), + (20, "SLSN-R"), + (23, "Afterglow"), + (24, "LBV"), + (25, "ILRT"), + (26, "Nova"), + (27, "CV"), + (28, "Varstar"), + (29, "AGN"), + (30, "Galaxy"), + (31, "QSO"), + (40, "Light-Echo"), + (50, "Std-spec"), + (60, "Gap"), + (61, "Gap I"), + (62, "Gap II"), + (65, "LRN"), + (66, "FBOT"), + (70, "Kilonova"), + (99, "Impostor-SN"), + (100, "SN Ia-pec"), + (102, "SN Ia-SC"), + (103, "SN Ia-91bg-like"), + (104, "SN Ia-91T-like"), + (105, "SN Iax[02cx-like]"), + (106, "SN Ia-CSM"), + (107, "SN Ib-pec"), + (108, "SN Ic-pec"), + (109, "SN Icn"), + (110, "SN Ibn/Icn"), + (111, "SN II-pec"), + (112, "SN IIn-pec"), + (115, "SN Ib-Ca-rich"), + (116, "SN Ib/c-Ca-rich"), + (117, "SN Ic-Ca-rich"), + (118, "SN Ia-Ca-rich"), + (120, "TDE"), + (121, "TDE-H"), + (122, "TDE-He"), + (123, "TDE-H-He"), + (200, "WR"), + (201, "WR-WN"), + (202, "WR-RC"), + (203, "WR-WO"), + (210, "M dwarf"), +] + + +class TNSReportForm(forms.Form): + ra = forms.FloatField(label='R.A.') + dec = forms.FloatField(label='Dec.') + reporting_group = forms.ChoiceField(choices=[ + (66, "SAGUARO"), + ], initial=(66, "SAGUARO")) + discovery_data_source = forms.ChoiceField(choices=[ + (66, "SAGUARO"), + ], initial=(66, "SAGUARO")) + reporter = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) + discovery_date = forms.DateTimeField(initial=datetime.utcnow()) + at_type = forms.ChoiceField(choices=[ + (0, "Other"), + (1, "PSN"), + (2, "PNV"), + (3, "AGN"), + (4, "NUC"), + (5, "FRB"), + ], initial=(1, "PSN"), label='AT type') + archive = forms.ChoiceField(choices=[ + (0, "Other"), + (1, "SDSS"), + (2, "DSS"), + ], initial=(0, "Other")) + archival_remarks = forms.CharField(initial="CSS") + observation_date = forms.DateTimeField() + flux = forms.FloatField() + flux_error = forms.FloatField() + flux_units = forms.ChoiceField(choices=[ + (0, "Other"), + (1, "ABMag"), + (2, "STMag"), + (3, "VegaMag"), + (4, "erg cm(-2) sec(-1)"), + (5, "erg cm(-2) sec(-1) Hz(-1)"), + (6, "erg cm(-2) sec(-1) Ang(-1)"), + (7, "counts sec(-1)"), + (8, "Jy"), + (9, "mJy"), + (10, "Neutrino events"), + (33, "Photons sec(-1) cm(-2)"), + ], initial=(1, "ABMag")) + filter = forms.ChoiceField(choices=TNS_FILTER_CHOICES, initial=(22, "r-Sloan")) + instrument = forms.ChoiceField(choices=TNS_INSTRUMENT_CHOICES, initial=(0, "Other")) + limiting_flux = forms.FloatField(required=False) + exposure_time = forms.FloatField(required=False) + observer = forms.CharField(required=False) + discovery_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + photometry_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + nondetection_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Row( + Column('reporter', css_class='col-md-6'), + Column('reporting_group'), + Column('discovery_data_source'), + ), + Row( + Column(AppendedText('ra', 'deg')), + Column(AppendedText('dec', 'deg')), + Column('discovery_date'), + Column('at_type'), + ), + Row(Column('discovery_remarks')), + Row(HTML('

Discovery Photometry

')), + Row( + Column('observation_date'), + Column('instrument'), + Column('limiting_flux'), + Column('observer'), + ), + Row( + Column('flux'), + Column('flux_error'), + Column('flux_units'), + Column('filter'), + ), + Row(Column('photometry_remarks')), + Row(HTML('

Last Nondetection

')), + Row( + Column('archive'), + Column('archival_remarks'), + Column('nondetection_remarks', css_class='col-md-6'), + ), + Row(Column(Submit('submit', 'Submit Report'))), + ) + + def generate_tns_report(self): + """ + Generate TNS bulk transient report according to the schema in this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + + Returns the report as a JSON-formatted string + """ + report_data = { + "at_report": { + "0": { + "ra": { + "value": self.cleaned_data['ra'], + }, + "dec": { + "value": self.cleaned_data['dec'], + }, + "reporting_group_id": self.cleaned_data['reporting_group'], + "discovery_data_source_id": self.cleaned_data['discovery_data_source'], + "reporter": self.cleaned_data['reporter'], + "discovery_datetime": self.cleaned_data['discovery_date'].strftime('%Y-%m-%d %H:%M:%S'), + "at_type": self.cleaned_data['at_type'], + "non_detection": { + "archiveid": self.cleaned_data['archive'], + "archival_remarks": self.cleaned_data['archival_remarks'], + "comments": self.cleaned_data['nondetection_remarks'], + }, + "photometry": { + "photometry_group": { + "0": { + "obsdate": self.cleaned_data['observation_date'].strftime('%Y-%m-%d %H:%M:%S'), + "flux": self.cleaned_data['flux'], + "flux_error": self.cleaned_data['flux_error'], + "flux_units": self.cleaned_data['flux_units'], + "filter_value": self.cleaned_data['filter'], + "instrument_value": self.cleaned_data['instrument'], + "limiting_flux": self.cleaned_data['limiting_flux'], + "exptime": self.cleaned_data['exposure_time'], + "observer": self.cleaned_data['observer'], + "comments": self.cleaned_data['comments'], + }, + } + }, + } + } + } + return json.dumps(report_data) + + +class TargetClassifyForm(forms.Form): + name = forms.CharField() + classifier = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) + classification = forms.ChoiceField(choices=TNS_CLASSIFICATION_CHOICES, initial=(1, "SN")) + redshift = forms.FloatField(required=False) + group = forms.ChoiceField(choices=[ + (66, "SAGUARO"), + ], initial=(66, "SAGUARO")) + classification_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + observation_date = forms.DateTimeField() + instrument = forms.ChoiceField(choices=TNS_INSTRUMENT_CHOICES, initial=(0, "Other")) + exposure_time = forms.FloatField(required=False) + observer = forms.CharField() + reducer = forms.CharField(required=False) + spectrum_type = forms.ChoiceField(choices=[ + (1, 'Object'), + (2, 'Host'), + (3, 'Sky'), + (4, 'Arcs'), + (5, 'Synthetic'), + ]) + ascii_file = forms.FileField(label='ASCII file') + fits_file = forms.FileField(label='FITS file', required=False) + spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Row( + Column('classifier', css_class='col-md-8'), + Column('group'), + ), + Row( + Column('name'), + Column('classification'), + Column('redshift'), + ), + Row(Column('classification_remarks')), + Row(HTML('

Classification Spectrum

')), + Row( + Column('observation_date'), + Column('observer'), + Column('reducer'), + ), + Row( + Column('instrument'), + Column('exposure_time'), + Column('spectrum_type'), + ), + Row( + Column('ascii_file'), + Column('fits_file'), + ), + Row(Column('spectrum_remarks')), + Row(Column(Submit('submit', 'Submit Report'))), + ) + + def files_to_upload(self): + files_to_upload = {} + if self.cleaned_data['ascii_file'] is not None: + files_to_upload['files[0]'] = ( + os.path.basename(self.cleaned_data['ascii_file'].name), + self.cleaned_data['ascii_file'].open(), + 'text/plain' + ) + if self.cleaned_data['fits_file'] is not None: + files_to_upload['files[1]'] = ( + os.path.basename(self.cleaned_data['fits_file'].name), + self.cleaned_data['fits_file'].open(), + 'application/fits' + ) + return files_to_upload + + def generate_tns_report(self, new_filenames=None): + """ + Generate TNS bulk classification report according to the schema in this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + + Returns the report as a JSON-formatted string + """ + if new_filenames is None: + ascii_filename = os.path.basename(self.cleaned_data['ascii_file'].name) + else: + ascii_filename = new_filenames[0] + report_data = { + "classification_report": { + "0": { + "name": self.cleaned_data['name'], + "classifier": self.cleaned_data['classifier'], + "objtypeid": self.cleaned_data['classification'], + "redshift": self.cleaned_data['redshift'], + "groupid": self.cleaned_data['group'], + "remarks": self.cleaned_data['classification_remarks'], + "spectra": { + "spectra-group": { + "0": { + "obsdate": self.cleaned_data['observation_date'].strftime('%Y-%m-%d %H:%M:%S'), + "instrumentid": self.cleaned_data['instrument'], + "exptime": self.cleaned_data['exposure_time'], + "observer": self.cleaned_data['observer'], + "reducer": self.cleaned_data['reducer'], + "spectypeid": self.cleaned_data['spectrum_type'], + "ascii_file": ascii_filename, + "remarks": self.cleaned_data['spectrum_remarks'], + }, + } + }, + } + } + } + if self.cleaned_data['fits_file'] is not None: + if new_filenames is None: + fits_filename = os.path.basename(self.cleaned_data['fits_file'].name) + else: + fits_filename = new_filenames[1] + report_data['classification_report']['0']['spectra']['spectra_group']['0']['fits_file'] = fits_filename + return json.dumps(report_data) diff --git a/tom_tns/migrations/__init__.py b/tom_tns/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tom_tns/migrations/__pycache__/__init__.cpython-310.pyc b/tom_tns/migrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfec89c6e8e0c2f6655b948c3e1ee3b646ffecab GIT binary patch literal 152 zcmd1j<>g`kf-4n{sUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vSKO;XkRX-~^ zBe5hkCowZmKRvTVza&35z9g?0PUmK(7bTWt<^zS|<1_OzOXB183My}L*yQG?l;)(` MfebBX0un3?0GmrAxBvhE literal 0 HcmV?d00001 diff --git a/tom_tns/models.py b/tom_tns/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/tom_tns/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html new file mode 100644 index 0000000..df42dce --- /dev/null +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -0,0 +1,14 @@ +{% extends 'tom_common/base.html' %} +{% load bootstrap4 crispy_forms_tags %} +{% block title %}Report to TNS{% endblock %} +{% block content %} +

Report {{ target.name }} to the TNS

+
+
+
+ {% csrf_token %} + {% crispy form %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/tom_tns/tests.py b/tom_tns/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/tom_tns/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/tom_tns/urls.py b/tom_tns/urls.py new file mode 100644 index 0000000..7245f34 --- /dev/null +++ b/tom_tns/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from tom_tns.views import TNSFormView + +app_name = 'tom_tns' + +urlpatterns = [ + path('report/', TNSFormView.as_view(), name='report-tns'), +] diff --git a/tom_tns/views.py b/tom_tns/views.py new file mode 100644 index 0000000..65bda62 --- /dev/null +++ b/tom_tns/views.py @@ -0,0 +1,60 @@ +from django.urls import reverse_lazy +from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin +from django.shortcuts import redirect +from guardian.mixins import PermissionListMixin + +from tom_tns.forms import TNSReportForm, TNS_FILTER_CHOICES, TNS_INSTRUMENT_CHOICES, TNS_CLASSIFICATION_CHOICES +from tom_targets.models import Target + + +TNS_FILTER_IDS = {name: fid for fid, name in TNS_FILTER_CHOICES} +TNS_INSTRUMENT_IDS = {name: iid for iid, name in TNS_INSTRUMENT_CHOICES} +TNS_CLASSIFICATION_IDS = {name: cid for cid, name in TNS_CLASSIFICATION_CHOICES} + + +class TNSFormView(PermissionListMixin, TemplateResponseMixin, FormMixin, ProcessFormView): + """ + View that handles reporting a target to the TNS. + """ + form_class = TNSReportForm + template_name = 'tom_tns/tns_report.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['target'] = Target.objects.get(pk=self.kwargs['pk']) + return context + + def get_initial(self): + target = Target.objects.get(pk=self.kwargs['pk']) + initial = { + 'ra': target.ra, + 'dec': target.dec, + 'reporter': f'{self.request.user.get_full_name()}, on behalf of SAGUARO', + } + photometry = target.reduceddatum_set.filter(data_type='photometry') + if photometry.exists(): + reduced_datum = photometry.latest() + initial['observation_date'] = reduced_datum.timestamp + initial['flux'] = reduced_datum.value['magnitude'] + initial['flux_error'] = reduced_datum.value['error'] + filter_name = reduced_datum.value.get('filter') + if filter_name in TNS_FILTER_IDS: + initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) + instrument_name = reduced_datum.value.get('instrument') + if instrument_name in TNS_INSTRUMENT_IDS: + initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) + return initial + + def form_valid(self, form): + # report_id = send_tns_report(form.generate_tns_report()) + # iau_name = get_tns_report_reply(report_id, self.request) + + # update the target name + # if iau_name is not None: + # target = Target.objects.get(pk=self.kwargs['pk']) + # target.name = iau_name + # target.save() + return redirect(self.get_success_url()) + + def get_success_url(self): + return reverse_lazy('targets:detail', kwargs=self.kwargs) diff --git a/tom_tns_base/__init__.py b/tom_tns_base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tom_tns_base/__pycache__/__init__.cpython-310.pyc b/tom_tns_base/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c3b8e607d3ab3b1079b89b2c41970c4c969f23f GIT binary patch literal 146 zcmd1j<>g`kg7y2HQbF`%5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!Hienx(7s(w~- zMq){7PGV-BetKq!eo20Ad`Vt0jE+xAEKb#rkI&4@EQycTE2zB1VUwGmQks)$2Qsdh J2}rOo006+TAz}ak literal 0 HcmV?d00001 diff --git a/tom_tns_base/__pycache__/settings.cpython-310.pyc b/tom_tns_base/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49ecc2a49cebcfdc3b9430a34d54de7b6fa5d92b GIT binary patch literal 4767 zcmb7H-Co;97Pn<#8}ktYB%x3eCnX;yK@MLD$#&ZaTL%AP%Z?>Tn2YKsJcAL)lBp^DGz!lcjFT`P$HV9nX0tMzv3-~d*4Z7Wd{$0F* zpWvtXFK01y4}Xg?_^)UYe~0eF`~&h0JE;$GU_zj)jAu(yN4uw&^FDq zX;3$9ADr(>w=gV33@3e}n{7h>pS2uv(6`VV=IxoN&sTN#Q1&)EW-u&D>TQQC-C4N3 zPL2+4uHpsj2k+EcSZJ>w|FCjr`XD0GORoAH%pqsH+_~dXv1ionvq2!?B=)FfL2tFd-oat|2}}<3w(0i5AMc+V zk^X2xps#u?PiHs4fpU|{`)VL4+Ey!(eWJM*_Q&y?Iv-I z`o{=E#9C@oKenUhyad!k2%M}p-+={7Hf>ByDr_1J2rRo`5EG-MQp`&=Ni2D@-MSa% zA0n7_TWFZ}zHa&vuzsp#JCLR{@P9v{w+!u=oSq9UuRal0R-Oo}Yr^vS(~Y&YRd22f zxV*9AaI2<1TcAz zf9Z4l)YctAsfe{6d**ZOrYn@xE^8+4-C@jO0T%WhsNfD2*sx9nu|#S)X)_>t_b%eI zpq|72L|GMUlG5S4bpdBXH*{eOnhwuJtC~914M!*iuMXeB1z6xrZC6lO+u;hX$OC(T z8?&Iw9i31Nx`Ctn6Nas?erVgrRDcb_JwRSB}?(aln35R%aFCOsN5R*AEb1!y>36a|^)7Eh&23MSc_0Y_x z;NcVL6F3F%P579I1Wkr}^Be15f#%ILG(LYsZOh9JT;N)I!?42oP=b=+Rt^r}K40xo z-(Zl<@MtzK6~vvAs#T?0xu_^m-?UswR1`@;bAefF(u*Bt-Y#RIQY(upn(2PgCLdkQEc*#fgD(zE^Ajz&*PF(^!!gY{oBByDNEmDmc3_()#E-YY z3oREVH$_EamDf)xQm!Vc+H+~oAIeLcJ6rykSSnRsN_lO&qNs{LUX&FTNI-yct)S^# zt*BJ@z%k0zlBh~r6>2(~?@+t4AIvI#s$9(HOVUelet)!9si@jc4dPrb_`_XJ#h-Yo zY!%rerD87FwLhE}Rgvkc_%q^;x(#b7%F9YEukDJZB1~4`7BN;5<*glNm|P_<0bsE# zX{Z7vKe+?DEG}=zew_7G1lv^sImcG_C%a|_&uK9L{N{E*sN8aOCXWj7HhlU z0?_y45I)2@ahU6&QR$Uh6GN>OG!|@L0g5VZ?V)QOEzccG38E4WSE`cCHm9JW?Mmf2 z=ooBH-QFp0%3=}dlfAxH7GDLvgsz^`x;9}xhQ_vwvZ`p3EN;T_gR(HxO46=WLgSFd zRIN~hI~APAb2M(+btv=Q4_kkv*kqMoava+aXc60 zqDhW>&;QB4=O?%X7mY{xchDaL8sGQ)!B6qP4!<}*#K)K<|Bj0X?NHG2T0X$#`NnNfq0X?EuqlzlLAR zmKDDMLgLy@qzVZMEG6;d=G=47Jyxfu5rVP!dCgyr5c=j8$72GE7x0)%crmm_EMQ?B zh;SW>(RvghK4K#tvhhCHhcKVO{E?d%;d;!b?@=qAFhCjj6Mw4sXOrR^z!6Gw zTuFcw2k@rm4$^SGiZG1Q+QzI1VJbH%+frGmAziKn;gVdY7sRTj$_;VB1kxK4z4}50 zV3}2P)5_fQ_Gvxnd5W(X!&Kr*G)mhQ#v82!77rP1ntt;@)5l-*@vZ__z)^+pD->HU z;&|EDRVu-kSW>~C#cm!a(REFYdE&HUN2q^ep=`;uBa(W?1D)4^^*?M}d`eB`Ko-d# zjBaIKx2%!{h~V?N(p;MdeQv!l7^pydTr6KCc+*R{I>>csN9qJ0NcOQyFUmi&y{}b4 zK)@kW>^3}ZoFvhAa4<^3ZrUGrW2g9T4E`g#dGACY-J#uMw+3ejCH>ahWYSHu4D6F- z-N_MS4dzA~t`6!>j{5l(>6trluOH{cHIQucl3HHVN|K_oU^egl;p^{!cU{^%bhD>I Xv9{*V0{}4Yj*ijU*?c??X1{&|FFYut literal 0 HcmV?d00001 diff --git a/tom_tns_base/__pycache__/wsgi.cpython-310.pyc b/tom_tns_base/__pycache__/wsgi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7485d448f9969beb93555b7277d236eb221a137 GIT binary patch literal 559 zcmYjP!EO^V5cN7~Qx=MPfS!Gg1ZzN?5JD8SL_v@qQiPO?z!2gjcm z&-fBA19Anm!7B?c@JnB5p8rWZA(LSiA5;)TdndMJ#KYrvN0ZU1n4X-SjVGh2_;7ms z@%_p5!|fFm9zxKr^20$K2$6IM5h?n3uu)T#X^5i5ZfIhQ18{5Ipo@8GRIrd2Cd9j2 zwBJe7K56IvK26ds@Kw2#0gTj+S32!kj literal 0 HcmV?d00001 diff --git a/tom_tns_base/asgi.py b/tom_tns_base/asgi.py new file mode 100644 index 0000000..93951c7 --- /dev/null +++ b/tom_tns_base/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for tom_tns_base project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tom_tns_base.settings') + +application = get_asgi_application() diff --git a/tom_tns_base/settings.py b/tom_tns_base/settings.py new file mode 100644 index 0000000..1f9b8dd --- /dev/null +++ b/tom_tns_base/settings.py @@ -0,0 +1,274 @@ +""" +Django settings for tom_tns_base project. + +Generated by 'django-admin startproject' using Django 4.2.6. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +from pathlib import Path +import os + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-&$%5ejf!3d$n^zyn$$u5k^2%8-koutb*&s-3*1on5p34nj1a0j' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.sites', + 'django_extensions', + 'guardian', + 'tom_common', + 'django_comments', + 'bootstrap4', + 'crispy_forms', + 'crispy_bootstrap4', + 'rest_framework', + 'rest_framework.authtoken', + 'django_filters', + 'django_gravatar', + 'tom_targets', + 'tom_alerts', + 'tom_catalogs', + 'tom_observations', + 'tom_dataproducts', + 'tom_tns', +] + +CRISPY_TEMPLATE_PACK = 'bootstrap4' + +CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap4' + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'tom_tns_base.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'tom_tns_base.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# TOM Specific configuration +TARGET_TYPE = 'SIDEREAL' + +FACILITIES = { + 'LCO': { + 'portal_url': 'https://observe.lco.global', + 'api_key': '', + }, + 'GEM': { + 'portal_url': { + 'GS': 'https://139.229.34.15:8443', + 'GN': 'https://128.171.88.221:8443', + }, + 'api_key': { + 'GS': '', + 'GN': '', + }, + 'user_email': '', + 'programs': { + 'GS-YYYYS-T-NNN': { + 'MM': 'Std: Some descriptive text', + 'NN': 'Rap: Some descriptive text' + }, + 'GN-YYYYS-T-NNN': { + 'QQ': 'Std: Some descriptive text', + 'PP': 'Rap: Some descriptive text', + }, + }, + }, +} + +# Define the valid data product types for your TOM. Be careful when removing items, as previously valid types will no +# longer be valid, and may cause issues unless the offending records are modified. +DATA_PRODUCT_TYPES = { + 'photometry': ('photometry', 'Photometry'), + 'fits_file': ('fits_file', 'FITS File'), + 'spectroscopy': ('spectroscopy', 'Spectroscopy'), + 'image_file': ('image_file', 'Image File') +} + +DATA_PROCESSORS = { + 'photometry': 'tom_dataproducts.processors.photometry_processor.PhotometryProcessor', + 'spectroscopy': 'tom_dataproducts.processors.spectroscopy_processor.SpectroscopyProcessor', +} + +TOM_FACILITY_CLASSES = [ + 'tom_observations.facilities.lco.LCOFacility', + 'tom_observations.facilities.gemini.GEMFacility', + 'tom_observations.facilities.soar.SOARFacility', + 'tom_observations.facilities.lt.LTFacility' +] + +TOM_ALERT_CLASSES = [ + 'tom_alerts.brokers.alerce.ALeRCEBroker', + 'tom_alerts.brokers.antares.ANTARESBroker', + 'tom_alerts.brokers.gaia.GaiaBroker', + 'tom_alerts.brokers.lasair.LasairBroker', + 'tom_alerts.brokers.scout.ScoutBroker', + 'tom_alerts.brokers.tns.TNSBroker', +] + +BROKERS = { + 'TNS': { + 'api_key': '' + } +} + +HARVESTERS = { + 'TNS': { + 'api_key': '' + } +} + +SAVE_TEST_ALERTS = os.getenv('SAVE_TEST_ALERTS', True) + +# Define extra target fields here. Types can be any of "number", "string", "boolean" or "datetime" +# See https://tomtoolkit.github.io/docs/target_fields for documentation on this feature +# For example: +# EXTRA_FIELDS = [ +# {'name': 'redshift', 'type': 'number'}, +# {'name': 'discoverer', 'type': 'string'} +# {'name': 'eligible', 'type': 'boolean'}, +# {'name': 'dicovery_date', 'type': 'datetime'} +# ] +EXTRA_FIELDS = [] + +# Authentication strategy can either be LOCKED (required login for all views) +# or READ_ONLY (read only access to views) +AUTH_STRATEGY = 'READ_ONLY' + +# Row-level data permissions restrict users from viewing certain objects unless they are a member of the group to which +# the object belongs. Setting this value to True will allow all `ObservationRecord`, `DataProduct`, and `ReducedDatum` +# objects to be seen by everyone. Setting it to False will allow users to specify which groups can access +# `ObservationRecord`, `DataProduct`, and `ReducedDatum` objects. +TARGET_PERMISSIONS_ONLY = True + +# URLs that should be allowed access even with AUTH_STRATEGY = LOCKED +# for example: OPEN_URLS = ['/', '/about'] +OPEN_URLS = [] + +HOOKS = { + 'target_post_save': 'tom_common.hooks.target_post_save', + 'observation_change_state': 'tom_common.hooks.observation_change_state', + 'data_product_post_upload': 'tom_dataproducts.hooks.data_product_post_upload' +} + +AUTO_THUMBNAILS = False + +THUMBNAIL_MAX_SIZE = (0, 0) + +THUMBNAIL_DEFAULT_SIZE = (200, 200) + +HINTS_ENABLED = True +HINT_LEVEL = 20 + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + ], + 'TEST_REQUEST_DEFAULT_FORMAT': 'json', + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 100 +} + +try: + from local_settings import * # noqa +except ImportError: + pass \ No newline at end of file diff --git a/tom_tns_base/urls.py b/tom_tns_base/urls.py new file mode 100644 index 0000000..d1e8eb0 --- /dev/null +++ b/tom_tns_base/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for tom_tns_base project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.urls import include, path + +urlpatterns = [ + # path('', include('tom_common.urls')), + path('tns/', include('tom_tns.urls')), + path('', include('tom_common.urls')), +] diff --git a/tom_tns_base/wsgi.py b/tom_tns_base/wsgi.py new file mode 100644 index 0000000..9bdca2e --- /dev/null +++ b/tom_tns_base/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for tom_tns_base project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tom_tns_base.settings') + +application = get_wsgi_application() From bd1792b04a7acc2ed7c0a3b9d563084c536b6a5c Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Mon, 6 Nov 2023 14:48:52 -0800 Subject: [PATCH 02/21] update readme / add target pk to url --- README.md | 30 ++++++++++++++++++++++++++++-- tom_tns/urls.py | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c5e9115..49f0c74 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,28 @@ -# tom_tns -TOMtoolkit module for reporting transients to the TNS +# Transient Name Server (TNS) Report and Classification plugin for TOM Toolkit +TOMtoolkit module for reporting transients to the TNS + +## Installation + +1. Install the package into your TOM environment: + ```bash + pip install tom-tns + ``` + +2. In your project `settings.py`, add `tom_tns` to your `INSTALLED_APPS` setting: + + ```python + INSTALLED_APPS = [ + ... + 'tom_nonlocalizedevents', + ] + ``` + + Also add your TNS credentials to your `settins.py` if they don't already exist for the TNS Broker. + +3. Include the tom_nonlocalizedevent URLconf in your project `urls.py`: + ```python + urlpatterns = [ + ... + path('tns/', include('tom_tns.urls', namespace='tns')), + ] + ``` diff --git a/tom_tns/urls.py b/tom_tns/urls.py index 7245f34..44018c6 100644 --- a/tom_tns/urls.py +++ b/tom_tns/urls.py @@ -5,5 +5,5 @@ app_name = 'tom_tns' urlpatterns = [ - path('report/', TNSFormView.as_view(), name='report-tns'), + path('/report/', TNSFormView.as_view(), name='report-tns'), ] From 25849f37baee10e5419b532cd7d523bd10a67d3d Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 7 Nov 2023 17:40:57 -0800 Subject: [PATCH 03/21] pull form options from tns --- tom_tns/__pycache__/forms.cpython-310.pyc | Bin 7945 -> 6745 bytes tom_tns/__pycache__/urls.cpython-310.pyc | Bin 326 -> 335 bytes tom_tns/__pycache__/views.cpython-310.pyc | Bin 2628 -> 2910 bytes tom_tns/forms.py | 163 +++++----------------- tom_tns/tns_report.py | 155 ++++++++++++++++++++ tom_tns/views.py | 17 ++- 6 files changed, 202 insertions(+), 133 deletions(-) create mode 100644 tom_tns/tns_report.py diff --git a/tom_tns/__pycache__/forms.cpython-310.pyc b/tom_tns/__pycache__/forms.cpython-310.pyc index ac3069150c5999c88a2607080ac142e790dd826a..9dcec6df6b488d5d98bfe350ff34b93a2a03d8b1 100644 GIT binary patch literal 6745 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 c-U6Ddf0Aw!AX!igMhYAZ16qBWh%l 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 NQ_GlIQNA`i_P^Td>%ss4 diff --git a/tom_tns/__pycache__/urls.cpython-310.pyc b/tom_tns/__pycache__/urls.cpython-310.pyc index b24b20e802fa62b7ffd2df05774056413dc1a6a5..02a26ece38e80ff88d6cb933a3c28603ea743f05 100644 GIT binary patch delta 35 pcmX@cbe@SfpO=@50SGo2c&2Wh$Xm-LV3V0wVpWiBr$4cKHvpxW3X%W- delta 26 gcmX@lbc~5NpO=@50SI3Ga!y?{k++tSed3he0A%?HbpQYW diff --git a/tom_tns/__pycache__/views.cpython-310.pyc b/tom_tns/__pycache__/views.cpython-310.pyc index 0633ab46d00d9024c123cfa1c65e5c9e9d2658ef..ad3b08c2940ab4dfdcd63b1acd27cebcdd387b82 100644 GIT binary patch delta 1314 zcmaJ=%WfP+6z!Vs>7Jgq=j9oD67z^+4TN!!04Y`!DJTIEOazV*Qf*r8sq&;vKSotG z!STim`v#SI@VT1UCTCfjF7ASj^d&}dHNQiFf)~QpsPWQQW>$lFIy?R!w zl`VMw`27w)shrk_Bj5@3QK^Vl^1+FgCtFvVS7W z7F}jdwsd0gCTks&Th=k5eb!zuodq*s-38NIFe@KhY?<{>EY{~0?sAV8k6pUT{Cn1D z@EFaGh%QO4RFqC7;HuycxR5-E!^4LoTQ@#G{cGol=-STRd)s`L3H4DXl2Jjor(6Xp zmBB=C##0rAvDEc*@alQ@(z2+mBKzq<%YYmbhcCUH?htmfYqzw53cd<_GNh|3t(^V{xw zFsUtJ_w2r^2h%03$*l7 w?TvqPWTop##^y15wYEg(k#O!LeN!3&bVme_D2nw8)D690F`Z0{{R3 delta 1043 zcmaJ<%Wl&^6!pZ8AMqn;nuaC~txyrd0z!cbgs6f_NFh*CsR=^z#xj{{FtQUgc3O5_ zwELZlKSt*-+AYdIEhXOW;MkG?q67fKGO4#fQw)F)XOs*jR zxx~y~r@KvFjcB|vq9H4C>Ta|*d#!D^`EaY)md9 zqupHZwYNG`pN*K@^XP!YNkgn8k28xki=SzWEK^@$;*O7*H1tA3L8Vr5m}wMXZQn8+ z$^xC`AD&rVGgJQ*Y?0=C9C9Rip?M@^mmt9R6K^iT(c+Fs21g-i!0dI>RZG z<8&I{>PP7wT1@n8HJ|pL9=pIp$|tMY0+Jz`&L@xLw{tZx&jKt0ECZZRzRRn~POd7O zr6mw59GKz{{n+;cdLcPduBEH`JrvXV8r@06Lh?x&H)Kf4gB*?feG*0Pmcq|4y=`Jws};r!DQnr1U1h zZ2)jH({=lS7e##%1g`r>g>w*+E~v~cxVfmnK@}tq=oGvidyeV&r8gXm z>U@|e?Tl3>%C1lLqWXZiLqh$2onSwvBF~}{K$Zh5OhTg<{9o>m1!%Yl)}|RWuICo7}QOuLHf2e9hJFLTi6abb94XT?vP276s4D&hb9mhs diff --git a/tom_tns/forms.py b/tom_tns/forms.py index 214e4ea..b2823df 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -1,4 +1,8 @@ from django import forms +from django.conf import settings + +from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values + from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Row, Column, Submit, HTML from crispy_forms.bootstrap import AppendedText @@ -7,142 +11,22 @@ import os -TNS_FILTER_CHOICES = [ - (0, "Other"), - (1, "Clear"), - (10, "U-Johnson"), - (11, "B-Johnson"), - (12, "V-Johnson"), - (13, "R-Cousins"), - (14, "I-Cousins"), - (15, "J-Bessel"), - (16, "H-Bessel"), - (17, "K-Bessel"), - (18, "L"), - (19, "M"), - (20, "u-Sloan"), - (21, "g-Sloan"), - (22, "r-Sloan"), - (23, "i-Sloan"), - (24, "z-Sloan"), - (25, "y-P1"), - (26, "w-P1"), -] - -TNS_INSTRUMENT_CHOICES = [ - (0, "Other"), -] - -TNS_CLASSIFICATION_CHOICES = [ - (0, "Other"), - (1, "SN"), - (2, "SN I"), - (3, "SN Ia"), - (4, "SN Ib"), - (5, "SN Ic"), - (6, "SN Ib/c"), - (7, "SN Ic-BL"), - (9, "SN Ibn"), - (10, "SN II"), - (11, "SN IIP"), - (12, "SN IIL"), - (13, "SN IIn"), - (14, "SN IIb"), - (15, "SN I-faint"), - (16, "SN I-rapid"), - (18, "SLSN-I"), - (19, "SLSN-II"), - (20, "SLSN-R"), - (23, "Afterglow"), - (24, "LBV"), - (25, "ILRT"), - (26, "Nova"), - (27, "CV"), - (28, "Varstar"), - (29, "AGN"), - (30, "Galaxy"), - (31, "QSO"), - (40, "Light-Echo"), - (50, "Std-spec"), - (60, "Gap"), - (61, "Gap I"), - (62, "Gap II"), - (65, "LRN"), - (66, "FBOT"), - (70, "Kilonova"), - (99, "Impostor-SN"), - (100, "SN Ia-pec"), - (102, "SN Ia-SC"), - (103, "SN Ia-91bg-like"), - (104, "SN Ia-91T-like"), - (105, "SN Iax[02cx-like]"), - (106, "SN Ia-CSM"), - (107, "SN Ib-pec"), - (108, "SN Ic-pec"), - (109, "SN Icn"), - (110, "SN Ibn/Icn"), - (111, "SN II-pec"), - (112, "SN IIn-pec"), - (115, "SN Ib-Ca-rich"), - (116, "SN Ib/c-Ca-rich"), - (117, "SN Ic-Ca-rich"), - (118, "SN Ia-Ca-rich"), - (120, "TDE"), - (121, "TDE-H"), - (122, "TDE-He"), - (123, "TDE-H-He"), - (200, "WR"), - (201, "WR-WN"), - (202, "WR-RC"), - (203, "WR-WO"), - (210, "M dwarf"), -] - - class TNSReportForm(forms.Form): ra = forms.FloatField(label='R.A.') dec = forms.FloatField(label='Dec.') - reporting_group = forms.ChoiceField(choices=[ - (66, "SAGUARO"), - ], initial=(66, "SAGUARO")) - discovery_data_source = forms.ChoiceField(choices=[ - (66, "SAGUARO"), - ], initial=(66, "SAGUARO")) + reporting_group = forms.ChoiceField(choices=[]) + discovery_data_source = forms.ChoiceField(choices=[]) reporter = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) discovery_date = forms.DateTimeField(initial=datetime.utcnow()) - at_type = forms.ChoiceField(choices=[ - (0, "Other"), - (1, "PSN"), - (2, "PNV"), - (3, "AGN"), - (4, "NUC"), - (5, "FRB"), - ], initial=(1, "PSN"), label='AT type') - archive = forms.ChoiceField(choices=[ - (0, "Other"), - (1, "SDSS"), - (2, "DSS"), - ], initial=(0, "Other")) + at_type = forms.ChoiceField(choices=[], label='AT type') + archive = forms.ChoiceField(choices=[]) archival_remarks = forms.CharField(initial="CSS") observation_date = forms.DateTimeField() flux = forms.FloatField() flux_error = forms.FloatField() - flux_units = forms.ChoiceField(choices=[ - (0, "Other"), - (1, "ABMag"), - (2, "STMag"), - (3, "VegaMag"), - (4, "erg cm(-2) sec(-1)"), - (5, "erg cm(-2) sec(-1) Hz(-1)"), - (6, "erg cm(-2) sec(-1) Ang(-1)"), - (7, "counts sec(-1)"), - (8, "Jy"), - (9, "mJy"), - (10, "Neutrino events"), - (33, "Photons sec(-1) cm(-2)"), - ], initial=(1, "ABMag")) - filter = forms.ChoiceField(choices=TNS_FILTER_CHOICES, initial=(22, "r-Sloan")) - instrument = forms.ChoiceField(choices=TNS_INSTRUMENT_CHOICES, initial=(0, "Other")) + flux_units = forms.ChoiceField(choices=[]) + filter = forms.ChoiceField(choices=[]) + instrument = forms.ChoiceField(choices=[]) limiting_flux = forms.FloatField(required=False) exposure_time = forms.FloatField(required=False) observer = forms.CharField(required=False) @@ -152,6 +36,27 @@ class TNSReportForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.fields['reporting_group'].choices = get_tns_values('groups') + self.fields['discovery_data_source'].choices = get_tns_values('groups') + self.fields['at_type'].choices = get_tns_values('at_types') + self.fields['at_type'].initial = (1, "PSN") + self.fields['filter'].choices = get_tns_values('filters') + self.fields['filter'].initial = (22, "r-Sloan") + self.fields['archive'].choices = get_tns_values('archives') + self.fields['instrument'].choices = get_tns_values('instruments') + self.fields['instrument'].initial = (0, "Other") + self.fields['flux_units'].choices = get_tns_values('units') + self.fields['flux_units'].initial = (1, "ABMag") + + # set initial group if tom_name is in the list of tns group names + tns_group_name = get_tns_credentials().get('group_name', None) + if not tns_group_name: + tns_group_name = settings.TOM_NAME + default_group = get_reverse_tns_values('groups', tns_group_name) + if default_group: + self.fields['reporting_group'].initial = default_group + self.fields['discovery_data_source'].initial = default_group + self.helper = FormHelper() self.helper.layout = Layout( Row( @@ -240,14 +145,14 @@ def generate_tns_report(self): class TargetClassifyForm(forms.Form): name = forms.CharField() classifier = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) - classification = forms.ChoiceField(choices=TNS_CLASSIFICATION_CHOICES, initial=(1, "SN")) + classification = forms.ChoiceField(choices=[], initial=(1, "SN")) redshift = forms.FloatField(required=False) group = forms.ChoiceField(choices=[ (66, "SAGUARO"), ], initial=(66, "SAGUARO")) classification_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) observation_date = forms.DateTimeField() - instrument = forms.ChoiceField(choices=TNS_INSTRUMENT_CHOICES, initial=(0, "Other")) + instrument = forms.ChoiceField(choices=[], initial=(0, "Other")) exposure_time = forms.FloatField(required=False) observer = forms.CharField() reducer = forms.CharField(required=False) diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py new file mode 100644 index 0000000..80aa2b4 --- /dev/null +++ b/tom_tns/tns_report.py @@ -0,0 +1,155 @@ +import requests +from urllib.parse import urljoin + +from django.core.cache import cache +from django.conf import settings + +import logging +logger = logging.getLogger(__name__) + + +def get_tns_credentials(): + """ + Get the TNS credentials from settings.py. + """ + return settings.BROKERS['TNS'] + + +def get_tns_values(option_list): + """ Retrieve the TNS options. These are cached for one hour. + Returns a list of tuples, each tuple containing the option value and the option label. + """ + all_tns_values = cache.get("all_tns_values", {}) + if not all_tns_values: + all_tns_values, _ = populate_tns_values() + selected_values = all_tns_values[option_list] + tuple_list = [] + if isinstance(selected_values, list): + tuple_list = [(v, v) for v in selected_values] + if isinstance(selected_values, dict): + tuple_list = [(k, v) for k, v in selected_values.items()] + return tuple_list + + +def get_reverse_tns_values(option_list, value=None): + """ Retrieve the reverse mapping of TNS options used to go from option to value. + I.e. reversed_tns_values['groups'] = { + 'group name 1': 1, + 'group name 2': 4, + 'group whatever': 129 + } + """ + reversed_tns_values = cache.get("reverse_tns_values", {}) + if not reversed_tns_values: + _, reversed_tns_values = populate_tns_values() + if not value: + return reversed_tns_values[option_list] + try: + return reversed_tns_values[option_list][value], value + except KeyError: + return None + + +def populate_tns_values(): + """pull all the values from the TNS API""" + + # Need to spoof a web based user agent or TNS will block the request :( + SPOOF_USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:110.0) Gecko/20100101 Firefox/110.0.' + + tns_base_url = get_tns_credentials()['tns_base_url'] + all_tns_values = {} + reversed_tns_values = {} + try: + resp = requests.get(urljoin(tns_base_url, 'api/values/'), + headers={'user-agent': SPOOF_USER_AGENT}) + resp.raise_for_status() + all_tns_values = resp.json().get('data', {}) + reversed_tns_values = reverse_tns_values(all_tns_values) + cache.set("all_tns_values", all_tns_values, 3600) + cache.set("reverse_tns_values", reversed_tns_values, 3600) + except Exception as e: + logging.warning(f"Failed to retrieve tns values: {repr(e)}") + return all_tns_values, reversed_tns_values + + +def reverse_tns_values(all_tns_values): + reversed_tns_values = {} + for key, values in all_tns_values.items(): + if isinstance(values, list): + reversed_tns_values[key] = {value: index for index, value in enumerate(values)} + elif isinstance(values, dict): + reversed_tns_values[key] = {v: k for k, v in values.items()} + return reversed_tns_values + + +def upload_files_to_tns(files): + """ + Upload files to the Transient Name Server according to this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + """ + json_data = {'api_key': TNS['api_key']} + response = requests.post(TNS_URL + '/file-upload', headers={'User-Agent': TNS_MARKER}, data=json_data, files=files) + response.raise_for_status() + new_filenames = response.json()['data'] + logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") + return new_filenames + + +def send_tns_report(data): + """ + Send a JSON bulk report to the Transient Name Server according to this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + """ + json_data = {'api_key': TNS['api_key'], 'data': data} + response = requests.post(TNS_URL + '/bulk-report', headers={'User-Agent': TNS_MARKER}, data=json_data) + response.raise_for_status() + report_id = response.json()['data']['report_id'] + logger.info(f'Sent TNS report ID {report_id:d}') + return report_id + + +def get_tns_report_reply(report_id, request): + """ + Get feedback from the Transient Name Server in response to a bulk report according to this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + + Posts an informational message in a banner on the page using ``request`` + """ + json_data = {'api_key': TNS['api_key'], 'report_id': report_id} + for _ in range(6): + time.sleep(5) + response = requests.post(TNS_URL + '/bulk-report-reply', headers={'User-Agent': TNS_MARKER}, data=json_data) + if response.ok: + break + response.raise_for_status() + feedback_section = response.json()['data']['feedback'] + feedbacks = [] + if 'at_report' in feedback_section: + feedbacks += feedback_section['at_report'] + if 'classification_report' in feedback_section: + feedbacks += feedback_section['classification_report'][0]['classification_messages'] + for feedback in feedbacks: + if '100' in feedback: # transient object was inserted + iau_name = 'AT' + feedback['100']['objname'] + log_message = f'New transient {iau_name} was created' + logger.info(log_message) + messages.success(request, log_message) + break + elif '101' in feedback: # transient object exists + iau_name = feedback['101']['prefix'] + feedback['101']['objname'] + log_message = f'Existing transient {iau_name} was reported' + logger.info(log_message) + messages.info(request, log_message) + break + elif '121' in feedback: # object name prefix has changed + iau_name = feedback['121']['new_object_name'] + log_message = f'Transient name changed to {iau_name}' + logger.info(log_message) + messages.success(request, log_message) + break + else: # this should never happen + iau_name = None + log_message = 'Problem getting response from TNS' + logger.error(log_message) + messages.error(request, log_message) + return iau_name \ No newline at end of file diff --git a/tom_tns/views.py b/tom_tns/views.py index 65bda62..75acd04 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -1,15 +1,24 @@ +from django.conf import settings from django.urls import reverse_lazy from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin from django.shortcuts import redirect from guardian.mixins import PermissionListMixin -from tom_tns.forms import TNSReportForm, TNS_FILTER_CHOICES, TNS_INSTRUMENT_CHOICES, TNS_CLASSIFICATION_CHOICES +from tom_tns.forms import TNSReportForm +from tom_tns.tns_report import get_tns_credentials, get_tns_values from tom_targets.models import Target +import json -TNS_FILTER_IDS = {name: fid for fid, name in TNS_FILTER_CHOICES} -TNS_INSTRUMENT_IDS = {name: iid for iid, name in TNS_INSTRUMENT_CHOICES} -TNS_CLASSIFICATION_IDS = {name: cid for cid, name in TNS_CLASSIFICATION_CHOICES} + +TNS_URL = 'https://sandbox.wis-tns.org/api' # TODO: change this to the main site +TNS_credentials = get_tns_credentials() +TNS_MARKER = 'tns_marker' + json.dumps({'tns_id': TNS_credentials['bot_id'], + 'type': 'bot', + 'name': TNS_credentials['bot_name']}) +TNS_FILTER_IDS = {name: fid for fid, name in get_tns_values('filters')} +TNS_INSTRUMENT_IDS = {name: iid for iid, name in get_tns_values('instruments')} +TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} class TNSFormView(PermissionListMixin, TemplateResponseMixin, FormMixin, ProcessFormView): From c314167135e24f303b8e3dd5355c4dcea637619b Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 8 Nov 2023 17:09:52 -0800 Subject: [PATCH 04/21] merge forms under single view --- tom_tns/forms.py | 55 +++++++----- tom_tns/templates/tom_tns/tns_report.html | 66 ++++++++++++-- tom_tns/views.py | 102 +++++++++++++++++----- 3 files changed, 172 insertions(+), 51 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index b2823df..6ef2193 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -16,7 +16,7 @@ class TNSReportForm(forms.Form): dec = forms.FloatField(label='Dec.') reporting_group = forms.ChoiceField(choices=[]) discovery_data_source = forms.ChoiceField(choices=[]) - reporter = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) + reporter = forms.CharField(widget=forms.Textarea(attrs={'rows': 1}), label='Reporter Name(s) / Author List') discovery_date = forms.DateTimeField(initial=datetime.utcnow()) at_type = forms.ChoiceField(choices=[], label='AT type') archive = forms.ChoiceField(choices=[]) @@ -30,9 +30,9 @@ class TNSReportForm(forms.Form): limiting_flux = forms.FloatField(required=False) exposure_time = forms.FloatField(required=False) observer = forms.CharField(required=False) - discovery_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) - photometry_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) - nondetection_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + discovery_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) + photometry_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) + nondetection_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -142,41 +142,48 @@ def generate_tns_report(self): return json.dumps(report_data) -class TargetClassifyForm(forms.Form): - name = forms.CharField() - classifier = forms.CharField(widget=forms.Textarea(attrs={'rows': 1})) - classification = forms.ChoiceField(choices=[], initial=(1, "SN")) +class TNSClassifyForm(forms.Form): + object_name = forms.CharField() + classifier = forms.CharField(widget=forms.Textarea(attrs={'rows': 1}), label='Classifier Name(s) / Author List') + classification = forms.ChoiceField(choices=[]) redshift = forms.FloatField(required=False) - group = forms.ChoiceField(choices=[ - (66, "SAGUARO"), - ], initial=(66, "SAGUARO")) - classification_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + reporting_group = forms.ChoiceField(choices=[]) + classification_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) observation_date = forms.DateTimeField() - instrument = forms.ChoiceField(choices=[], initial=(0, "Other")) + instrument = forms.ChoiceField(choices=[]) exposure_time = forms.FloatField(required=False) observer = forms.CharField() reducer = forms.CharField(required=False) - spectrum_type = forms.ChoiceField(choices=[ - (1, 'Object'), - (2, 'Host'), - (3, 'Sky'), - (4, 'Arcs'), - (5, 'Synthetic'), - ]) + spectrum_type = forms.ChoiceField(choices=[]) ascii_file = forms.FileField(label='ASCII file') fits_file = forms.FileField(label='FITS file', required=False) - spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 1})) + spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.fields['reporting_group'].choices = get_tns_values('groups') + self.fields['instrument'].choices = get_tns_values('instruments') + self.fields['instrument'].initial = (0, "Other") + self.fields['classification'].choices = get_tns_values('object_types') + self.fields['classification'].initial = (1, "SN") + self.fields['spectrum_type'].choices = get_tns_values('spectra_types') + + # set initial group if tom_name is in the list of tns group names + tns_group_name = get_tns_credentials().get('group_name', None) + if not tns_group_name: + tns_group_name = settings.TOM_NAME + default_group = get_reverse_tns_values('groups', tns_group_name) + if default_group: + self.fields['reporting_group'].initial = default_group + self.helper = FormHelper() self.helper.layout = Layout( Row( Column('classifier', css_class='col-md-8'), - Column('group'), + Column('reporting_group'), ), Row( - Column('name'), + Column('object_name'), Column('classification'), Column('redshift'), ), @@ -197,7 +204,7 @@ def __init__(self, *args, **kwargs): Column('fits_file'), ), Row(Column('spectrum_remarks')), - Row(Column(Submit('submit', 'Submit Report'))), + Row(Column(Submit('submit', 'Submit Classification'))), ) def files_to_upload(self): diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html index df42dce..e731fb0 100644 --- a/tom_tns/templates/tom_tns/tns_report.html +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -1,14 +1,66 @@ {% extends 'tom_common/base.html' %} {% load bootstrap4 crispy_forms_tags %} {% block title %}Report to TNS{% endblock %} +{% block additional_css %} {% endblock %} {% block content %} -

Report {{ target.name }} to the TNS

-
-
-
- {% csrf_token %} - {% crispy form %} -
+

Submit {{ target.name }} Information to the TNS

+{% if default_form == 'supernova' %} +
Warning: This target {{target.names}} may have already been reported to and classified with the TNS
+{% endif %} +
+ +
+ +
+ {% if default_form == 'classify' %} +
+
Warning: This target {{target.names}} may have already been reported to the TNS
+ {% else %} +
+ {% endif %} +
+
+

Report {{ target.name }} to the TNS

+
+
+ {% csrf_token %} + {% crispy report_form %} +
+
+
+ {% if default_form == 'classify' %} +
+ {% else %} +
+ {% if default_form != 'supernova' %} +
Warning: This target {{target.names}} may not have been reported to the TNS yet
+ {% endif %} + {% endif %} +
+
+

Classify {{ target.name }} with the TNS

+
+
+ {% csrf_token %} + {% crispy classify_form %} +
+
+ {% endblock %} \ No newline at end of file diff --git a/tom_tns/views.py b/tom_tns/views.py index 75acd04..0e203e9 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -1,10 +1,11 @@ from django.conf import settings from django.urls import reverse_lazy from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin +from django.views.generic.base import TemplateView from django.shortcuts import redirect from guardian.mixins import PermissionListMixin -from tom_tns.forms import TNSReportForm +from tom_tns.forms import TNSReportForm, TNSClassifyForm from tom_tns.tns_report import get_tns_credentials, get_tns_values from tom_targets.models import Target @@ -21,16 +22,77 @@ TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} -class TNSFormView(PermissionListMixin, TemplateResponseMixin, FormMixin, ProcessFormView): - """ - View that handles reporting a target to the TNS. - """ - form_class = TNSReportForm +# class TNSFormView(PermissionListMixin, TemplateResponseMixin, FormMixin, ProcessFormView): +# """ +# View that handles reporting a target to the TNS. +# """ +# template_name = 'tom_tns/tns_report.html' +# +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# context['target'] = Target.objects.get(pk=self.kwargs['pk']) +# context['report_form'] = TNSReportForm(initial=self.get_initial()) +# context['classify_form'] = TNSClassifyForm(initial=self.get_initial()) +# return context +# +# def get_initial(self): +# target = Target.objects.get(pk=self.kwargs['pk']) +# initial = { +# 'ra': target.ra, +# 'dec': target.dec, +# 'object_name': target.name, +# 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', +# 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', +# } +# photometry = target.reduceddatum_set.filter(data_type='photometry') +# if photometry.exists(): +# reduced_datum = photometry.latest() +# initial['observation_date'] = reduced_datum.timestamp +# initial['flux'] = reduced_datum.value['magnitude'] +# initial['flux_error'] = reduced_datum.value['error'] +# filter_name = reduced_datum.value.get('filter') +# if filter_name in TNS_FILTER_IDS: +# initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) +# instrument_name = reduced_datum.value.get('instrument') +# if instrument_name in TNS_INSTRUMENT_IDS: +# initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) +# return initial +# +# def form_valid(self, form): +# # report_id = send_tns_report(form.generate_tns_report()) +# # iau_name = get_tns_report_reply(report_id, self.request) +# +# # update the target name +# # if iau_name is not None: +# # target = Target.objects.get(pk=self.kwargs['pk']) +# # target.name = iau_name +# # target.save() +# return redirect(self.get_success_url()) +# +# def get_success_url(self): +# return reverse_lazy('targets:detail', kwargs=self.kwargs) + + +class TNSFormView(PermissionListMixin, TemplateView): template_name = 'tom_tns/tns_report.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['target'] = Target.objects.get(pk=self.kwargs['pk']) + target = Target.objects.get(pk=self.kwargs['pk']) + context['target'] = target + context['report_form'] = TNSReportForm(initial=self.get_initial()) + context['classify_form'] = TNSClassifyForm(initial=self.get_initial()) + # We want to establish a default tab to display. + # by default, we start on report, but change to classify if the target name starts with AT + # If the target has an SN name, we warn the user that the target has likely been classified already. + context['default_form'] = 'report' + for name in target.names: + if name.upper().startswith('AT'): + context['default_form'] = 'classify' + if name.upper().startswith('SN'): + context['default_form'] = 'supernova' + break + print(context) return context def get_initial(self): @@ -38,7 +100,9 @@ def get_initial(self): initial = { 'ra': target.ra, 'dec': target.dec, - 'reporter': f'{self.request.user.get_full_name()}, on behalf of SAGUARO', + 'object_name': target.name, + 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', + 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', } photometry = target.reduceddatum_set.filter(data_type='photometry') if photometry.exists(): @@ -54,16 +118,14 @@ def get_initial(self): initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) return initial - def form_valid(self, form): - # report_id = send_tns_report(form.generate_tns_report()) - # iau_name = get_tns_report_reply(report_id, self.request) - - # update the target name - # if iau_name is not None: - # target = Target.objects.get(pk=self.kwargs['pk']) - # target.name = iau_name - # target.save() - return redirect(self.get_success_url()) + # def get(self, request, *args, **kwargs): + # return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')}) - def get_success_url(self): - return reverse_lazy('targets:detail', kwargs=self.kwargs) + # def post(self, request, *args, **kwargs): + # aform = _get_form(request, AForm, 'aform_pre') + # bform = _get_form(request, BForm, 'bform_pre') + # if aform.is_bound and aform.is_valid(): + # # Process aform and render response + # elif bform.is_bound and bform.is_valid(): + # # Process bform and render response + # return self.render_to_response({'aform': aform, 'bform': bform}) From 4f13668040199308cea3ddf2607976108141430d Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Thu, 9 Nov 2023 17:59:40 -0800 Subject: [PATCH 05/21] refactor to use template tags --- tom_tns/forms.py | 19 +----- .../tom_tns/partials/tns_classify_form.html | 6 ++ .../tom_tns/partials/tns_report_form.html | 6 ++ tom_tns/templates/tom_tns/tns_report.html | 12 +--- tom_tns/templatetags/__init__.py | 0 tom_tns/templatetags/tns_extras.py | 62 +++++++++++++++++ tom_tns/tns_report.py | 26 ++++++- tom_tns/urls.py | 7 +- tom_tns/views.py | 68 +++++++++++-------- 9 files changed, 146 insertions(+), 60 deletions(-) create mode 100644 tom_tns/templates/tom_tns/partials/tns_classify_form.html create mode 100644 tom_tns/templates/tom_tns/partials/tns_report_form.html create mode 100644 tom_tns/templatetags/__init__.py create mode 100644 tom_tns/templatetags/tns_extras.py diff --git a/tom_tns/forms.py b/tom_tns/forms.py index 6ef2193..fcb603c 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -115,6 +115,7 @@ def generate_tns_report(self): "reporter": self.cleaned_data['reporter'], "discovery_datetime": self.cleaned_data['discovery_date'].strftime('%Y-%m-%d %H:%M:%S'), "at_type": self.cleaned_data['at_type'], + "remarks": self.cleaned_data["discovery_remarks"], "non_detection": { "archiveid": self.cleaned_data['archive'], "archival_remarks": self.cleaned_data['archival_remarks'], @@ -132,7 +133,7 @@ def generate_tns_report(self): "limiting_flux": self.cleaned_data['limiting_flux'], "exptime": self.cleaned_data['exposure_time'], "observer": self.cleaned_data['observer'], - "comments": self.cleaned_data['comments'], + "comments": self.cleaned_data['photometry_remarks'], }, } }, @@ -207,22 +208,6 @@ def __init__(self, *args, **kwargs): Row(Column(Submit('submit', 'Submit Classification'))), ) - def files_to_upload(self): - files_to_upload = {} - if self.cleaned_data['ascii_file'] is not None: - files_to_upload['files[0]'] = ( - os.path.basename(self.cleaned_data['ascii_file'].name), - self.cleaned_data['ascii_file'].open(), - 'text/plain' - ) - if self.cleaned_data['fits_file'] is not None: - files_to_upload['files[1]'] = ( - os.path.basename(self.cleaned_data['fits_file'].name), - self.cleaned_data['fits_file'].open(), - 'application/fits' - ) - return files_to_upload - def generate_tns_report(self, new_filenames=None): """ Generate TNS bulk classification report according to the schema in this manual: diff --git a/tom_tns/templates/tom_tns/partials/tns_classify_form.html b/tom_tns/templates/tom_tns/partials/tns_classify_form.html new file mode 100644 index 0000000..ccaf2b1 --- /dev/null +++ b/tom_tns/templates/tom_tns/partials/tns_classify_form.html @@ -0,0 +1,6 @@ +{% load crispy_forms_tags %} + +
+ {% csrf_token %} + {% crispy form %} +
\ No newline at end of file diff --git a/tom_tns/templates/tom_tns/partials/tns_report_form.html b/tom_tns/templates/tom_tns/partials/tns_report_form.html new file mode 100644 index 0000000..aa8e027 --- /dev/null +++ b/tom_tns/templates/tom_tns/partials/tns_report_form.html @@ -0,0 +1,6 @@ +{% load crispy_forms_tags %} + +
+ {% csrf_token %} + {% crispy form %} +
\ No newline at end of file diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html index e731fb0..77019ab 100644 --- a/tom_tns/templates/tom_tns/tns_report.html +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -1,5 +1,5 @@ {% extends 'tom_common/base.html' %} -{% load bootstrap4 crispy_forms_tags %} +{% load bootstrap4 tns_extras%} {% block title %}Report to TNS{% endblock %} {% block additional_css %} {% endblock %} {% block content %} @@ -37,10 +37,7 @@

Submit {{ tar

Report {{ target.name }} to the TNS


-
- {% csrf_token %} - {% crispy report_form %} -
+ {% report_to_tns %}

{% if default_form == 'classify' %} @@ -55,10 +52,7 @@

Report {{ target.name }} to the TNS


Classify {{ target.name }} with the TNS


-
- {% csrf_token %} - {% crispy classify_form %} -
+ {% classify_with_tns %}
diff --git a/tom_tns/templatetags/__init__.py b/tom_tns/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tom_tns/templatetags/tns_extras.py b/tom_tns/templatetags/tns_extras.py new file mode 100644 index 0000000..b621897 --- /dev/null +++ b/tom_tns/templatetags/tns_extras.py @@ -0,0 +1,62 @@ +from django import template +from django.conf import settings + +from tom_tns.tns_report import get_tns_credentials, get_tns_values +from tom_tns.forms import TNSReportForm, TNSClassifyForm + +register = template.Library() + +TNS_FILTER_IDS = {name: fid for fid, name in get_tns_values('filters')} +TNS_INSTRUMENT_IDS = {name: iid for iid, name in get_tns_values('instruments')} +TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} + + +@register.inclusion_tag('tom_tns/partials/tns_report_form.html', takes_context=True) +def report_to_tns(context): + target = context['target'] + initial = { + 'ra': target.ra, + 'dec': target.dec, + 'reporter': f"{context['request'].user.get_full_name()}, using {settings.TOM_NAME}", + } + # Get photometry if available + photometry = target.reduceddatum_set.filter(data_type='photometry') + if photometry.exists(): + reduced_datum = photometry.latest() + initial['observation_date'] = reduced_datum.timestamp + initial['flux'] = reduced_datum.value['magnitude'] + initial['flux_error'] = reduced_datum.value['error'] + filter_name = reduced_datum.value.get('filter') + if filter_name in TNS_FILTER_IDS: + initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) + instrument_name = reduced_datum.value.get('instrument') + if instrument_name in TNS_INSTRUMENT_IDS: + initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) + tns_report_form = TNSReportForm(initial=initial) + return {'target': target, + 'form': tns_report_form} + + +@register.inclusion_tag('tom_tns/partials/tns_classify_form.html', takes_context=True) +def classify_with_tns(context): + target = context['target'] + initial = { + 'object_name': target.name, + 'classifier': f'{context["request"].user.get_full_name()}, using {settings.TOM_NAME}', + } + # Get photometry if available + photometry = target.reduceddatum_set.filter(data_type='photometry') + if photometry.exists(): + reduced_datum = photometry.latest() + initial['observation_date'] = reduced_datum.timestamp + initial['flux'] = reduced_datum.value['magnitude'] + initial['flux_error'] = reduced_datum.value['error'] + filter_name = reduced_datum.value.get('filter') + if filter_name in TNS_FILTER_IDS: + initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) + instrument_name = reduced_datum.value.get('instrument') + if instrument_name in TNS_INSTRUMENT_IDS: + initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) + tns_classify_form = TNSClassifyForm(initial=initial) + return {'target': target, + 'form': tns_classify_form} diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index 80aa2b4..84d378c 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -4,6 +4,7 @@ from django.core.cache import cache from django.conf import settings +import json import logging logger = logging.getLogger(__name__) @@ -82,13 +83,32 @@ def reverse_tns_values(all_tns_values): return reversed_tns_values -def upload_files_to_tns(files): +def build_file_dict(files): + """ + Build a dictionary of files to upload to the TNS. + """ + file_load = {} + for i, file in enumerate(files): + if file['type'] == 'ascii': + file_load[f'file[{i}]'] = (file['filename'], file['content'], 'text/plain') + if file['type'] == 'fits': + file_load[f'file[{i}]'] = (file['filename'], file['content'], 'application/fits') + return file_load + + +def pre_upload_files_to_tns(files): """ Upload files to the Transient Name Server according to this manual: https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf """ - json_data = {'api_key': TNS['api_key']} - response = requests.post(TNS_URL + '/file-upload', headers={'User-Agent': TNS_MARKER}, data=json_data, files=files) + tns_credentials = get_tns_credentials() + file_load = build_file_dict(files) + tns_marker = 'tns_marker' + json.dumps({'tns_id': tns_credentials['bot_id'], + 'type': 'bot', + 'name': tns_credentials['bot_name']}) + json_data = {'api_key': tns_credentials['api_key']} + response = requests.post(tns_credentials['url'] + '/file-upload', headers={'User-Agent': tns_marker}, + data=json_data, files=file_load) response.raise_for_status() new_filenames = response.json()['data'] logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") diff --git a/tom_tns/urls.py b/tom_tns/urls.py index 44018c6..b8936aa 100644 --- a/tom_tns/urls.py +++ b/tom_tns/urls.py @@ -1,9 +1,12 @@ from django.urls import path -from tom_tns.views import TNSFormView +from tom_tns.views import TNSFormView, TNSSubmitView +from tom_tns.forms import TNSReportForm, TNSClassifyForm app_name = 'tom_tns' urlpatterns = [ - path('/report/', TNSFormView.as_view(), name='report-tns'), + path('/', TNSFormView.as_view(), name='report-tns'), + path('/report', TNSSubmitView.as_view(form_class=TNSReportForm), name='submit-report'), + path('/classify', TNSSubmitView.as_view(form_class=TNSClassifyForm), name='submit-classify'), ] diff --git a/tom_tns/views.py b/tom_tns/views.py index 0e203e9..011b06e 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -1,8 +1,10 @@ from django.conf import settings from django.urls import reverse_lazy -from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin +from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin, FormView from django.views.generic.base import TemplateView from django.shortcuts import redirect +from django.http import HttpResponseRedirect +from django.contrib import messages from guardian.mixins import PermissionListMixin from tom_tns.forms import TNSReportForm, TNSClassifyForm @@ -80,8 +82,6 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) target = Target.objects.get(pk=self.kwargs['pk']) context['target'] = target - context['report_form'] = TNSReportForm(initial=self.get_initial()) - context['classify_form'] = TNSClassifyForm(initial=self.get_initial()) # We want to establish a default tab to display. # by default, we start on report, but change to classify if the target name starts with AT # If the target has an SN name, we warn the user that the target has likely been classified already. @@ -92,34 +92,30 @@ def get_context_data(self, **kwargs): if name.upper().startswith('SN'): context['default_form'] = 'supernova' break - print(context) return context - def get_initial(self): - target = Target.objects.get(pk=self.kwargs['pk']) - initial = { - 'ra': target.ra, - 'dec': target.dec, - 'object_name': target.name, - 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', - 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', - } - photometry = target.reduceddatum_set.filter(data_type='photometry') - if photometry.exists(): - reduced_datum = photometry.latest() - initial['observation_date'] = reduced_datum.timestamp - initial['flux'] = reduced_datum.value['magnitude'] - initial['flux_error'] = reduced_datum.value['error'] - filter_name = reduced_datum.value.get('filter') - if filter_name in TNS_FILTER_IDS: - initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) - instrument_name = reduced_datum.value.get('instrument') - if instrument_name in TNS_INSTRUMENT_IDS: - initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) - return initial - - # def get(self, request, *args, **kwargs): - # return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')}) + # def get_initial(self): + # target = Target.objects.get(pk=self.kwargs['pk']) + # initial = { + # 'ra': target.ra, + # 'dec': target.dec, + # 'object_name': target.name, + # 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', + # 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', + # } + # photometry = target.reduceddatum_set.filter(data_type='photometry') + # if photometry.exists(): + # reduced_datum = photometry.latest() + # initial['observation_date'] = reduced_datum.timestamp + # initial['flux'] = reduced_datum.value['magnitude'] + # initial['flux_error'] = reduced_datum.value['error'] + # filter_name = reduced_datum.value.get('filter') + # if filter_name in TNS_FILTER_IDS: + # initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) + # instrument_name = reduced_datum.value.get('instrument') + # if instrument_name in TNS_INSTRUMENT_IDS: + # initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) + # return initial # def post(self, request, *args, **kwargs): # aform = _get_form(request, AForm, 'aform_pre') @@ -129,3 +125,17 @@ def get_initial(self): # elif bform.is_bound and bform.is_valid(): # # Process bform and render response # return self.render_to_response({'aform': aform, 'bform': bform}) + + +class TNSSubmitView(FormView): + success_url = reverse_lazy('targets:list') + + def form_invalid(self, form): + messages.error(self.request, 'The following error was encountered when submitting to the TNS: ' + f'{form.errors.as_json()}') + return HttpResponseRedirect(self.get_success_url()) + + def form_valid(self, form): + tns_report = form.generate_tns_report() + print(tns_report) + return HttpResponseRedirect(self.get_success_url()) From 404774674f9ba308ec6bfa2f46891ec58b4f4cdd Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Mon, 13 Nov 2023 11:15:02 -0800 Subject: [PATCH 06/21] file upload infrastructure --- tom_tns/forms.py | 22 +++++++++---------- .../tom_tns/partials/tns_classify_form.html | 2 +- tom_tns/tns_report.py | 18 ++++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index fcb603c..8e76633 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -1,7 +1,7 @@ from django import forms from django.conf import settings -from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values +from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values, pre_upload_files_to_tns from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Row, Column, Submit, HTML @@ -156,8 +156,8 @@ class TNSClassifyForm(forms.Form): observer = forms.CharField() reducer = forms.CharField(required=False) spectrum_type = forms.ChoiceField(choices=[]) - ascii_file = forms.FileField(label='ASCII file') - fits_file = forms.FileField(label='FITS file', required=False) + ascii_file = forms.FileField(label='ASCII file', required=False, widget=forms.ClearableFileInput()) + fits_file = forms.FileField(label='FITS file', required=False, widget=forms.ClearableFileInput()) spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) def __init__(self, *args, **kwargs): @@ -208,17 +208,15 @@ def __init__(self, *args, **kwargs): Row(Column(Submit('submit', 'Submit Classification'))), ) - def generate_tns_report(self, new_filenames=None): + def generate_tns_report(self): """ Generate TNS bulk classification report according to the schema in this manual: https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf Returns the report as a JSON-formatted string """ - if new_filenames is None: - ascii_filename = os.path.basename(self.cleaned_data['ascii_file'].name) - else: - ascii_filename = new_filenames[0] + file_list = {'ascii_file': self.cleaned_data['ascii_file'], 'fits_file': self.cleaned_data['fits_file']} + tns_filenames = pre_upload_files_to_tns(file_list) report_data = { "classification_report": { "0": { @@ -237,7 +235,7 @@ def generate_tns_report(self, new_filenames=None): "observer": self.cleaned_data['observer'], "reducer": self.cleaned_data['reducer'], "spectypeid": self.cleaned_data['spectrum_type'], - "ascii_file": ascii_filename, + "ascii_file": tns_filenames['ascii_file'], "remarks": self.cleaned_data['spectrum_remarks'], }, } @@ -246,9 +244,9 @@ def generate_tns_report(self, new_filenames=None): } } if self.cleaned_data['fits_file'] is not None: - if new_filenames is None: + if tns_filenames is None: fits_filename = os.path.basename(self.cleaned_data['fits_file'].name) else: - fits_filename = new_filenames[1] - report_data['classification_report']['0']['spectra']['spectra_group']['0']['fits_file'] = fits_filename + fits_filename = tns_filenames[1] + report_data['classification_report']['0']['spectra']['spectra_group']['0']['fits_file'] = tn_filenames['fits_file'] return json.dumps(report_data) diff --git a/tom_tns/templates/tom_tns/partials/tns_classify_form.html b/tom_tns/templates/tom_tns/partials/tns_classify_form.html index ccaf2b1..dec5794 100644 --- a/tom_tns/templates/tom_tns/partials/tns_classify_form.html +++ b/tom_tns/templates/tom_tns/partials/tns_classify_form.html @@ -1,6 +1,6 @@ {% load crispy_forms_tags %} -
+ {% csrf_token %} {% crispy form %}
\ No newline at end of file diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index 84d378c..700dc5a 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -88,12 +88,13 @@ def build_file_dict(files): Build a dictionary of files to upload to the TNS. """ file_load = {} - for i, file in enumerate(files): - if file['type'] == 'ascii': - file_load[f'file[{i}]'] = (file['filename'], file['content'], 'text/plain') - if file['type'] == 'fits': - file_load[f'file[{i}]'] = (file['filename'], file['content'], 'application/fits') - return file_load + i = 0 + for file in files: + if files[file]: + file_load[f'file[{i}]'] = (files[file].name, files[file].open(), files[file].content_type) + files[file] = f'file[{i}]' + i += 1 + return file_load, files def pre_upload_files_to_tns(files): @@ -102,16 +103,17 @@ def pre_upload_files_to_tns(files): https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf """ tns_credentials = get_tns_credentials() - file_load = build_file_dict(files) + file_load, files = build_file_dict(files) tns_marker = 'tns_marker' + json.dumps({'tns_id': tns_credentials['bot_id'], 'type': 'bot', 'name': tns_credentials['bot_name']}) json_data = {'api_key': tns_credentials['api_key']} - response = requests.post(tns_credentials['url'] + '/file-upload', headers={'User-Agent': tns_marker}, + response = requests.post(tns_credentials['tns_base_url'] + '/file-upload', headers={'User-Agent': tns_marker}, data=json_data, files=file_load) response.raise_for_status() new_filenames = response.json()['data'] logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") + return new_filenames From aacfb56d42f7e7e95634058e81439fad6a9f0b9b Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Mon, 13 Nov 2023 17:32:41 -0800 Subject: [PATCH 07/21] Automatically rename target on TNS response --- tom_tns/forms.py | 36 +++++++++------ tom_tns/tns_report.py | 64 +++++++++++++++++++------ tom_tns/views.py | 105 ++++++++---------------------------------- 3 files changed, 89 insertions(+), 116 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index 8e76633..e205d24 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -1,7 +1,10 @@ +import requests + from django import forms from django.conf import settings -from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values, pre_upload_files_to_tns +from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values, pre_upload_files_to_tns,\ + BadTnsRequest from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Row, Column, Submit, HTML @@ -140,7 +143,7 @@ def generate_tns_report(self): } } } - return json.dumps(report_data) + return report_data class TNSClassifyForm(forms.Form): @@ -215,16 +218,21 @@ def generate_tns_report(self): Returns the report as a JSON-formatted string """ - file_list = {'ascii_file': self.cleaned_data['ascii_file'], 'fits_file': self.cleaned_data['fits_file']} - tns_filenames = pre_upload_files_to_tns(file_list) + file_list = {'ascii_file': self.cleaned_data['ascii_file'], + 'fits_file': self.cleaned_data['fits_file'], + 'other_files': []} + try: + tns_filenames = pre_upload_files_to_tns(file_list) + except requests.exceptions.HTTPError as e: + return {'message': f"ERROR: {e}"} report_data = { "classification_report": { "0": { - "name": self.cleaned_data['name'], + "name": self.cleaned_data['object_name'], "classifier": self.cleaned_data['classifier'], "objtypeid": self.cleaned_data['classification'], "redshift": self.cleaned_data['redshift'], - "groupid": self.cleaned_data['group'], + "groupid": self.cleaned_data['reporting_group'], "remarks": self.cleaned_data['classification_remarks'], "spectra": { "spectra-group": { @@ -235,7 +243,6 @@ def generate_tns_report(self): "observer": self.cleaned_data['observer'], "reducer": self.cleaned_data['reducer'], "spectypeid": self.cleaned_data['spectrum_type'], - "ascii_file": tns_filenames['ascii_file'], "remarks": self.cleaned_data['spectrum_remarks'], }, } @@ -243,10 +250,11 @@ def generate_tns_report(self): } } } - if self.cleaned_data['fits_file'] is not None: - if tns_filenames is None: - fits_filename = os.path.basename(self.cleaned_data['fits_file'].name) - else: - fits_filename = tns_filenames[1] - report_data['classification_report']['0']['spectra']['spectra_group']['0']['fits_file'] = tn_filenames['fits_file'] - return json.dumps(report_data) + if tns_filenames: + report_data['classification_report']['0']['spectra']['spectra-group']['0']['ascii_file'] = \ + tns_filenames['ascii_file'] + report_data['classification_report']['0']['spectra']['spectra-group']['0']['fits_file'] = \ + tns_filenames['fits_file'] + else: + raise BadTnsRequest("No files were uploaded to TNS") + return report_data diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index 700dc5a..ad755b3 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -3,17 +3,28 @@ from django.core.cache import cache from django.conf import settings +from django.contrib import messages import json +import time import logging logger = logging.getLogger(__name__) +class BadTnsRequest(Exception): + """ This Exception will be raised by errors during the TNS submission process """ + pass + + def get_tns_credentials(): """ Get the TNS credentials from settings.py. """ - return settings.BROKERS['TNS'] + tns_info = settings.BROKERS['TNS'] + tns_info['marker'] = 'tns_marker' + json.dumps({'tns_id': tns_info['bot_id'], + 'type': 'bot', + 'name': tns_info['bot_name']}) + return tns_info def get_tns_values(option_list): @@ -26,7 +37,7 @@ def get_tns_values(option_list): selected_values = all_tns_values[option_list] tuple_list = [] if isinstance(selected_values, list): - tuple_list = [(v, v) for v in selected_values] + tuple_list = [(i, v) for i, v in enumerate(selected_values)] if isinstance(selected_values, dict): tuple_list = [(k, v) for k, v in selected_values.items()] return tuple_list @@ -89,11 +100,25 @@ def build_file_dict(files): """ file_load = {} i = 0 - for file in files: - if files[file]: - file_load[f'file[{i}]'] = (files[file].name, files[file].open(), files[file].content_type) - files[file] = f'file[{i}]' + if files['ascii_file']: + file_load[f'file[{i}]'] = (files['ascii_file'].name, + files['ascii_file'].open(), + files['ascii_file'].content_type) + files['ascii_file'] = i + i += 1 + if files['fits_file']: + file_load[f'file[{i}]'] = (files['fits_file'].name, + files['fits_file'].open(), + files['fits_file'].content_type) + files['fits_file'] = i + i += 1 + other_index = [] + for file in files['other_files']: + if file: + file_load[f'file[{i}]'] = (file.name, file.open(), file.content_type) + other_index.append(i) i += 1 + files['other_files'] = other_index return file_load, files @@ -104,14 +129,18 @@ def pre_upload_files_to_tns(files): """ tns_credentials = get_tns_credentials() file_load, files = build_file_dict(files) - tns_marker = 'tns_marker' + json.dumps({'tns_id': tns_credentials['bot_id'], - 'type': 'bot', - 'name': tns_credentials['bot_name']}) + print(file_load) + if not file_load: + return None + tns_marker = tns_credentials['marker'] json_data = {'api_key': tns_credentials['api_key']} response = requests.post(tns_credentials['tns_base_url'] + '/file-upload', headers={'User-Agent': tns_marker}, data=json_data, files=file_load) + print(response.text) + print(response) + print(response.json()) response.raise_for_status() - new_filenames = response.json()['data'] + new_filenames = response.json().get('data', {}) logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") return new_filenames @@ -122,8 +151,11 @@ def send_tns_report(data): Send a JSON bulk report to the Transient Name Server according to this manual: https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf """ - json_data = {'api_key': TNS['api_key'], 'data': data} - response = requests.post(TNS_URL + '/bulk-report', headers={'User-Agent': TNS_MARKER}, data=json_data) + tns_info = get_tns_credentials() + json_data = {'api_key': tns_info['api_key'], 'data': data} + response = requests.post(tns_info['tns_base_url'] + '/bulk-report', + headers={'User-Agent': tns_info['marker']}, + data=json_data) response.raise_for_status() report_id = response.json()['data']['report_id'] logger.info(f'Sent TNS report ID {report_id:d}') @@ -137,10 +169,12 @@ def get_tns_report_reply(report_id, request): Posts an informational message in a banner on the page using ``request`` """ - json_data = {'api_key': TNS['api_key'], 'report_id': report_id} + tns_info = get_tns_credentials() + json_data = {'api_key': tns_info['api_key'], 'report_id': report_id} for _ in range(6): time.sleep(5) - response = requests.post(TNS_URL + '/bulk-report-reply', headers={'User-Agent': TNS_MARKER}, data=json_data) + response = requests.post(tns_info['tns_base_url'] + '/bulk-report-reply', + headers={'User-Agent': tns_info['marker']}, data=json_data) if response.ok: break response.raise_for_status() @@ -174,4 +208,4 @@ def get_tns_report_reply(report_id, request): log_message = 'Problem getting response from TNS' logger.error(log_message) messages.error(request, log_message) - return iau_name \ No newline at end of file + return iau_name diff --git a/tom_tns/views.py b/tom_tns/views.py index 011b06e..bac3eff 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -1,3 +1,4 @@ +import requests.exceptions from django.conf import settings from django.urls import reverse_lazy from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin, FormView @@ -8,8 +9,9 @@ from guardian.mixins import PermissionListMixin from tom_tns.forms import TNSReportForm, TNSClassifyForm -from tom_tns.tns_report import get_tns_credentials, get_tns_values -from tom_targets.models import Target +from tom_tns.tns_report import get_tns_credentials, get_tns_values, send_tns_report, get_tns_report_reply,\ + BadTnsRequest +from tom_targets.models import Target, TargetName import json @@ -24,57 +26,6 @@ TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} -# class TNSFormView(PermissionListMixin, TemplateResponseMixin, FormMixin, ProcessFormView): -# """ -# View that handles reporting a target to the TNS. -# """ -# template_name = 'tom_tns/tns_report.html' -# -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) -# context['target'] = Target.objects.get(pk=self.kwargs['pk']) -# context['report_form'] = TNSReportForm(initial=self.get_initial()) -# context['classify_form'] = TNSClassifyForm(initial=self.get_initial()) -# return context -# -# def get_initial(self): -# target = Target.objects.get(pk=self.kwargs['pk']) -# initial = { -# 'ra': target.ra, -# 'dec': target.dec, -# 'object_name': target.name, -# 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', -# 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', -# } -# photometry = target.reduceddatum_set.filter(data_type='photometry') -# if photometry.exists(): -# reduced_datum = photometry.latest() -# initial['observation_date'] = reduced_datum.timestamp -# initial['flux'] = reduced_datum.value['magnitude'] -# initial['flux_error'] = reduced_datum.value['error'] -# filter_name = reduced_datum.value.get('filter') -# if filter_name in TNS_FILTER_IDS: -# initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) -# instrument_name = reduced_datum.value.get('instrument') -# if instrument_name in TNS_INSTRUMENT_IDS: -# initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) -# return initial -# -# def form_valid(self, form): -# # report_id = send_tns_report(form.generate_tns_report()) -# # iau_name = get_tns_report_reply(report_id, self.request) -# -# # update the target name -# # if iau_name is not None: -# # target = Target.objects.get(pk=self.kwargs['pk']) -# # target.name = iau_name -# # target.save() -# return redirect(self.get_success_url()) -# -# def get_success_url(self): -# return reverse_lazy('targets:detail', kwargs=self.kwargs) - - class TNSFormView(PermissionListMixin, TemplateView): template_name = 'tom_tns/tns_report.html' @@ -94,38 +45,6 @@ def get_context_data(self, **kwargs): break return context - # def get_initial(self): - # target = Target.objects.get(pk=self.kwargs['pk']) - # initial = { - # 'ra': target.ra, - # 'dec': target.dec, - # 'object_name': target.name, - # 'reporter': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', - # 'classifier': f'{self.request.user.get_full_name()}, using {settings.TOM_NAME}', - # } - # photometry = target.reduceddatum_set.filter(data_type='photometry') - # if photometry.exists(): - # reduced_datum = photometry.latest() - # initial['observation_date'] = reduced_datum.timestamp - # initial['flux'] = reduced_datum.value['magnitude'] - # initial['flux_error'] = reduced_datum.value['error'] - # filter_name = reduced_datum.value.get('filter') - # if filter_name in TNS_FILTER_IDS: - # initial['filter'] = (TNS_FILTER_IDS[filter_name], filter_name) - # instrument_name = reduced_datum.value.get('instrument') - # if instrument_name in TNS_INSTRUMENT_IDS: - # initial['instrument'] = (TNS_INSTRUMENT_IDS[instrument_name], instrument_name) - # return initial - - # def post(self, request, *args, **kwargs): - # aform = _get_form(request, AForm, 'aform_pre') - # bform = _get_form(request, BForm, 'bform_pre') - # if aform.is_bound and aform.is_valid(): - # # Process aform and render response - # elif bform.is_bound and bform.is_valid(): - # # Process bform and render response - # return self.render_to_response({'aform': aform, 'bform': bform}) - class TNSSubmitView(FormView): success_url = reverse_lazy('targets:list') @@ -136,6 +55,18 @@ def form_invalid(self, form): return HttpResponseRedirect(self.get_success_url()) def form_valid(self, form): - tns_report = form.generate_tns_report() - print(tns_report) + try: + tns_report = form.generate_tns_report() + report_id = send_tns_report(json.dumps(tns_report)) + iau_name = get_tns_report_reply(report_id, self.request) + # update the target name + if iau_name is not None: + target = Target.objects.get(pk=self.kwargs['pk']) + old_name = target.name + target.name = iau_name + target.save() + new_alias = TargetName(name=old_name, target=target) + new_alias.save() + except (requests.exceptions.HTTPError, BadTnsRequest) as e: + messages.error(self.request, f'TNS returned an error: {e}') return HttpResponseRedirect(self.get_success_url()) From 071ddf83407745e7447610ffcd61d9eabe152212 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 15 Nov 2023 13:41:48 -0800 Subject: [PATCH 08/21] clean up classification submission --- tom_tns/forms.py | 6 +- tom_tns/templatetags/tns_extras.py | 2 +- tom_tns/tns_report.py | 90 +++++++++++++++++------------- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index e205d24..bf7c792 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -159,7 +159,7 @@ class TNSClassifyForm(forms.Form): observer = forms.CharField() reducer = forms.CharField(required=False) spectrum_type = forms.ChoiceField(choices=[]) - ascii_file = forms.FileField(label='ASCII file', required=False, widget=forms.ClearableFileInput()) + ascii_file = forms.FileField(label='ASCII file', required=True, widget=forms.ClearableFileInput()) fits_file = forms.FileField(label='FITS file', required=False, widget=forms.ClearableFileInput()) spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) @@ -252,9 +252,9 @@ def generate_tns_report(self): } if tns_filenames: report_data['classification_report']['0']['spectra']['spectra-group']['0']['ascii_file'] = \ - tns_filenames['ascii_file'] + tns_filenames.get('ascii_file', '') report_data['classification_report']['0']['spectra']['spectra-group']['0']['fits_file'] = \ - tns_filenames['fits_file'] + tns_filenames.get('fits_file', '') else: raise BadTnsRequest("No files were uploaded to TNS") return report_data diff --git a/tom_tns/templatetags/tns_extras.py b/tom_tns/templatetags/tns_extras.py index b621897..79f8be3 100644 --- a/tom_tns/templatetags/tns_extras.py +++ b/tom_tns/templatetags/tns_extras.py @@ -41,7 +41,7 @@ def report_to_tns(context): def classify_with_tns(context): target = context['target'] initial = { - 'object_name': target.name, + 'object_name': target.name.replace('AT', '').replace('SN', ''), 'classifier': f'{context["request"].user.get_full_name()}, using {settings.TOM_NAME}', } # Get photometry if available diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index ad755b3..abc9a26 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -98,28 +98,18 @@ def build_file_dict(files): """ Build a dictionary of files to upload to the TNS. """ + new_files = {} file_load = {} i = 0 if files['ascii_file']: - file_load[f'file[{i}]'] = (files['ascii_file'].name, - files['ascii_file'].open(), - files['ascii_file'].content_type) - files['ascii_file'] = i + file_load[f'files[{i}]'] = (files['ascii_file'].name, files['ascii_file'].open(), 'text/plain') + new_files['ascii_file'] = i i += 1 if files['fits_file']: - file_load[f'file[{i}]'] = (files['fits_file'].name, - files['fits_file'].open(), - files['fits_file'].content_type) - files['fits_file'] = i + file_load[f'files[{i}]'] = (files['fits_file'].name, files['fits_file'].open('rb'), 'application/fits') + new_files['fits_file'] = i i += 1 - other_index = [] - for file in files['other_files']: - if file: - file_load[f'file[{i}]'] = (file.name, file.open(), file.content_type) - other_index.append(i) - i += 1 - files['other_files'] = other_index - return file_load, files + return file_load, new_files def pre_upload_files_to_tns(files): @@ -128,22 +118,24 @@ def pre_upload_files_to_tns(files): https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf """ tns_credentials = get_tns_credentials() - file_load, files = build_file_dict(files) - print(file_load) + file_load, new_files = build_file_dict(files) if not file_load: return None tns_marker = tns_credentials['marker'] json_data = {'api_key': tns_credentials['api_key']} response = requests.post(tns_credentials['tns_base_url'] + '/file-upload', headers={'User-Agent': tns_marker}, data=json_data, files=file_load) - print(response.text) - print(response) - print(response.json()) response.raise_for_status() new_filenames = response.json().get('data', {}) logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") - - return new_filenames + if not new_filenames: + return None + for file in new_files: + try: + new_files[file] = new_filenames[new_files[file]] + except IndexError: + new_files[file] = '' + return new_files def send_tns_report(data): @@ -162,23 +154,8 @@ def send_tns_report(data): return report_id -def get_tns_report_reply(report_id, request): - """ - Get feedback from the Transient Name Server in response to a bulk report according to this manual: - https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf - - Posts an informational message in a banner on the page using ``request`` - """ - tns_info = get_tns_credentials() - json_data = {'api_key': tns_info['api_key'], 'report_id': report_id} - for _ in range(6): - time.sleep(5) - response = requests.post(tns_info['tns_base_url'] + '/bulk-report-reply', - headers={'User-Agent': tns_info['marker']}, data=json_data) - if response.ok: - break - response.raise_for_status() - feedback_section = response.json()['data']['feedback'] +def parse_object_from_tns_response(response_json, request): + feedback_section = response_json['data']['feedback'] feedbacks = [] if 'at_report' in feedback_section: feedbacks += feedback_section['at_report'] @@ -209,3 +186,36 @@ def get_tns_report_reply(report_id, request): logger.error(log_message) messages.error(request, log_message) return iau_name + + +def get_tns_report_reply(report_id, request): + """ + Get feedback from the Transient Name Server in response to a bulk report according to this manual: + https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + + Posts an informational message in a banner on the page using ``request`` + """ + tns_info = get_tns_credentials() + reply_data = {'api_key': tns_info['api_key'], 'report_id': report_id} + attempts = 0 + # TNS Submissions return immediately with an id, which you must then check to see if the message + # was processed, and if it was accepted or rejected. Here we check up to 10 times, waiting 1s + # between checks. Under normal circumstances, it should be processed within a few seconds. + while attempts < 10: + response = requests.post(tns_info['tns_base_url'] + '/bulk-report-reply', + headers={'User-Agent': tns_info['marker']}, data=reply_data) + attempts += 1 + # A 404 response means the report has not been processed yet + if response.status_code == 404: + time.sleep(1) + # A 400 response means the report failed with certain errors + elif response.status_code == 400: + raise BadTnsRequest(f"TNS submission failed with feedback: " + f"{response.json().get('data', {}).get('feedback', {})}") + # A 200 response means the report was successful, and we can parse out the object name + elif response.status_code == 200: + iau_name = parse_object_from_tns_response(response.json(), request) + break + else: + raise BadTnsRequest(f"TNS submission failed with status code {response.status_code}") + return iau_name From af41d889945f8a5798ae3e4b4a02d36e1c17b0bf Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 15 Nov 2023 13:56:59 -0800 Subject: [PATCH 09/21] clean up a few things' --- tom_tns/forms.py | 2 -- tom_tns/templatetags/tns_extras.py | 2 +- tom_tns/views.py | 22 ++++++---------------- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index bf7c792..9ae4acb 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -10,8 +10,6 @@ from crispy_forms.layout import Layout, Row, Column, Submit, HTML from crispy_forms.bootstrap import AppendedText from datetime import datetime -import json -import os class TNSReportForm(forms.Form): diff --git a/tom_tns/templatetags/tns_extras.py b/tom_tns/templatetags/tns_extras.py index 79f8be3..8eaccb6 100644 --- a/tom_tns/templatetags/tns_extras.py +++ b/tom_tns/templatetags/tns_extras.py @@ -1,7 +1,7 @@ from django import template from django.conf import settings -from tom_tns.tns_report import get_tns_credentials, get_tns_values +from tom_tns.tns_report import get_tns_values from tom_tns.forms import TNSReportForm, TNSClassifyForm register = template.Library() diff --git a/tom_tns/views.py b/tom_tns/views.py index bac3eff..dd0bba3 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -1,31 +1,19 @@ import requests.exceptions -from django.conf import settings + from django.urls import reverse_lazy -from django.views.generic.edit import ProcessFormView, TemplateResponseMixin, FormMixin, FormView +from django.views.generic.edit import FormView from django.views.generic.base import TemplateView -from django.shortcuts import redirect from django.http import HttpResponseRedirect from django.contrib import messages from guardian.mixins import PermissionListMixin -from tom_tns.forms import TNSReportForm, TNSClassifyForm -from tom_tns.tns_report import get_tns_credentials, get_tns_values, send_tns_report, get_tns_report_reply,\ +from tom_tns.tns_report import send_tns_report, get_tns_report_reply,\ BadTnsRequest from tom_targets.models import Target, TargetName import json -TNS_URL = 'https://sandbox.wis-tns.org/api' # TODO: change this to the main site -TNS_credentials = get_tns_credentials() -TNS_MARKER = 'tns_marker' + json.dumps({'tns_id': TNS_credentials['bot_id'], - 'type': 'bot', - 'name': TNS_credentials['bot_name']}) -TNS_FILTER_IDS = {name: fid for fid, name in get_tns_values('filters')} -TNS_INSTRUMENT_IDS = {name: iid for iid, name in get_tns_values('instruments')} -TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} - - class TNSFormView(PermissionListMixin, TemplateView): template_name = 'tom_tns/tns_report.html' @@ -47,7 +35,9 @@ def get_context_data(self, **kwargs): class TNSSubmitView(FormView): - success_url = reverse_lazy('targets:list') + + def get_success_url(self): + return reverse_lazy('targets:detail', kwargs=self.kwargs) def form_invalid(self, form): messages.error(self.request, 'The following error was encountered when submitting to the TNS: ' From 4d84419ac0a0a3651ce2f517b6ef80b13245583f Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 15 Nov 2023 15:44:44 -0800 Subject: [PATCH 10/21] add doc strings --- tom_tns/forms.py | 17 +++++----- tom_tns/templatetags/tns_extras.py | 10 ++++++ tom_tns/tns_report.py | 50 ++++++++++++++++++------------ tom_tns/views.py | 18 +++++++++-- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index 9ae4acb..2ba5fbb 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -36,6 +36,10 @@ class TNSReportForm(forms.Form): nondetection_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) def __init__(self, *args, **kwargs): + """ + Initialize choices from TNS API values, and set common defaults. + Also define the form layout using crispy_forms. + """ super().__init__(*args, **kwargs) self.fields['reporting_group'].choices = get_tns_values('groups') self.fields['discovery_data_source'].choices = get_tns_values('groups') @@ -162,6 +166,10 @@ class TNSClassifyForm(forms.Form): spectrum_remarks = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2})) def __init__(self, *args, **kwargs): + """ + Set initial choices for the classification form using the TNS API values and set default values. + Also define the form layout using crispy-forms. + """ super().__init__(*args, **kwargs) self.fields['reporting_group'].choices = get_tns_values('groups') self.fields['instrument'].choices = get_tns_values('instruments') @@ -241,6 +249,8 @@ def generate_tns_report(self): "observer": self.cleaned_data['observer'], "reducer": self.cleaned_data['reducer'], "spectypeid": self.cleaned_data['spectrum_type'], + "ascii_file": tns_filenames.get('ascii_file', ''), + "fits_file": tns_filenames.get('fits_file', ''), "remarks": self.cleaned_data['spectrum_remarks'], }, } @@ -248,11 +258,4 @@ def generate_tns_report(self): } } } - if tns_filenames: - report_data['classification_report']['0']['spectra']['spectra-group']['0']['ascii_file'] = \ - tns_filenames.get('ascii_file', '') - report_data['classification_report']['0']['spectra']['spectra-group']['0']['fits_file'] = \ - tns_filenames.get('fits_file', '') - else: - raise BadTnsRequest("No files were uploaded to TNS") return report_data diff --git a/tom_tns/templatetags/tns_extras.py b/tom_tns/templatetags/tns_extras.py index 8eaccb6..ee0163c 100644 --- a/tom_tns/templatetags/tns_extras.py +++ b/tom_tns/templatetags/tns_extras.py @@ -6,6 +6,8 @@ register = template.Library() + +# Define form Choices from Cached TNS Values TNS_FILTER_IDS = {name: fid for fid, name in get_tns_values('filters')} TNS_INSTRUMENT_IDS = {name: iid for iid, name in get_tns_values('instruments')} TNS_CLASSIFICATION_IDS = {name: cid for cid, name in get_tns_values('object_types')} @@ -13,6 +15,10 @@ @register.inclusion_tag('tom_tns/partials/tns_report_form.html', takes_context=True) def report_to_tns(context): + """ + Build context data for TNS AT Report Form. + Includes the latest Photometry data if available. + """ target = context['target'] initial = { 'ra': target.ra, @@ -39,6 +45,10 @@ def report_to_tns(context): @register.inclusion_tag('tom_tns/partials/tns_classify_form.html', takes_context=True) def classify_with_tns(context): + """ + Build context data for TNS Classification Form. + Includes the latest Photometry data if available. + """ target = context['target'] initial = { 'object_name': target.name.replace('AT', '').replace('SN', ''), diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index abc9a26..1cf4867 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -19,11 +19,13 @@ class BadTnsRequest(Exception): def get_tns_credentials(): """ Get the TNS credentials from settings.py. + This should include the bot_id, bot_name, api_key, tns_base_url, and possibly group_name. """ tns_info = settings.BROKERS['TNS'] - tns_info['marker'] = 'tns_marker' + json.dumps({'tns_id': tns_info['bot_id'], + # Build TNS Marker using Bot info + tns_info['marker'] = 'tns_marker' + json.dumps({'tns_id': tns_info.get('bot_id', None), 'type': 'bot', - 'name': tns_info['bot_name']}) + 'name': tns_info.get('bot_name', None)}) return tns_info @@ -43,19 +45,14 @@ def get_tns_values(option_list): return tuple_list -def get_reverse_tns_values(option_list, value=None): - """ Retrieve the reverse mapping of TNS options used to go from option to value. - I.e. reversed_tns_values['groups'] = { - 'group name 1': 1, - 'group name 2': 4, - 'group whatever': 129 - } +def get_reverse_tns_values(option_list, value): + """ + Retrieve the reverse mapping of TNS options. Used to go from option to value for a specific list of options. + Returns a tuple of the option value and the option label. """ reversed_tns_values = cache.get("reverse_tns_values", {}) if not reversed_tns_values: _, reversed_tns_values = populate_tns_values() - if not value: - return reversed_tns_values[option_list] try: return reversed_tns_values[option_list][value], value except KeyError: @@ -63,12 +60,13 @@ def get_reverse_tns_values(option_list, value=None): def populate_tns_values(): - """pull all the values from the TNS API""" + """pull all the values from the TNS API and Cache for an hour""" # Need to spoof a web based user agent or TNS will block the request :( SPOOF_USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:110.0) Gecko/20100101 Firefox/110.0.' - tns_base_url = get_tns_credentials()['tns_base_url'] + # Use sandbox URL if no url found in settings.py + tns_base_url = get_tns_credentials().get('tns_base_url', 'https://sandbox.wis-tns.org/api') all_tns_values = {} reversed_tns_values = {} try: @@ -85,6 +83,7 @@ def populate_tns_values(): def reverse_tns_values(all_tns_values): + """reverse the values from the TNS API""" reversed_tns_values = {} for key, values in all_tns_values.items(): if isinstance(values, list): @@ -96,7 +95,12 @@ def reverse_tns_values(all_tns_values): def build_file_dict(files): """ - Build a dictionary of files to upload to the TNS. + Build a dictionary of files to upload to the TNS as well as a dictionary connecting the uploaded name to the new + name returned from TNS. + TNS requires a specific format for the file upload: + https://www.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + file_Load: {files[0]: Filename, files[1]: Filename2, ...} + new_files: {ascii_file: <>, fits_file: <>, ...} """ new_files = {} file_load = {} @@ -121,15 +125,18 @@ def pre_upload_files_to_tns(files): file_load, new_files = build_file_dict(files) if not file_load: return None + # build request parameters tns_marker = tns_credentials['marker'] - json_data = {'api_key': tns_credentials['api_key']} + upload_data = {'api_key': tns_credentials['api_key']} response = requests.post(tns_credentials['tns_base_url'] + '/file-upload', headers={'User-Agent': tns_marker}, - data=json_data, files=file_load) + data=upload_data, files=file_load) response.raise_for_status() + # If successful, TNS returns a list of new filenames new_filenames = response.json().get('data', {}) logger.info(f"Uploaded {', '.join(new_filenames)} to the TNS") if not new_filenames: return None + # Return dictionary of updated TNS names for each uploaded file_type for file in new_files: try: new_files[file] = new_filenames[new_files[file]] @@ -142,6 +149,8 @@ def send_tns_report(data): """ Send a JSON bulk report to the Transient Name Server according to this manual: https://sandbox.wis-tns.org/sites/default/files/api/TNS_bulk_reports_manual.pdf + Returns a report ID if successful. + This ID can be used to retrieve the report from the TNS. """ tns_info = get_tns_credentials() json_data = {'api_key': tns_info['api_key'], 'data': data} @@ -157,6 +166,7 @@ def send_tns_report(data): def parse_object_from_tns_response(response_json, request): feedback_section = response_json['data']['feedback'] feedbacks = [] + iau_name = None if 'at_report' in feedback_section: feedbacks += feedback_section['at_report'] if 'classification_report' in feedback_section: @@ -180,9 +190,8 @@ def parse_object_from_tns_response(response_json, request): logger.info(log_message) messages.success(request, log_message) break - else: # this should never happen - iau_name = None - log_message = 'Problem getting response from TNS' + else: # If neither 'classification_report', nor 'at_report' were in the feedback section + log_message = 'No recognized feedback in the TNS response.' logger.error(log_message) messages.error(request, log_message) return iau_name @@ -197,6 +206,7 @@ def get_tns_report_reply(report_id, request): """ tns_info = get_tns_credentials() reply_data = {'api_key': tns_info['api_key'], 'report_id': report_id} + iau_name = None attempts = 0 # TNS Submissions return immediately with an id, which you must then check to see if the message # was processed, and if it was accepted or rejected. Here we check up to 10 times, waiting 1s @@ -218,4 +228,6 @@ def get_tns_report_reply(report_id, request): break else: raise BadTnsRequest(f"TNS submission failed with status code {response.status_code}") + if not iau_name: + raise BadTnsRequest(f"TNS submission failed to be processed within 10 seconds. The report_id = {report_id}") return iau_name diff --git a/tom_tns/views.py b/tom_tns/views.py index dd0bba3..82ea19e 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -15,6 +15,10 @@ class TNSFormView(PermissionListMixin, TemplateView): + """ + This view is used to display the TNS report forms. + The default form is the report form, but if the target name starts with AT, we switch to the classification form. + """ template_name = 'tom_tns/tns_report.html' def get_context_data(self, **kwargs): @@ -22,7 +26,7 @@ def get_context_data(self, **kwargs): target = Target.objects.get(pk=self.kwargs['pk']) context['target'] = target # We want to establish a default tab to display. - # by default, we start on report, but change to classify if the target name starts with AT + # by default, we start on report, but change to classify if the target name starts with AT. # If the target has an SN name, we warn the user that the target has likely been classified already. context['default_form'] = 'report' for name in target.names: @@ -35,6 +39,9 @@ def get_context_data(self, **kwargs): class TNSSubmitView(FormView): + """ + This View is used to submit the TNS report forms. + """ def get_success_url(self): return reverse_lazy('targets:detail', kwargs=self.kwargs) @@ -45,16 +52,23 @@ def form_invalid(self, form): return HttpResponseRedirect(self.get_success_url()) def form_valid(self, form): + """ + If the Form is successfully constructed, we generate the TNS report and submit it to the TNS. + """ try: + # Build TNS Report tns_report = form.generate_tns_report() + # Submit TNS Report report_id = send_tns_report(json.dumps(tns_report)) + # Get IAU name from Report Reply iau_name = get_tns_report_reply(report_id, self.request) - # update the target name if iau_name is not None: + # update the target name in Tom DB target = Target.objects.get(pk=self.kwargs['pk']) old_name = target.name target.name = iau_name target.save() + # Save old name as alias new_alias = TargetName(name=old_name, target=target) new_alias.save() except (requests.exceptions.HTTPError, BadTnsRequest) as e: From d5de401bb2c9dfbdda1cf53013bc449d7a76d224 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 15 Nov 2023 16:28:30 -0800 Subject: [PATCH 11/21] handle unconfigured TNS credentials --- tom_tns/templates/tom_tns/tns_report.html | 103 ++++++++++++---------- tom_tns/tns_report.py | 14 +-- tom_tns/views.py | 4 +- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html index 77019ab..578aced 100644 --- a/tom_tns/templates/tom_tns/tns_report.html +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -4,57 +4,64 @@ {% block additional_css %} {% endblock %} {% block content %}

Submit {{ target.name }} Information to the TNS

-{% if default_form == 'supernova' %} -
Warning: This target {{target.names}} may have already been reported to and classified with the TNS
-{% endif %} -
- -
- -
- {% if default_form == 'classify' %} -
-
Warning: This target {{target.names}} may have already been reported to the TNS
- {% else %} -
- {% endif %} -
-
-

Report {{ target.name }} to the TNS

-
- {% report_to_tns %} -
+{% if not tns_configured %} +
+ TNS Credentials have not been configured! + See the TOM_TNS README for information on how to configure.
- {% if default_form == 'classify' %} -
- {% else %} -
- {% if default_form != 'supernova' %} -
Warning: This target {{target.names}} may not have been reported to the TNS yet
- {% endif %} +{% else %} + {% if default_form == 'supernova' %} +
Warning: This target {{target.names}} may have already been reported to and classified with the TNS
{% endif %} -
-
-

Classify {{ target.name }} with the TNS

-
- {% classify_with_tns %} +
+ +
+ +
+ {% if default_form == 'classify' %} +
+
Warning: This target {{target.names}} may have already been reported to the TNS
+ {% else %} +
+ {% endif %} +
+
+

Report {{ target.name }} to the TNS

+
+ {% report_to_tns %} +
+
+ {% if default_form == 'classify' %} +
+ {% else %} +
+ {% if default_form != 'supernova' %} +
Warning: This target {{target.names}} may not have been reported to the TNS yet
+ {% endif %} + {% endif %} +
+
+

Classify {{ target.name }} with the TNS

+
+ {% classify_with_tns %} +
-
+{% endif %} {% endblock %} \ No newline at end of file diff --git a/tom_tns/tns_report.py b/tom_tns/tns_report.py index 1cf4867..7d91bf4 100644 --- a/tom_tns/tns_report.py +++ b/tom_tns/tns_report.py @@ -21,11 +21,15 @@ def get_tns_credentials(): Get the TNS credentials from settings.py. This should include the bot_id, bot_name, api_key, tns_base_url, and possibly group_name. """ - tns_info = settings.BROKERS['TNS'] - # Build TNS Marker using Bot info - tns_info['marker'] = 'tns_marker' + json.dumps({'tns_id': tns_info.get('bot_id', None), - 'type': 'bot', - 'name': tns_info.get('bot_name', None)}) + try: + tns_info = settings.BROKERS['TNS'] + # Build TNS Marker using Bot info + tns_info['marker'] = 'tns_marker' + json.dumps({'tns_id': tns_info.get('bot_id', None), + 'type': 'bot', + 'name': tns_info.get('bot_name', None)}) + except (KeyError, AttributeError): + logger.error("TNS credentials not found in settings.py") + tns_info = {} return tns_info diff --git a/tom_tns/views.py b/tom_tns/views.py index 82ea19e..83171c2 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -7,8 +7,7 @@ from django.contrib import messages from guardian.mixins import PermissionListMixin -from tom_tns.tns_report import send_tns_report, get_tns_report_reply,\ - BadTnsRequest +from tom_tns.tns_report import send_tns_report, get_tns_report_reply, get_tns_credentials, BadTnsRequest from tom_targets.models import Target, TargetName import json @@ -24,6 +23,7 @@ class TNSFormView(PermissionListMixin, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) target = Target.objects.get(pk=self.kwargs['pk']) + context['tns_configured'] = bool(get_tns_credentials()) context['target'] = target # We want to establish a default tab to display. # by default, we start on report, but change to classify if the target name starts with AT. From 56ae46963f877dab9965e0268f1b92ce76cdd955 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Wed, 15 Nov 2023 16:49:17 -0800 Subject: [PATCH 12/21] update readme --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 49f0c74..3d6d884 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,27 @@ TOMtoolkit module for reporting transients to the TNS ] ``` - Also add your TNS credentials to your `settins.py` if they don't already exist for the TNS Broker. +3. Add your TNS credentials to your `settings.py` if they don't already exist for the TNS Broker. + + If you don't have access to a TNS Bot for your TOM, you can make one from the [TNS website](https://www.wis-tns.org/bots). + + NOTE: If you are testing on the sandbox, the sandbox is only synced every Sunday, so new bots created using the above link +won't show up until after the next update. -3. Include the tom_nonlocalizedevent URLconf in your project `urls.py`: + ```python + BROKERS = { + ... + 'TNS': { + 'bot_id': os.getenv('TNS_BOT_ID', ''), # This is the BOT ID you plan to use to submit to TNS + 'bot_name': os.getenv('TNS_BOT_NAME', ''), # This is the BOT name associated with the above ID + 'api_key': os.getenv('TNS_API_KEY', ''), # This is the API key for the associated BOT + 'tns_base_url': 'https://sandbox.wis-tns.org/api', # This is the sandbox URL. Use https://www.wis-tns.org/api for live submission. + 'group_name': os.getenv('TNS_GROUP_NAME', ''), # Optional. Include if you wish to use an affiliated Group Name. + }, + } + ``` + +5. Include the tom_nonlocalizedevent URLconf in your project `urls.py`: ```python urlpatterns = [ ... From d3aa46154910cf91a53fbaaacc1bb4e9b9b5d248 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Thu, 16 Nov 2023 17:08:36 -0800 Subject: [PATCH 13/21] add integration points to app meta data. --- tom_tns/apps.py | 6 ++++++ tom_tns/templates/tom_tns/partials/tns_button.html | 1 + tom_tns_base/urls.py | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tom_tns/templates/tom_tns/partials/tns_button.html diff --git a/tom_tns/apps.py b/tom_tns/apps.py index 83bd424..6bce368 100644 --- a/tom_tns/apps.py +++ b/tom_tns/apps.py @@ -4,3 +4,9 @@ class TomTnsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'tom_tns' + integration_points = {'target_detail_button': {'namespace': 'tns:report-tns', + 'title': 'TNS', + 'class': 'btn btn-info', + 'text': 'TNS', + } + } diff --git a/tom_tns/templates/tom_tns/partials/tns_button.html b/tom_tns/templates/tom_tns/partials/tns_button.html new file mode 100644 index 0000000..1aa2dfc --- /dev/null +++ b/tom_tns/templates/tom_tns/partials/tns_button.html @@ -0,0 +1 @@ + TNS \ No newline at end of file diff --git a/tom_tns_base/urls.py b/tom_tns_base/urls.py index d1e8eb0..500e3fa 100644 --- a/tom_tns_base/urls.py +++ b/tom_tns_base/urls.py @@ -17,7 +17,6 @@ from django.urls import include, path urlpatterns = [ - # path('', include('tom_common.urls')), path('tns/', include('tom_tns.urls')), path('', include('tom_common.urls')), ] From a159dcd2ff9f136f5de397b78df8ebb62d02e2f5 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Sun, 26 Nov 2023 15:15:26 -0800 Subject: [PATCH 14/21] fix typo --- README.md | 2 +- tom_tns/__pycache__/apps.cpython-310.pyc | Bin 421 -> 560 bytes tom_tns/__pycache__/forms.cpython-310.pyc | Bin 6745 -> 6981 bytes tom_tns/__pycache__/urls.cpython-310.pyc | Bin 335 -> 555 bytes tom_tns/__pycache__/views.cpython-310.pyc | Bin 2910 -> 3036 bytes 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d6d884..305e053 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ won't show up until after the next update. } ``` -5. Include the tom_nonlocalizedevent URLconf in your project `urls.py`: +5. Include the tom_tns URLconf in your project `urls.py`: ```python urlpatterns = [ ... diff --git a/tom_tns/__pycache__/apps.cpython-310.pyc b/tom_tns/__pycache__/apps.cpython-310.pyc index 8e4ef559766f29f7b442722e0df5146aaf727b4e..f1073ccb8bc62c3e38f6ad28c77d14d3bc0b61f3 100644 GIT binary patch delta 257 zcmZ3=yn%%;pO=@50SMIggr%}EP2@{8WdrgYfVfxMhoi%#s`+m7J4U zTzreAB(OuXrUx5kJtOMf{V+7_CJFfJ_bs c7DfqMJ3@nTsjFX=+DriAuz|tUs8%W&Z Wu*uC&Da}c>16fqe0wj1CM3@0KSr|_M diff --git a/tom_tns/__pycache__/forms.cpython-310.pyc b/tom_tns/__pycache__/forms.cpython-310.pyc index 9dcec6df6b488d5d98bfe350ff34b93a2a03d8b1..1120bb59b7d35a06366700e8eb2ffabdaed89e7d 100644 GIT binary patch literal 6981 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 literal 6745 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 c-U6Dd`8DU%Tt{X=)8SF9&Q9623}#iY z$@nE^p|5IBxwDw5i}NE!Ys0o0R23x2T*%Ec7wu zLp^vLNqH{b5bx?-9lkE2O~GiiU2YOdvn0)@9tIvJ7A65^ Gj{gAnJSX@7 diff --git a/tom_tns/__pycache__/views.cpython-310.pyc b/tom_tns/__pycache__/views.cpython-310.pyc index ad3b08c2940ab4dfdcd63b1acd27cebcdd387b82..ce323032b8c958b10cb386fe1f4a7277536c2339 100644 GIT binary patch literal 3036 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 JOS|;0`)|}5;*S6T From 7b844b8dd110d7190b9a421f7d15f8c0870a9955 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 28 Nov 2023 13:40:06 -0700 Subject: [PATCH 15/21] update readme add gitignore --- .gitignore | 121 ++++++++++++++++++++++ README.md | 2 +- tom_tns/templates/tom_tns/tns_button.html | 10 ++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 tom_tns/templates/tom_tns/tns_button.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4134850 --- /dev/null +++ b/.gitignore @@ -0,0 +1,121 @@ +# Default ignored files +/shelf/ +/workspace.xml + +# Scratch files +scratch/ + +# Media files +media/ +data/ + +# Cache +tmp/ + +# Development database +*.sqlite3 + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +*local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# emacs +*~ + +# Linting +.flake8 + +# VS Code +.vscode/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +.venv/ +.venv +venv/ +env/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/README.md b/README.md index 305e053..ae89011 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ TOMtoolkit module for reporting transients to the TNS ```python INSTALLED_APPS = [ ... - 'tom_nonlocalizedevents', + 'tom_tns', ] ``` diff --git a/tom_tns/templates/tom_tns/tns_button.html b/tom_tns/templates/tom_tns/tns_button.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/tom_tns/templates/tom_tns/tns_button.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file From ed434dde3c26bc3635f8cfea86de8414de642dda Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 28 Nov 2023 13:43:44 -0700 Subject: [PATCH 16/21] remove pycache --- tom_tns/__pycache__/__init__.cpython-310.pyc | Bin 141 -> 0 bytes tom_tns/__pycache__/admin.cpython-310.pyc | Bin 182 -> 0 bytes tom_tns/__pycache__/apps.cpython-310.pyc | Bin 560 -> 0 bytes tom_tns/__pycache__/forms.cpython-310.pyc | Bin 6981 -> 0 bytes tom_tns/__pycache__/models.cpython-310.pyc | Bin 179 -> 0 bytes tom_tns/__pycache__/urls.cpython-310.pyc | Bin 555 -> 0 bytes tom_tns/__pycache__/views.cpython-310.pyc | Bin 3036 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 152 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 146 -> 0 bytes .../__pycache__/settings.cpython-310.pyc | Bin 4767 -> 0 bytes tom_tns_base/__pycache__/urls.cpython-310.pyc | Bin 949 -> 0 bytes tom_tns_base/__pycache__/wsgi.cpython-310.pyc | Bin 559 -> 0 bytes 12 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tom_tns/__pycache__/__init__.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/admin.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/apps.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/forms.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/models.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/urls.cpython-310.pyc delete mode 100644 tom_tns/__pycache__/views.cpython-310.pyc delete mode 100644 tom_tns/migrations/__pycache__/__init__.cpython-310.pyc delete mode 100644 tom_tns_base/__pycache__/__init__.cpython-310.pyc delete mode 100644 tom_tns_base/__pycache__/settings.cpython-310.pyc delete mode 100644 tom_tns_base/__pycache__/urls.cpython-310.pyc delete mode 100644 tom_tns_base/__pycache__/wsgi.cpython-310.pyc diff --git a/tom_tns/__pycache__/__init__.cpython-310.pyc b/tom_tns/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 77c6e9c285d35d661fc7fbbf48c1c0775de1b6db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmd1j<>g`kf-4n{sUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6v2KO;XkRX-~^ zBe5hkCowZmKRvTVza&35z9g?0PRGY*=4F<|$LkeT-r}&y%}*)KNwou+RLleg`kf-4n{srEqnF^Gc(44TX@fuanW zjJH@5Q*tx&{4|-O_)@YG^V0M6lJoOQiZYXmKnAR2C}IXuVB(jqenx(7s(w~-Mq){7 tPGV-BetKq!eo20Ad`Vt0oCcexS5SG2!zMRBr8FnijuB`=F~~Fl1^|=KDwhBN diff --git a/tom_tns/__pycache__/apps.cpython-310.pyc b/tom_tns/__pycache__/apps.cpython-310.pyc deleted file mode 100644 index f1073ccb8bc62c3e38f6ad28c77d14d3bc0b61f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 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 diff --git a/tom_tns/__pycache__/models.cpython-310.pyc b/tom_tns/__pycache__/models.cpython-310.pyc deleted file mode 100644 index 0269da6d1480942c3e7c98accca832950ab3d0ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmd1j<>g`kf-4n{sg^+cF^Gc(44TX@fuanW zjJMcw^HWlDiv2X1ZgHk$CFZ5)>!l`8DU%Tt{X=)8SF9&Q9623}#iY z$@nE^p|5IBxwDw5i}NE!Ys0o0R23x2T*%Ec7wu zLp^vLNqH{b5bx?-9lkE2O~GiiU2YOdvn0)Z3Y6u}1HXeuwNXZIqxP&0VDB5v|(h_R*bd|^L^u=3M zz231rDAKIRQ}_c2$s-}8edUSYQm;Jm4^Sio=T!G(Jj;r<`qZi8x}Ni$?{wMkcL+TH zia+E(?h*12>})?SbRNPh{|bT;Ml%vpXsyi3?a-!Zvoj}mL)Yj|=H;!hWpp?5^LE%a zx|enGZrC+?E9>R`uy1rf8{|9TPQDxN=C{II`ChnZ?CoqnABMyHc6i(LX?7>P^Gm`y ztow|xF7NZ!g%jQdOON%z(&z4l9o}Q~n2ZMhgtL;P5!F4xpKu|0l*Owv==<-LBL8j5 zm-yd==Zh>>yy*|$S8DNy%SBm8{)n?w@I>L9oJ$#>atZqW2VCT-lxbP~I+f}->C?0r zIeJI(f<>y3k>HC`C_OyoYO9at>`d=pSrfq-FH{<5Quluvvq>Qz@y8XHYGi3|5(^lp zJ9T>$=RCptY(I9>k>3J^n}~!KqoK{L(D{r^DYKdL%n#kqh(kgTeB$cvfyDc$1{~@3EgrH;?;q-3$OeG2t_W)oU(38*7Ps*kM!J9*4&;uYch8kJpd$sPS0)S zsn!L#plgfS56Iev)_Fjf3$2I!x&L$WyRV)*YiHBGB%2OvT~hHT^PB&#tZlG9WS!bd z$=W(5k6%MM>K$oo@yT;4cHm6m0I!gg#WX#w01MiyU8KF*g<-F8Z-idg*1MC?Z~Sr= zTE|CWTUHA$itq#9ksL{MR=qr(w!*EOIBs5_oC==6=%&qttet;0_MJIlHbfRr_lZ8CJE27<~HU^ z;u~-@@hXY|h+TN455gx7b)2DPtpAgu7L0g^L%##!W{NWV83`TcFc(^vd8`Gk$9#ZE zTle9b$JI%mD&(crJJ*Sd{q01RX4L=5(;JC_NrH*-GdvUCLjjpX5>QA01gL$q8q_G1 zZ!@mqG#gP9_mL&=F11d@*HOnv*8<(5cQ72XN)lkos1n&2j1nge=;~`X?qdSCH8R0b zR}bnQyyl4C21St#9ub&rP3QK;xg>ugf3_|yN|d`M8}AY_7LINmtsYK*I;LfomCLj^ z4Y&}c2$r!7c#)J9WCUbcu$=KCsJ*6eRKqxMz_)`@OW@YpHB)s<)O@Tx)0esx%V;jk zLLkA2?|}fnpf9w8Ir^eUdT5N&;z^vP><2K+Af|)Z{!hEiiW-mn z9hB8uYq}wq1XzqJYXdxWNiUGU9N;WqvqvB%oK-HUT^_S@*>RRf;FKGQneMyx>n#WM}}$zrYnRhfW{El>Mr;mN|mZa0hox3 z8Z3Z-+aVO_dbh3GEil!!gR$2xtMY{u7!~mb3glOT^d;`0K$ty>rSi{bB(F@ykm55Rn`e|I%6lO@- zFEeJkx?S*P1mKIMru;>8b`0PQHh%<*%Ws0P9N!vPJg`kf-4n{sUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vSKO;XkRX-~^ zBe5hkCowZmKRvTVza&35z9g?0PUmK(7bTWt<^zS|<1_OzOXB183My}L*yQG?l;)(` MfebBX0un3?0GmrAxBvhE diff --git a/tom_tns_base/__pycache__/__init__.cpython-310.pyc b/tom_tns_base/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 9c3b8e607d3ab3b1079b89b2c41970c4c969f23f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmd1j<>g`kg7y2HQbF`%5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!Hienx(7s(w~- zMq){7PGV-BetKq!eo20Ad`Vt0jE+xAEKb#rkI&4@EQycTE2zB1VUwGmQks)$2Qsdh J2}rOo006+TAz}ak diff --git a/tom_tns_base/__pycache__/settings.cpython-310.pyc b/tom_tns_base/__pycache__/settings.cpython-310.pyc deleted file mode 100644 index 49ecc2a49cebcfdc3b9430a34d54de7b6fa5d92b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4767 zcmb7H-Co;97Pn<#8}ktYB%x3eCnX;yK@MLD$#&ZaTL%AP%Z?>Tn2YKsJcAL)lBp^DGz!lcjFT`P$HV9nX0tMzv3-~d*4Z7Wd{$0F* zpWvtXFK01y4}Xg?_^)UYe~0eF`~&h0JE;$GU_zj)jAu(yN4uw&^FDq zX;3$9ADr(>w=gV33@3e}n{7h>pS2uv(6`VV=IxoN&sTN#Q1&)EW-u&D>TQQC-C4N3 zPL2+4uHpsj2k+EcSZJ>w|FCjr`XD0GORoAH%pqsH+_~dXv1ionvq2!?B=)FfL2tFd-oat|2}}<3w(0i5AMc+V zk^X2xps#u?PiHs4fpU|{`)VL4+Ey!(eWJM*_Q&y?Iv-I z`o{=E#9C@oKenUhyad!k2%M}p-+={7Hf>ByDr_1J2rRo`5EG-MQp`&=Ni2D@-MSa% zA0n7_TWFZ}zHa&vuzsp#JCLR{@P9v{w+!u=oSq9UuRal0R-Oo}Yr^vS(~Y&YRd22f zxV*9AaI2<1TcAz zf9Z4l)YctAsfe{6d**ZOrYn@xE^8+4-C@jO0T%WhsNfD2*sx9nu|#S)X)_>t_b%eI zpq|72L|GMUlG5S4bpdBXH*{eOnhwuJtC~914M!*iuMXeB1z6xrZC6lO+u;hX$OC(T z8?&Iw9i31Nx`Ctn6Nas?erVgrRDcb_JwRSB}?(aln35R%aFCOsN5R*AEb1!y>36a|^)7Eh&23MSc_0Y_x z;NcVL6F3F%P579I1Wkr}^Be15f#%ILG(LYsZOh9JT;N)I!?42oP=b=+Rt^r}K40xo z-(Zl<@MtzK6~vvAs#T?0xu_^m-?UswR1`@;bAefF(u*Bt-Y#RIQY(upn(2PgCLdkQEc*#fgD(zE^Ajz&*PF(^!!gY{oBByDNEmDmc3_()#E-YY z3oREVH$_EamDf)xQm!Vc+H+~oAIeLcJ6rykSSnRsN_lO&qNs{LUX&FTNI-yct)S^# zt*BJ@z%k0zlBh~r6>2(~?@+t4AIvI#s$9(HOVUelet)!9si@jc4dPrb_`_XJ#h-Yo zY!%rerD87FwLhE}Rgvkc_%q^;x(#b7%F9YEukDJZB1~4`7BN;5<*glNm|P_<0bsE# zX{Z7vKe+?DEG}=zew_7G1lv^sImcG_C%a|_&uK9L{N{E*sN8aOCXWj7HhlU z0?_y45I)2@ahU6&QR$Uh6GN>OG!|@L0g5VZ?V)QOEzccG38E4WSE`cCHm9JW?Mmf2 z=ooBH-QFp0%3=}dlfAxH7GDLvgsz^`x;9}xhQ_vwvZ`p3EN;T_gR(HxO46=WLgSFd zRIN~hI~APAb2M(+btv=Q4_kkv*kqMoava+aXc60 zqDhW>&;QB4=O?%X7mY{xchDaL8sGQ)!B6qP4!<}*#K)K<|Bj0X?NHG2T0X$#`NnNfq0X?EuqlzlLAR zmKDDMLgLy@qzVZMEG6;d=G=47Jyxfu5rVP!dCgyr5c=j8$72GE7x0)%crmm_EMQ?B zh;SW>(RvghK4K#tvhhCHhcKVO{E?d%;d;!b?@=qAFhCjj6Mw4sXOrR^z!6Gw zTuFcw2k@rm4$^SGiZG1Q+QzI1VJbH%+frGmAziKn;gVdY7sRTj$_;VB1kxK4z4}50 zV3}2P)5_fQ_Gvxnd5W(X!&Kr*G)mhQ#v82!77rP1ntt;@)5l-*@vZ__z)^+pD->HU z;&|EDRVu-kSW>~C#cm!a(REFYdE&HUN2q^ep=`;uBa(W?1D)4^^*?M}d`eB`Ko-d# zjBaIKx2%!{h~V?N(p;MdeQv!l7^pydTr6KCc+*R{I>>csN9qJ0NcOQyFUmi&y{}b4 zK)@kW>^3}ZoFvhAa4<^3ZrUGrW2g9T4E`g#dGACY-J#uMw+3ejCH>ahWYSHu4D6F- z-N_MS4dzA~t`6!>j{5l(>6trluOH{cHIQucl3HHVN|K_oU^egl;p^{!cU{^%bhD>I Xv9{*V0{}4Yj*ijU*?c??X1{&|FFYut diff --git a/tom_tns_base/__pycache__/wsgi.cpython-310.pyc b/tom_tns_base/__pycache__/wsgi.cpython-310.pyc deleted file mode 100644 index f7485d448f9969beb93555b7277d236eb221a137..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559 zcmYjP!EO^V5cN7~Qx=MPfS!Gg1ZzN?5JD8SL_v@qQiPO?z!2gjcm z&-fBA19Anm!7B?c@JnB5p8rWZA(LSiA5;)TdndMJ#KYrvN0ZU1n4X-SjVGh2_;7ms z@%_p5!|fFm9zxKr^20$K2$6IM5h?n3uu)T#X^5i5ZfIhQ18{5Ipo@8GRIrd2Cd9j2 zwBJe7K56IvK26ds@Kw2#0gTj+S32!kj From acffffb3328aced16ef7303d5b6ffa07aec30760 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 28 Nov 2023 13:50:40 -0700 Subject: [PATCH 17/21] add endlines --- tom_tns/templates/tom_tns/partials/tns_button.html | 2 +- .../templates/tom_tns/partials/tns_classify_form.html | 2 +- .../templates/tom_tns/partials/tns_report_form.html | 2 +- tom_tns/templates/tom_tns/tns_button.html | 10 ---------- tom_tns/templates/tom_tns/tns_report.html | 2 +- tom_tns_base/settings.py | 2 +- 6 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 tom_tns/templates/tom_tns/tns_button.html diff --git a/tom_tns/templates/tom_tns/partials/tns_button.html b/tom_tns/templates/tom_tns/partials/tns_button.html index 1aa2dfc..a2fcb7a 100644 --- a/tom_tns/templates/tom_tns/partials/tns_button.html +++ b/tom_tns/templates/tom_tns/partials/tns_button.html @@ -1 +1 @@ - TNS \ No newline at end of file + TNS diff --git a/tom_tns/templates/tom_tns/partials/tns_classify_form.html b/tom_tns/templates/tom_tns/partials/tns_classify_form.html index dec5794..53becd9 100644 --- a/tom_tns/templates/tom_tns/partials/tns_classify_form.html +++ b/tom_tns/templates/tom_tns/partials/tns_classify_form.html @@ -3,4 +3,4 @@
{% csrf_token %} {% crispy form %} -
\ No newline at end of file + diff --git a/tom_tns/templates/tom_tns/partials/tns_report_form.html b/tom_tns/templates/tom_tns/partials/tns_report_form.html index aa8e027..3867ee7 100644 --- a/tom_tns/templates/tom_tns/partials/tns_report_form.html +++ b/tom_tns/templates/tom_tns/partials/tns_report_form.html @@ -3,4 +3,4 @@
{% csrf_token %} {% crispy form %} -
\ No newline at end of file + diff --git a/tom_tns/templates/tom_tns/tns_button.html b/tom_tns/templates/tom_tns/tns_button.html deleted file mode 100644 index 566549b..0000000 --- a/tom_tns/templates/tom_tns/tns_button.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Title - - - - - \ No newline at end of file diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html index 578aced..d972d91 100644 --- a/tom_tns/templates/tom_tns/tns_report.html +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -64,4 +64,4 @@

Classify {{ target.name }} with the TNS

{% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/tom_tns_base/settings.py b/tom_tns_base/settings.py index 1f9b8dd..6d1cc9b 100644 --- a/tom_tns_base/settings.py +++ b/tom_tns_base/settings.py @@ -271,4 +271,4 @@ try: from local_settings import * # noqa except ImportError: - pass \ No newline at end of file + pass From a98fac962196c9c92647046a062af7c528664e02 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 28 Nov 2023 14:02:00 -0700 Subject: [PATCH 18/21] move to tns_api --- tom_tns/forms.py | 3 +-- tom_tns/templatetags/tns_extras.py | 2 +- tom_tns/{tns_report.py => tns_api.py} | 0 tom_tns/views.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) rename tom_tns/{tns_report.py => tns_api.py} (100%) diff --git a/tom_tns/forms.py b/tom_tns/forms.py index 2ba5fbb..c9fdf94 100644 --- a/tom_tns/forms.py +++ b/tom_tns/forms.py @@ -3,8 +3,7 @@ from django import forms from django.conf import settings -from tom_tns.tns_report import get_tns_values, get_tns_credentials, get_reverse_tns_values, pre_upload_files_to_tns,\ - BadTnsRequest +from tom_tns.tns_api import get_tns_values, get_tns_credentials, get_reverse_tns_values, pre_upload_files_to_tns from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Row, Column, Submit, HTML diff --git a/tom_tns/templatetags/tns_extras.py b/tom_tns/templatetags/tns_extras.py index ee0163c..8296203 100644 --- a/tom_tns/templatetags/tns_extras.py +++ b/tom_tns/templatetags/tns_extras.py @@ -1,7 +1,7 @@ from django import template from django.conf import settings -from tom_tns.tns_report import get_tns_values +from tom_tns.tns_api import get_tns_values from tom_tns.forms import TNSReportForm, TNSClassifyForm register = template.Library() diff --git a/tom_tns/tns_report.py b/tom_tns/tns_api.py similarity index 100% rename from tom_tns/tns_report.py rename to tom_tns/tns_api.py diff --git a/tom_tns/views.py b/tom_tns/views.py index 83171c2..ac7a673 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -7,7 +7,7 @@ from django.contrib import messages from guardian.mixins import PermissionListMixin -from tom_tns.tns_report import send_tns_report, get_tns_report_reply, get_tns_credentials, BadTnsRequest +from tom_tns.tns_api import send_tns_report, get_tns_report_reply, get_tns_credentials, BadTnsRequest from tom_targets.models import Target, TargetName import json From 6346210bbf2d97c6dd50b79c9b72ac60de096978 Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Tue, 28 Nov 2023 14:20:22 -0700 Subject: [PATCH 19/21] add dynamic versioning to poetry --- pyproject.toml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6889f7c..4a509d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,26 @@ readme = "README.md" packages = [{include = "tom_tns"}] [tool.poetry.dependencies] -python = "^3.8.1" +python = "^3.8.1,<3.12" tomtoolkit = "^2.14" +[tool.poetry-dynamic-versioning] +enable = true +vcs = "git" +style = "pep440" +# the default pattern regex makes the 'v' manditory +# this pattern modifies the default regex in order to make the 'v' optional +# ('v' becomes '[v]?' meaning a single v, [v], and ? means optional) +pattern = "(?x)^[v]?((?P\\d+)!)?(?P\\d+(\\.\\d+)*)([-._]?((?P[a-zA-Z]+)[-._]?(?P\\d+)?))?(\\+(?P.+))?$" + +# substitute version not only in pyproject.toml (which the config block above does) +# but also the __version__.py file +[tool.poetry-dynamic-versioning.substitution] + + [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"] +build-backend = "poetry_dynamic_versioning.backend" +# poetry_dynamic_versioning.backend is a thin wrapper around the standard build-backend = "poetry.core.masonry.api" + From d9bbafc913bca6d11f9c6c70de778ecf5248d0e7 Mon Sep 17 00:00:00 2001 From: Joey Chatelain <32683393+jchate6@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:08:09 -0800 Subject: [PATCH 20/21] Update __init__.py to include version --- tom_tns/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tom_tns/__init__.py b/tom_tns/__init__.py index e69de29..198ad6d 100644 --- a/tom_tns/__init__.py +++ b/tom_tns/__init__.py @@ -0,0 +1,2 @@ +# this is a placeholder for poetry-dynamic-versioning (see pyproject.toml) +__version__ = "0.0.0" From 608d450acace7aed7618323ae899723364a48f8a Mon Sep 17 00:00:00 2001 From: Joseph Chatelain Date: Thu, 14 Dec 2023 10:34:49 -0800 Subject: [PATCH 21/21] add versioning to form pages --- tom_tns/templates/tom_tns/tns_report.html | 2 ++ tom_tns/views.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tom_tns/templates/tom_tns/tns_report.html b/tom_tns/templates/tom_tns/tns_report.html index d972d91..b487c23 100644 --- a/tom_tns/templates/tom_tns/tns_report.html +++ b/tom_tns/templates/tom_tns/tns_report.html @@ -63,5 +63,7 @@

Classify {{ target.name }} with the TNS

{% endif %} +
+

TOM Toolkit Module (tom_tns) version {{ version }}

{% endblock %} diff --git a/tom_tns/views.py b/tom_tns/views.py index ac7a673..7bc891c 100644 --- a/tom_tns/views.py +++ b/tom_tns/views.py @@ -7,6 +7,7 @@ from django.contrib import messages from guardian.mixins import PermissionListMixin +from tom_tns import __version__ from tom_tns.tns_api import send_tns_report, get_tns_report_reply, get_tns_credentials, BadTnsRequest from tom_targets.models import Target, TargetName @@ -25,6 +26,7 @@ def get_context_data(self, **kwargs): target = Target.objects.get(pk=self.kwargs['pk']) context['tns_configured'] = bool(get_tns_credentials()) context['target'] = target + context['version'] = __version__ # from tom_tns.__init__.py # We want to establish a default tab to display. # by default, we start on report, but change to classify if the target name starts with AT. # If the target has an SN name, we warn the user that the target has likely been classified already.