diff --git a/poetry.lock b/poetry.lock index cbd9269d..42513456 100644 --- a/poetry.lock +++ b/poetry.lock @@ -59,6 +59,45 @@ six = "*" [package.extras] test = ["astroid", "pytest"] +[[package]] +name = "azure-core" +version = "1.29.1" +description = "Microsoft Azure Core Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-core-1.29.1.zip", hash = "sha256:68e5bb6e3a3230ec202001cc5cb88e57f11c441c8345e921a9ffb8c370abf936"}, + {file = "azure_core-1.29.1-py3-none-any.whl", hash = "sha256:6bcefa1f70ff7bf3c39c07c73d8a21df73288eff7e6a1031eb8cfae71cc7bed4"}, +] + +[package.dependencies] +requests = ">=2.18.4" +six = ">=1.11.0" +typing-extensions = ">=4.3.0" + +[package.extras] +aio = ["aiohttp (>=3.0)"] + +[[package]] +name = "azure-storage-blob" +version = "12.17.0" +description = "Microsoft Azure Blob Storage Client Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-storage-blob-12.17.0.zip", hash = "sha256:c14b785a17050b30fc326a315bdae6bc4a078855f4f94a4c303ad74a48dc8c63"}, + {file = "azure_storage_blob-12.17.0-py3-none-any.whl", hash = "sha256:0016e0c549a80282d7b4920c03f2f4ba35c53e6e3c7dbcd2a4a8c8eb3882c1e7"}, +] + +[package.dependencies] +azure-core = ">=1.28.0,<2.0.0" +cryptography = ">=2.1.4" +isodate = ">=0.6.1" +typing-extensions = ">=4.3.0" + +[package.extras] +aio = ["azure-core[aio] (>=1.28.0,<2.0.0)"] + [[package]] name = "backcall" version = "0.2.0" @@ -745,6 +784,29 @@ files = [ [package.dependencies] django = ">=3.2" +[[package]] +name = "django-storages" +version = "1.13.2" +description = "Support for many storage backends in Django" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-storages-1.13.2.tar.gz", hash = "sha256:cbadd15c909ceb7247d4ffc503f12a9bec36999df8d0bef7c31e57177d512688"}, + {file = "django_storages-1.13.2-py3-none-any.whl", hash = "sha256:31dc5a992520be571908c4c40d55d292660ece3a55b8141462b4e719aa38eab3"}, +] + +[package.dependencies] +azure-storage-blob = {version = ">=12.0.0", optional = true, markers = "extra == \"azure\""} +Django = ">=3.2" + +[package.extras] +azure = ["azure-storage-blob (>=12.0.0)"] +boto3 = ["boto3 (>=1.4.4)"] +dropbox = ["dropbox (>=7.2.1)"] +google = ["google-cloud-storage (>=1.27.0)"] +libcloud = ["apache-libcloud"] +sftp = ["paramiko (>=1.10.0)"] + [[package]] name = "django-taggit" version = "4.0.0" @@ -969,6 +1031,20 @@ qtconsole = ["qtconsole"] test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +optional = false +python-versions = "*" +files = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "jedi" version = "0.19.0" @@ -1843,4 +1919,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.11" -content-hash = "a9e9f0815103df6a96b60d613a3e690feeba6b5b2052b33d97377ef5fab10246" +content-hash = "542823902019e1ecc8f609f154b04c5b5e7a36e0a027b448a129e0ec8919a25f" diff --git a/prs2/referral/models.py b/prs2/referral/models.py index ed0ae919..38e397ae 100644 --- a/prs2/referral/models.py +++ b/prs2/referral/models.py @@ -1,3 +1,4 @@ +from azure.core.exceptions import ResourceNotFoundError from copy import copy from datetime import date from dateutil.parser import parse @@ -5,7 +6,6 @@ from django.contrib.auth.signals import user_logged_in from django.contrib.gis.db import models from django.contrib.gis.geos import GeometryCollection -from django.core.exceptions import SuspiciousFileOperation from django.core.mail import EmailMultiAlternatives from django.core.validators import MaxLengthValidator from django.db.models import Q @@ -1140,19 +1140,16 @@ def filename(self): @property def extension(self): - try: # Account for SuspiciousFileOperation exceptions. - if self.uploaded_file and os.path.exists(self.uploaded_file.path): - ext = os.path.splitext(self.uploaded_file.name)[1] - return ext.replace(".", "").upper() - else: - return "" - except SuspiciousFileOperation: + if self.uploaded_file: + ext = os.path.splitext(self.uploaded_file.name)[1] + return ext.replace(".", "").upper() + else: return "" @property def filesize_str(self): - try: # Account for SuspiciousFileOperation exceptions. - if self.uploaded_file and os.path.exists(self.uploaded_file.path): + try: + if self.uploaded_file: num = self.uploaded_file.size for x in ["b", "Kb", "Mb", "Gb"]: if num < 1024.0: @@ -1160,7 +1157,7 @@ def filesize_str(self): num /= 1024.0 else: return "" - except SuspiciousFileOperation: + except ResourceNotFoundError: return "" def as_row(self): diff --git a/prs2/settings.py b/prs2/settings.py index ec0c4598..ec29d0f6 100644 --- a/prs2/settings.py +++ b/prs2/settings.py @@ -28,10 +28,14 @@ INTERNAL_IPS = ['127.0.0.1', '::1'] ROOT_URLCONF = 'prs2.urls' WSGI_APPLICATION = 'prs2.wsgi.application' -# Allow overriding the Django default for FILE_UPLOAD_PERMISSIONS (0o644). -# Required for non-local Azure storage volumes in Kubernetes environment. -FILE_UPLOAD_PERMISSIONS = env('FILE_UPLOAD_PERMISSIONS', None) +# Use Azure blob storage for media uploads. +DEFAULT_FILE_STORAGE = 'storages.backends.azure_storage.AzureStorage' +AZURE_ACCOUNT_NAME = env('AZURE_ACCOUNT_NAME', 'name') +AZURE_ACCOUNT_KEY = env('AZURE_ACCOUNT_KEY', 'key') +AZURE_CONTAINER = env('AZURE_CONTAINER', 'container') + +# PRS may deploy its own instance of Geoserver. PRS_GEOSERVER_WMTS_URL = env('PRS_GEOSERVER_WMTS_URL', '') PRS_GEOSERVER_WFS_URL = env('PRS_GEOSERVER_WFS_URL', '') GEOSERVER_WMTS_URL = env('GEOSERVER_WMTS_URL', '') @@ -39,6 +43,7 @@ GEOSERVER_SSO_USER = env('GEOSERVER_SSO_USER', 'username') GEOSERVER_SSO_PASS = env('GEOSERVER_SSO_PASS', 'password') GEOCODER_URL = env('GEOCODER_URL', '') + INSTALLED_APPS = ( 'whitenoise.runserver_nostatic', 'django.contrib.admin', diff --git a/pyproject.toml b/pyproject.toml index 28a5dfae..b7b51d55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ django-celery-results = "2.4.0" pygeopkg = "0.1.3" whitenoise = {version = "6.5.0", extras = ["brotli"]} django-crum = "0.7.9" +django-storages = {version = "1.13.2", extras = ["azure"]} [tool.poetry.group.dev.dependencies] ipython = "^8.10.0"