From d74f35d765a6e90b06e893219b237959406685cf Mon Sep 17 00:00:00 2001 From: Christopher Charbonneau Wells <10456740+cdubz@users.noreply.github.com> Date: Fri, 12 Nov 2021 10:13:08 -0800 Subject: [PATCH] Handle format customization in middelware (#332) This is done to allow for format difference between `en-US` (`en`) and `en-GB` that are not well supported by Django's format handling. --- .github/workflows/ci.yml | 18 +++---- babybuddy/formats/en/__init__.py | 0 babybuddy/formats/en/formats.py | 34 -------------- babybuddy/middleware.py | 47 +++++++++++++++++++ babybuddy/settings/base.py | 3 +- babybuddy/{ => tests}/formats/__init__.py | 0 .../tests_en_us.py} | 7 ++- gulpfile.config.js | 2 +- 8 files changed, 64 insertions(+), 47 deletions(-) delete mode 100644 babybuddy/formats/en/__init__.py delete mode 100644 babybuddy/formats/en/formats.py rename babybuddy/{ => tests}/formats/__init__.py (100%) rename babybuddy/tests/{tests_formats.py => formats/tests_en_us.py} (92%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bab11f48..38e040bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,16 +30,16 @@ jobs: - name: Install pipenv run: | python -m pip install --upgrade pipenv wheel - - name: Cache Python dependencies - id: cache-pipenv - uses: actions/cache@v2 - with: - path: ./.venv - key: ${{ runner.os }}-python-${{ matrix.python-version }}-pipenv-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-python-${{ matrix.python-version }}-pipenv- +# - name: Cache Python dependencies +# id: cache-pipenv +# uses: actions/cache@v2 +# with: +# path: ./.venv +# key: ${{ runner.os }}-python-${{ matrix.python-version }}-pipenv-${{ hashFiles('**/requirements.txt') }} +# restore-keys: | +# ${{ runner.os }}-python-${{ matrix.python-version }}-pipenv- - name: Install dependencies - if: steps.cache-pipenv.outputs.cache-hit != 'true' +# if: steps.cache-pipenv.outputs.cache-hit != 'true' run: | pipenv install --dev - run: gulp lint diff --git a/babybuddy/formats/en/__init__.py b/babybuddy/formats/en/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/babybuddy/formats/en/formats.py b/babybuddy/formats/en/formats.py deleted file mode 100644 index bc4eef83..00000000 --- a/babybuddy/formats/en/formats.py +++ /dev/null @@ -1,34 +0,0 @@ -from django.conf import settings -from django.conf.locale.en import formats as formats_us -from django.conf.locale.en_GB import formats as formats_gb - -# Override the regular locale settings to support 24 hour time. -if settings.USE_24_HOUR_TIME_FORMAT: - DATETIME_FORMAT = 'N j, Y, H:i:s' - CUSTOM_INPUT_FORMATS = [ - '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' - '%m/%d/%Y %H:%M', # '10/25/2006 14:30' - ] - SHORT_DATETIME_FORMAT = 'm/d/Y G:i:s' - TIME_FORMAT = 'H:i:s' -else: - # These formats are added to support the locale style of Baby Buddy's - # frontend library, which uses momentjs. - CUSTOM_INPUT_FORMATS = [ - '%m/%d/%Y %I:%M:%S %p', # '10/25/2006 2:30:59 PM' - '%m/%d/%Y %I:%M %p', # '10/25/2006 2:30 PM' - ] - -# Add custom "short" version of `MONTH_DAY_FORMAT`. This customization will -# only work with the locale format locale specified by this file. -SHORT_MONTH_DAY_FORMAT = 'M j' - -# Combine en_US (en) and en_GB formats. This seems to be necessary because when -# an en language variation is used this file is still loaded and there is no -# way to implement e.g. an en_US format. Combining these formats allows the app -# to support both en (en_US) and en_GB without enforcing US-centric input -# formats on users with a en_GB setting. -formats = formats_us.DATETIME_INPUT_FORMATS + formats_gb.DATETIME_INPUT_FORMATS - -# Append all other input formats from the base locale. -DATETIME_INPUT_FORMATS = CUSTOM_INPUT_FORMATS + formats diff --git a/babybuddy/middleware.py b/babybuddy/middleware.py index c6d5c38b..a00b72c7 100644 --- a/babybuddy/middleware.py +++ b/babybuddy/middleware.py @@ -4,6 +4,53 @@ import pytz from django.conf import settings from django.utils import timezone +from django.conf.locale.en import formats as formats_en_us + + +def update_en_us_date_formats(): + """ + Update the datetime formats for the en-US locale. This is handled here and + not using `FORMAT_MODULE_PATH` because the processing of format modules + does not allow us to distinguish appropriately between en-US and en-GB + based on user settings. + """ + if settings.USE_24_HOUR_TIME_FORMAT: + formats_en_us.DATETIME_FORMAT = 'N j, Y, H:i:s' + custom_input_formats = [ + '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' + '%m/%d/%Y %H:%M', # '10/25/2006 14:30' + ] + formats_en_us.SHORT_DATETIME_FORMAT = 'm/d/Y G:i:s' + formats_en_us.TIME_FORMAT = 'H:i:s' + else: + # These formats are added to support the locale style of Baby Buddy's + # frontend library, which uses momentjs. + custom_input_formats = [ + '%m/%d/%Y %I:%M:%S %p', # '10/25/2006 2:30:59 PM' + '%m/%d/%Y %I:%M %p', # '10/25/2006 2:30 PM' + ] + + # Add custom "short" version of `MONTH_DAY_FORMAT`. + formats_en_us.SHORT_MONTH_DAY_FORMAT = 'M j' + + # Append all other input formats from the base locale. + formats_en_us.DATETIME_INPUT_FORMATS = \ + custom_input_formats + formats_en_us.DATETIME_INPUT_FORMATS + + +class UserLanguageMiddleware: + """ + Customizes settings based on user language setting. + """ + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + user = request.user + if hasattr(user, 'settings') and user.settings.language == 'en-US': + update_en_us_date_formats() + + return self.get_response(request) class UserTimezoneMiddleware: diff --git a/babybuddy/settings/base.py b/babybuddy/settings/base.py index d22f842c..21c75b57 100644 --- a/babybuddy/settings/base.py +++ b/babybuddy/settings/base.py @@ -64,6 +64,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'babybuddy.middleware.UserTimezoneMiddleware', 'django.middleware.locale.LocaleMiddleware', + 'babybuddy.middleware.UserLanguageMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', @@ -196,8 +197,6 @@ USE_L10N = True USE_24_HOUR_TIME_FORMAT = strtobool(os.environ.get('USE_24_HOUR_TIME_FORMAT') or 'False') -FORMAT_MODULE_PATH = ['babybuddy.formats'] - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ diff --git a/babybuddy/formats/__init__.py b/babybuddy/tests/formats/__init__.py similarity index 100% rename from babybuddy/formats/__init__.py rename to babybuddy/tests/formats/__init__.py diff --git a/babybuddy/tests/tests_formats.py b/babybuddy/tests/formats/tests_en_us.py similarity index 92% rename from babybuddy/tests/tests_formats.py rename to babybuddy/tests/formats/tests_en_us.py index 34ee06aa..e05e98d4 100644 --- a/babybuddy/tests/tests_formats.py +++ b/babybuddy/tests/formats/tests_en_us.py @@ -6,9 +6,12 @@ from django.forms.fields import DateTimeField from django.test import TestCase, override_settings, tag from django.utils.formats import date_format, time_format +from babybuddy.middleware import update_en_us_date_formats + class FormatsTestCase(TestCase): def test_datetime_input_formats(self): + update_en_us_date_formats() field = DateTimeField() supported_custom_examples = [ '01/20/2020 9:30 AM', @@ -29,7 +32,8 @@ class FormatsTestCase(TestCase): @tag('isolate') @override_settings(LANGUAGE_CODE='en-US', USE_24_HOUR_TIME_FORMAT=True) - def test_use_24_hour_time_format_en(self): + def test_use_24_hour_time_format(self): + update_en_us_date_formats() field = DateTimeField() supported_custom_examples = [ '10/25/2006 2:30:59', @@ -62,6 +66,7 @@ class FormatsTestCase(TestCase): self.assertEqual(time_format(t), '16:02:25') def test_short_month_day_format(self): + update_en_us_date_formats() dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5, second=5) self.assertEqual(date_format(dt, 'SHORT_MONTH_DAY_FORMAT'), 'Jul 31') diff --git a/gulpfile.config.js b/gulpfile.config.js index 0a85076a..581f677a 100644 --- a/gulpfile.config.js +++ b/gulpfile.config.js @@ -77,7 +77,7 @@ module.exports = { }, testsConfig: { isolated: [ - 'babybuddy.tests.tests_formats.FormatsTestCase.test_use_24_hour_time_format_en' + 'babybuddy.tests.formats.tests_en_us.FormatsTestCase.test_use_24_hour_time_format' ], }, watchConfig: {