diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99265390..b51bf600 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] + python-version: [ '3.10', '3.11', '3.12' ] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/setup diff --git a/Pipfile b/Pipfile index cdc3835c..391f45c0 100644 --- a/Pipfile +++ b/Pipfile @@ -6,7 +6,7 @@ name = "pypi" [packages] boto3 = "*" dj-database-url = "*" -django = "~=4.0" +django = "~=5.0" django-axes = "*" django-filter = "*" django-imagekit = "*" diff --git a/api/tests.py b/api/tests.py index c6a5d6b9..f707e49b 100644 --- a/api/tests.py +++ b/api/tests.py @@ -316,8 +316,8 @@ class FeedingAPITestCase(TestBase.BabyBuddyAPITestCaseBase): { "id": 3, "child": 1, - "start": "2017-11-18T19:00:00-05:00", - "end": "2017-11-18T19:15:00-05:00", + "start": "2017-11-18T09:00:00-05:00", + "end": "2017-11-18T09:15:00-05:00", "duration": "00:15:00", "type": "formula", "method": "bottle", @@ -335,7 +335,7 @@ class FeedingAPITestCase(TestBase.BabyBuddyAPITestCaseBase): def test_get_with_iso_filter(self): response = self.client.get( - self.endpoint, {"start_min": "2017-11-18T11:30:00-05:00"} + self.endpoint, {"start_min": "2017-11-18T04:00:00-05:00"} ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["count"], 3) diff --git a/babybuddy/fixtures/tests.json b/babybuddy/fixtures/tests.json index 2be23b30..f3fc938b 100644 --- a/babybuddy/fixtures/tests.json +++ b/babybuddy/fixtures/tests.json @@ -249,8 +249,8 @@ "fields": { "child": 1, - "start": "2017-11-18T14:00:00-05:00", - "end": "2017-11-18T14:30:00-05:00", + "start": "2017-11-18T09:00:00Z", + "end": "2017-11-18T09:30:00Z", "duration": "00:30:00", "type": "breast milk", "method": "left breast", @@ -263,8 +263,8 @@ "fields": { "child": 1, - "start": "2017-11-18T16:30:00-05:00", - "end": "2017-11-18T17:00:00-05:00", + "start": "2017-11-18T11:30:00Z", + "end": "2017-11-18T12:00:00Z", "duration": "00:30:00", "type": "breast milk", "method": "right breast", @@ -277,8 +277,8 @@ "fields": { "child": 1, - "start": "2017-11-18T19:00:00-05:00", - "end": "2017-11-18T19:15:00-05:00", + "start": "2017-11-18T14:00:00Z", + "end": "2017-11-18T14:15:00Z", "duration": "00:15:00", "type": "formula", "method": "bottle", @@ -292,8 +292,8 @@ "fields": { "child": 1, - "start": "2017-11-17T19:00:00-05:00", - "end": "2017-11-17T19:15:00-05:00", + "start": "2017-11-17T14:00:00Z", + "end": "2017-11-17T14:15:00Z", "duration": "00:15:00", "type": "formula", "method": "bottle", @@ -307,8 +307,8 @@ "fields": { "child": 1, - "start": "2017-11-11T19:00:00-05:00", - "end": "2017-11-11T19:15:00-05:00", + "start": "2017-11-11T14:00:00Z", + "end": "2017-11-11T14:15:00Z", "duration": "00:15:00", "type": "formula", "method": "bottle", @@ -322,8 +322,8 @@ "fields": { "child": 1, - "start": "2017-11-11T00:00:00-05:00", - "end": "2017-11-11T00:15:00-05:00", + "start": "2017-11-11T05:00:00Z", + "end": "2017-11-11T05:15:00Z", "duration": "00:15:00", "type": "formula", "method": "bottle", diff --git a/babybuddy/middleware.py b/babybuddy/middleware.py index 924952b7..e07039eb 100644 --- a/babybuddy/middleware.py +++ b/babybuddy/middleware.py @@ -2,7 +2,6 @@ from os import getenv from time import time from functools import wraps -import pytz from urllib.parse import urlunsplit, urlsplit from django.conf import settings @@ -55,8 +54,8 @@ class UserTimezoneMiddleware: user = request.user if hasattr(user, "settings") and user.settings.timezone: try: - timezone.activate(pytz.timezone(user.settings.timezone)) - except pytz.UnknownTimeZoneError: + timezone.activate(user.settings.timezone) + except ValueError: pass return self.get_response(request) diff --git a/babybuddy/models.py b/babybuddy/models.py index 6ca19159..8e9a461a 100644 --- a/babybuddy/models.py +++ b/babybuddy/models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import pytz +import zoneinfo from django.conf import settings from django.contrib.auth import get_user_model @@ -63,7 +63,9 @@ class Settings(models.Model): verbose_name=_("Language"), ) timezone = models.CharField( - choices=tuple(zip(pytz.common_timezones, pytz.common_timezones)), + choices=sorted( + tuple(zip(zoneinfo.available_timezones(), zoneinfo.available_timezones())) + ), default=timezone.get_default_timezone_name(), max_length=100, verbose_name=_("Timezone"), diff --git a/babybuddy/settings/base.py b/babybuddy/settings/base.py index a517c435..93ad7c59 100644 --- a/babybuddy/settings/base.py +++ b/babybuddy/settings/base.py @@ -20,7 +20,7 @@ DEBUG = bool(strtobool(os.environ.get("DEBUG") or "False")) # Applications -# https://docs.djangoproject.com/en/4.0/ref/applications/ +# https://docs.djangoproject.com/en/5.0/ref/applications/ INSTALLED_APPS = [ "api", @@ -48,7 +48,7 @@ INSTALLED_APPS = [ ] # Middleware -# https://docs.djangoproject.com/en/4.0/ref/middleware/ +# https://docs.djangoproject.com/en/5.0/ref/middleware/ MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", @@ -69,13 +69,13 @@ MIDDLEWARE = [ # URL dispatcher -# https://docs.djangoproject.com/en/4.0/topics/http/urls/ +# https://docs.djangoproject.com/en/5.0/topics/http/urls/ ROOT_URLCONF = "babybuddy.urls" # Templates -# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-TEMPLATES +# https://docs.djangoproject.com/en/5.0/ref/settings/#std:setting-TEMPLATES TEMPLATES = [ { @@ -95,7 +95,7 @@ TEMPLATES = [ # Database -# https://docs.djangoproject.com/en/4.0/ref/settings/#databases +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases config = { "ENGINE": os.getenv("DB_ENGINE") or "django.db.backends.sqlite3", @@ -118,7 +118,7 @@ DATABASES = {"default": config} # Cache -# https://docs.djangoproject.com/en/4.0/topics/cache/ +# https://docs.djangoproject.com/en/5.0/topics/cache/ CACHES = { "default": { @@ -129,13 +129,13 @@ CACHES = { # WGSI -# https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +# https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ WSGI_APPLICATION = "babybuddy.wsgi.application" # Authentication -# https://docs.djangoproject.com/en/4.0/topics/auth/default/ +# https://docs.djangoproject.com/en/5.0/topics/auth/default/ AUTHENTICATION_BACKENDS = [ "axes.backends.AxesBackend", @@ -158,14 +158,14 @@ if REVERSE_PROXY_AUTH: # Timezone -# https://docs.djangoproject.com/en/4.0/topics/i18n/timezones/ +# https://docs.djangoproject.com/en/5.0/topics/i18n/timezones/ USE_TZ = True TIME_ZONE = "UTC" # Internationalization -# https://docs.djangoproject.com/en/4.0/topics/i18n/ +# https://docs.djangoproject.com/en/5.0/topics/i18n/ USE_I18N = True @@ -199,18 +199,26 @@ LANGUAGES = [ # Format localization -# https://docs.djangoproject.com/en/4.0/topics/i18n/formatting/ - -USE_L10N = True +# https://docs.djangoproject.com/en/5.0/topics/i18n/formatting/ FORMAT_MODULE_PATH = ["babybuddy.formats"] -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.0/howto/static-files/ -# http://whitenoise.evans.io/en/stable/django.html +# Storage +# https://docs.djangoproject.com/en/5.0/ref/files/storage/ +# https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STORAGES -STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" +STORAGES = { + "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"}, + "staticfiles": { + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", + }, +} + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ +# http://whitenoise.evans.io/en/stable/django.html STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder", @@ -225,7 +233,7 @@ WHITENOISE_ROOT = os.path.join(BASE_DIR, "static", "babybuddy", "root") # Media files (User uploaded content) -# https://docs.djangoproject.com/en/4.0/topics/files/ +# https://docs.djangoproject.com/en/5.0/topics/files/ MEDIA_ROOT = os.path.join(BASE_DIR, "media") @@ -240,11 +248,11 @@ AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") or None AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL") or None if AWS_STORAGE_BUCKET_NAME: - DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" + STORAGES["default"]["BACKEND"] = "storages.backends.s3boto3.S3Boto3Storage" # Email -# https://docs.djangoproject.com/en/4.0/topics/email/ +# https://docs.djangoproject.com/en/5.0/topics/email/ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" EMAIL_SUBJECT_PREFIX = "[Baby Buddy] " @@ -263,17 +271,17 @@ if os.environ.get("EMAIL_HOST"): # Security -# https://docs.djangoproject.com/en/4.0/ref/settings/#secure-proxy-ssl-header +# https://docs.djangoproject.com/en/5.0/ref/settings/#secure-proxy-ssl-header if os.environ.get("SECURE_PROXY_SSL_HEADER"): SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") -# https://docs.djangoproject.com/en/4.0/topics/http/sessions/#settings +# https://docs.djangoproject.com/en/5.0/topics/http/sessions/#settings SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SECURE = bool( strtobool(os.environ.get("SESSION_COOKIE_SECURE") or "False") ) -# https://docs.djangoproject.com/en/4.0/ref/csrf/#settings +# https://docs.djangoproject.com/en/5.0/ref/csrf/#settings CSRF_COOKIE_HTTPONLY = True CSRF_COOKIE_SECURE = bool(strtobool(os.environ.get("CSRF_COOKIE_SECURE") or "False")) CSRF_FAILURE_VIEW = "babybuddy.views.csrf_failure" @@ -282,7 +290,7 @@ CSRF_TRUSTED_ORIGINS = list( ) -# https://docs.djangoproject.com/en/4.0/topics/auth/passwords/ +# https://docs.djangoproject.com/en/5.0/topics/auth/passwords/ AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", @@ -349,7 +357,7 @@ AXES_LOCKOUT_URL = "/login/lock" ROLLING_SESSION_REFRESH = 86400 # Set default auto field for models. -# See https://docs.djangoproject.com/en/4.0/releases/3.2/#customizing-type-of-auto-created-primary-keys +# See https://docs.djangoproject.com/en/5.0/releases/3.2/#customizing-type-of-auto-created-primary-keys DEFAULT_AUTO_FIELD = "django.db.models.AutoField" diff --git a/babybuddy/settings/ci.py b/babybuddy/settings/ci.py index 0c3990e4..5a5e3f97 100644 --- a/babybuddy/settings/ci.py +++ b/babybuddy/settings/ci.py @@ -4,6 +4,8 @@ SECRET_KEY = "CISECRETKEYIGUESS" # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.0/howto/static-files/ +# https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage" +STORAGES["staticfiles"][ + "BACKEND" +] = "django.contrib.staticfiles.storage.StaticFilesStorage" diff --git a/babybuddy/settings/development.py b/babybuddy/settings/development.py index 252de2db..4124b2bb 100644 --- a/babybuddy/settings/development.py +++ b/babybuddy/settings/development.py @@ -1,20 +1,22 @@ from .base import * # Quick-start development settings - unsuitable for production -# https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ +# https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ SECRET_KEY = os.environ.get("SECRET_KEY") or "DEVELOPMENT!!" DEBUG = bool(strtobool(os.environ.get("DEBUG") or "True")) # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.0/howto/static-files/ +# https://docs.djangoproject.com/en/5.0/howto/static-files/ # -# Comment out STATICFILES_STORAGE and uncomment DEBUG = False to test with -# production static files. +# Comment out STORAGES["staticfiles"]["BACKEND"] and uncomment DEBUG = False to +# test with production static files. # DEBUG = False -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage" +STORAGES["staticfiles"][ + "BACKEND" +] = "django.contrib.staticfiles.storage.StaticFilesStorage" # Django Rest Framework diff --git a/babybuddy/settings/gitpod.py b/babybuddy/settings/gitpod.py index 174c80f7..166ee933 100644 --- a/babybuddy/settings/gitpod.py +++ b/babybuddy/settings/gitpod.py @@ -1,7 +1,7 @@ from .development import * # CSRF configuration -# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS +# https://docs.djangoproject.com/en/5.0/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS # https://www.gitpod.io/docs/environment-variables/#default-environment-variables CSRF_TRUSTED_ORIGINS = [ diff --git a/babybuddy/settings/production.example.py b/babybuddy/settings/production.example.py index 0d838e5b..054b0803 100644 --- a/babybuddy/settings/production.example.py +++ b/babybuddy/settings/production.example.py @@ -8,7 +8,7 @@ SECRET_KEY = "" ALLOWED_HOSTS = [""] # Database -# https://docs.djangoproject.com/en/4.0/ref/settings/#databases +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases DATABASES = { "default": { @@ -18,7 +18,7 @@ DATABASES = { } # Media files -# https://docs.djangoproject.com/en/4.0/topics/files/ +# https://docs.djangoproject.com/en/5.0/topics/files/ MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media") @@ -26,8 +26,8 @@ MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media") # After setting up SSL, uncomment the settings below for enhanced security of # application cookies. # -# See https://docs.djangoproject.com/en/4.0/topics/http/sessions/#settings -# See https://docs.djangoproject.com/en/4.0/ref/csrf/#settings +# See https://docs.djangoproject.com/en/5.0/topics/http/sessions/#settings +# See https://docs.djangoproject.com/en/5.0/ref/csrf/#settings # SESSION_COOKIE_SECURE = True # CSRF_COOKIE_SECURE = True diff --git a/babybuddy/settings/test.py b/babybuddy/settings/test.py index d5672d8d..d55788d2 100644 --- a/babybuddy/settings/test.py +++ b/babybuddy/settings/test.py @@ -3,15 +3,15 @@ from .base import * SECRET_KEY = "TESTS" # Password hasher configuration -# See https://docs.djangoproject.com/en/4.0/ref/settings/#password-hashers -# See https://docs.djangoproject.com/en/4.0/topics/testing/overview/#password-hashing +# See https://docs.djangoproject.com/en/5.0/ref/settings/#password-hashers +# See https://docs.djangoproject.com/en/5.0/topics/testing/overview/#password-hashing PASSWORD_HASHERS = [ "django.contrib.auth.hashers.MD5PasswordHasher", ] # Email -# https://docs.djangoproject.com/en/4.0/topics/email/ +# https://docs.djangoproject.com/en/5.0/topics/email/ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" diff --git a/babybuddy/tests/tests_forms.py b/babybuddy/tests/tests_forms.py index 467b77ce..47ccf073 100644 --- a/babybuddy/tests/tests_forms.py +++ b/babybuddy/tests/tests_forms.py @@ -65,8 +65,7 @@ class FormsTestCase(TestCase): page = self.c.post("/user/password/", params) self.assertEqual(page.status_code, 200) self.assertFormError( - page, - "form", + page.context["form"], "old_password", "Your old password was entered incorrectly. " "Please enter it again.", ) @@ -75,7 +74,9 @@ class FormsTestCase(TestCase): page = self.c.post("/user/password/", params) self.assertEqual(page.status_code, 200) self.assertFormError( - page, "form", "new_password2", "The two password fields didn’t match." + page.context["form"], + "new_password2", + "The two password fields didn’t match.", ) params["new_password2"] = "mynewpassword" @@ -101,7 +102,7 @@ class FormsTestCase(TestCase): page = self.c.post("/users/{}/delete/".format(new_user.id)) self.assertEqual(page.status_code, 302) - self.assertQuerysetEqual( + self.assertQuerySetEqual( get_user_model().objects.filter(username="username"), [] ) @@ -212,7 +213,9 @@ class FormsTestCase(TestCase): page = self.c.post("/user/settings/", params) self.assertEqual(page.status_code, 200) - self.assertFormError(page, "user_form", "email", "Enter a valid email address.") + self.assertFormError( + page.context["user_form"], "email", "Enter a valid email address." + ) def test_user_settings_language(self): self.c.login(**self.credentials) diff --git a/core/tests/tests_forms.py b/core/tests/tests_forms.py index 68ca75ef..3883c72b 100644 --- a/core/tests/tests_forms.py +++ b/core/tests/tests_forms.py @@ -210,7 +210,7 @@ class ChildFormsTestCase(FormsTestCaseBase): ) self.assertEqual(page.status_code, 200) self.assertFormError( - page, "form", "confirm_name", "Name does not match child name." + page.context["form"], "confirm_name", "Name does not match child name." ) params["confirm_name"] = str(self.child) @@ -798,7 +798,9 @@ class ValidationsTestCase(FormsTestCaseBase): page = self.c.post("/weight/{}/".format(entry.id), params, follow=True) self.assertEqual(page.status_code, 200) - self.assertFormError(page, "form", "date", "Date can not be in the future.") + self.assertFormError( + page.context["form"], "date", "Date can not be in the future." + ) def test_validate_duration(self): end = timezone.localtime() - timezone.timedelta(minutes=10) @@ -813,14 +815,14 @@ class ValidationsTestCase(FormsTestCaseBase): page = self.c.post("/tummy-time/add/", params, follow=True) self.assertEqual(page.status_code, 200) self.assertFormError( - page, "form", None, "Start time must come before end time." + page.context["form"], None, "Start time must come before end time." ) start = end - timezone.timedelta(weeks=53) params["start"] = self.localtime_string(start) page = self.c.post("/tummy-time/add/", params, follow=True) self.assertEqual(page.status_code, 200) - self.assertFormError(page, "form", None, "Duration too long.") + self.assertFormError(page.context["form"], None, "Duration too long.") def test_validate_time(self): future = timezone.localtime() + timezone.timedelta(hours=1) @@ -833,7 +835,9 @@ class ValidationsTestCase(FormsTestCaseBase): page = self.c.post("/tummy-time/add/", params, follow=True) self.assertEqual(page.status_code, 200) - self.assertFormError(page, "form", "end", "Date/time can not be in the future.") + self.assertFormError( + page.context["form"], "end", "Date/time can not be in the future." + ) def test_validate_unique_period(self): entry = models.TummyTime.objects.create( @@ -854,7 +858,9 @@ class ValidationsTestCase(FormsTestCaseBase): page = self.c.post("/tummy-time/add/", params, follow=True) self.assertEqual(page.status_code, 200) self.assertFormError( - page, "form", None, "Another entry intersects the specified time period." + page.context["form"], + None, + "Another entry intersects the specified time period.", ) diff --git a/core/tests/tests_import_export.py b/core/tests/tests_import_export.py index a106fa89..042b4c64 100644 --- a/core/tests/tests_import_export.py +++ b/core/tests/tests_import_export.py @@ -24,8 +24,9 @@ class ImportTestCase(TestCase): ).save() def get_dataset(self, model_name): - file = open(self.base_path + model_name + ".csv") - return tablib.Dataset().load(file.read()) + with open(self.base_path + model_name + ".csv", "r") as f: + data = f.read() + return tablib.Dataset().load(data) def import_data(self, model, count): dataset = self.get_dataset(model._meta.model_name) @@ -86,7 +87,7 @@ class ImportTestCase(TestCase): ] for pk, tags in tests: entry = models.Temperature.objects.get(pk=pk) - self.assertQuerysetEqual(entry.tags.names(), tags, ordered=False) + self.assertQuerySetEqual(entry.tags.names(), tags, ordered=False) def test_temperature(self): self.import_data(models.Temperature, 23) diff --git a/dashboard/tests/tests_templatetags.py b/dashboard/tests/tests_templatetags.py index 612afbf3..4889eeb7 100644 --- a/dashboard/tests/tests_templatetags.py +++ b/dashboard/tests/tests_templatetags.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -import pytz - from django.contrib.auth import get_user_model from django.test import TestCase from django.utils import timezone @@ -28,7 +26,7 @@ class TemplateTagsTestCase(TestCase): # Ensure timezone matches the one defined by fixtures. user_timezone = Settings.objects.first().timezone - timezone.activate(pytz.timezone(user_timezone)) + timezone.activate(user_timezone) # Test file data uses a basis date of 2017-11-18. date = timezone.localtime().strptime("2017-11-18", "%Y-%m-%d") @@ -160,9 +158,11 @@ class TemplateTagsTestCase(TestCase): def test_card_feeding_recent(self): data = cards.card_feeding_recent(self.context, self.child, self.date) + self.assertEqual(data["type"], "feeding") self.assertFalse(data["empty"]) self.assertFalse(data["hide_empty"]) + # most recent day self.assertEqual(data["feedings"][0]["total"], 2.5) self.assertEqual(data["feedings"][0]["count"], 3) @@ -273,7 +273,7 @@ class TemplateTagsTestCase(TestCase): }, { "type": "duration", - "stat": timezone.timedelta(days=1, seconds=46980), + "stat": timezone.timedelta(days=1, seconds=39780), "title": "Feeding frequency", }, { diff --git a/docs/contributing/development-environment.md b/docs/contributing/development-environment.md index 64449952..4e9fb263 100644 --- a/docs/contributing/development-environment.md +++ b/docs/contributing/development-environment.md @@ -7,7 +7,7 @@ information and steps below to set up a local development environment for Baby B ## Requirements -- Python 3.8+, pip, pipenv +- Python 3.10+, pip, pipenv - NodeJS 18.x and NPM 8.x (NVM recommended) - Gulp - Possibly `libpq-dev` diff --git a/docs/setup/deployment.md b/docs/setup/deployment.md index c4ce10f5..8c1fd84c 100644 --- a/docs/setup/deployment.md +++ b/docs/setup/deployment.md @@ -117,14 +117,14 @@ requirements are Python, a web server, an application server, and a database. ### Requirements -- Python 3.8+, pip, pipenv +- Python 3.10+, pip, pipenv - Web server ([nginx](http://nginx.org/), [Apache](http://httpd.apache.org/), etc.) - Application server ([uwsgi](http://projects.unbit.it/uwsgi), [gunicorn](http://gunicorn.org/), etc.) - Database (See [Django's databases documentation](https://docs.djangoproject.com/en/4.2/ref/databases/)). ### Example deployment -*This example assumes a 1 GB VPS instance with Ubuntu 20.04.* It uses Python 3.8, +*This example assumes a 1 GB VPS instance with Ubuntu 20.04.* It uses Python 3.10, nginx, uwsgi and sqlite. It should be sufficient for a few users (e.g., two parents and any number of children). diff --git a/gulpfile.js b/gulpfile.js index 31ac236a..4eb6c31c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -202,6 +202,7 @@ function test(cb) { let command = [ 'run', 'python', + '-Wa', 'manage.py', 'test', '--settings=babybuddy.settings.test', diff --git a/reports/tests/graphs/tests_sleep_pattern.py b/reports/tests/graphs/tests_sleep_pattern.py index 867eb298..59a4858e 100644 --- a/reports/tests/graphs/tests_sleep_pattern.py +++ b/reports/tests/graphs/tests_sleep_pattern.py @@ -23,8 +23,8 @@ class SleepPatternTestCase(TestCase): models.Sleep.objects.create( child=c, - start=dt.datetime(2000, 1, 1, 0, 0, tzinfo=timezone.utc), - end=dt.datetime(2000, 1, 1, 0, 1, tzinfo=timezone.utc), + start=dt.datetime(2000, 1, 1, 0, 0, tzinfo=dt.timezone.utc), + end=dt.datetime(2000, 1, 1, 0, 1, tzinfo=dt.timezone.utc), ) sleep_pattern(models.Sleep.objects.order_by("start")) diff --git a/requirements.txt b/requirements.txt index 9d1c7427..f093d35f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,25 @@ -i https://pypi.python.org/simple annotated-types==0.6.0; python_version >= '3.8' asgiref==3.7.2; python_version >= '3.7' -boto3==1.33.10; python_version >= '3.7' -botocore==1.33.10; python_version >= '3.7' +boto3==1.34.14; python_version >= '3.8' +botocore==1.34.14; python_version >= '3.8' defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff-match-patch==20230430; python_version >= '3.7' dj-database-url==2.1.0 -django==4.2.8; python_version >= '3.8' +django==5.0.1; python_version >= '3.10' django-appconf==1.0.6; python_version >= '3.7' -django-axes==6.1.1; python_version >= '3.7' +django-axes==6.3.0; python_version >= '3.7' django-dbsettings==1.3.0 django-filter==23.5; python_version >= '3.7' django-imagekit==5.0.0 -django-import-export==3.3.3; python_version >= '3.8' -django-qr-code==3.1.1; python_version >= '3.7' +django-import-export==3.3.5; python_version >= '3.8' +django-qr-code==4.0.0; python_version >= '3.7' django-storages==1.14.2; python_version >= '3.7' django-taggit==5.0.1; python_version >= '3.8' django-widget-tweaks==1.5.0; python_version >= '3.8' djangorestframework==3.14.0; python_version >= '3.6' et-xmlfile==1.1.0; python_version >= '3.6' -faker==20.1.0; python_version >= '3.8' +faker==22.0.0; python_version >= '3.8' gunicorn==21.2.0; python_version >= '3.5' jmespath==1.0.1; python_version >= '3.7' markuppy==1.14 @@ -27,23 +27,23 @@ odfpy==1.4.1 openpyxl==3.1.2 packaging==23.2; python_version >= '3.7' pilkit==3.0 -pillow==10.1.0; python_version >= '3.8' +pillow==10.2.0; python_version >= '3.8' plotly==5.18.0; python_version >= '3.6' psycopg2-binary==2.9.9; python_version >= '3.7' -pydantic==2.5.2; python_version >= '3.7' -pydantic-core==2.14.5; python_version >= '3.7' +pydantic==2.5.3; python_version >= '3.7' +pydantic-core==2.14.6; python_version >= '3.7' python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-dotenv==1.0.0; python_version >= '3.8' pytz==2023.3.post1 pyyaml==6.0.1; python_version >= '3.6' -s3transfer==0.8.2; python_version >= '3.7' +s3transfer==0.10.0; python_version >= '3.8' segno==1.6.0; python_version >= '3.5' -setuptools==69.0.2; python_version >= '3.8' +setuptools==69.0.3; python_version >= '3.8' six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' sqlparse==0.4.4; python_version >= '3.5' tablib[html,ods,xls,xlsx,yaml]==3.5.0; python_version >= '3.8' tenacity==8.2.3; python_version >= '3.7' -typing-extensions==4.8.0; python_version >= '3.8' +typing-extensions==4.9.0; python_version >= '3.8' uritemplate==4.1.1; python_version >= '3.6' urllib3==2.0.7; python_version >= '3.10' whitenoise==6.6.0; python_version >= '3.8'