From 9fb855a9673ca7385c5f976ff17dcf7ddce0b013 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Mon, 2 Jun 2025 11:45:57 +0200 Subject: [PATCH 1/6] Move format methods from Layout to utils --- src/onegov/core/layout.py | 71 ++----------------- src/onegov/core/utils.py | 109 ++++++++++++++++++++++++++--- src/onegov/org/forms/newsletter.py | 27 +++---- 3 files changed, 118 insertions(+), 89 deletions(-) diff --git a/src/onegov/core/layout.py b/src/onegov/core/layout.py index 6c367adb42..18a11d68b7 100644 --- a/src/onegov/core/layout.py +++ b/src/onegov/core/layout.py @@ -1,9 +1,6 @@ from __future__ import annotations -import arrow -import babel.dates -import babel.numbers import isodate import numbers import sedate @@ -13,9 +10,11 @@ from functools import lru_cache from onegov.core import utils from onegov.core.templates import PageTemplate +from onegov.core.utils import format_date, format_number, number_symbols from pytz import timezone from typing import overload, Any, TypeVar, TYPE_CHECKING + if TYPE_CHECKING: from chameleon import PageTemplateFile from collections.abc import Callable, Collection, Iterable, Iterator @@ -154,40 +153,8 @@ def csrf_protected_url(self, url: str) -> str: return self.request.csrf_protected_url(url) def format_date(self, dt: datetime | date | None, format: str) -> str: - """ Takes a datetime and formats it according to local timezone and - the given format. - - """ - if dt is None: - return '' - - if getattr(dt, 'tzinfo', None) is not None: - dt = self.timezone.normalize( - dt.astimezone(self.timezone) # type:ignore[attr-defined] - ) - - locale = self.request.locale - assert locale is not None, 'Cannot format date without a locale' - if format == 'relative': - adt = arrow.get(dt) - - try: - return adt.humanize(locale=locale) - except ValueError: - return adt.humanize(locale=locale.split('_')[0]) - - fmt = getattr(self, format + '_format') - if fmt.startswith('skeleton:'): - return babel.dates.format_skeleton( - fmt.replace('skeleton:', ''), - datetime=dt, - fuzzy=False, - locale=locale - ) - elif hasattr(dt, 'hour'): - return babel.dates.format_datetime(dt, format=fmt, locale=locale) - else: - return babel.dates.format_date(dt, format=fmt, locale=locale) + fmt = getattr(self, f'{format}_format', format) + return format_date(dt, fmt, self.request.locale, self.timezone) def isodate(self, date: datetime) -> str: """ Returns the given date in the ISO 8601 format. """ @@ -200,12 +167,7 @@ def parse_isodate(self, string: str) -> datetime: @staticmethod @lru_cache(maxsize=8) def number_symbols(locale: str) -> tuple[str, str]: - """ Returns the locale specific number symbols. """ - - return ( - babel.numbers.get_decimal_symbol(locale), - babel.numbers.get_group_symbol(locale) - ) + return number_symbols(locale) def format_number( self, @@ -213,27 +175,8 @@ def format_number( decimal_places: int | None = None, padding: str = '' ) -> str: - """ Takes the given numer and formats it according to locale. - - If the number is an integer, the default decimal places are 0, - otherwise 2. - - """ - if isinstance(number, str): - return number - - if number is None: - return '' - - if decimal_places is None: - if isinstance(number, numbers.Integral): - decimal_places = 0 - else: - decimal_places = 2 - - decimal, group = self.number_symbols(self.request.locale) - result = '{{:{},.{}f}}'.format(padding, decimal_places).format(number) - return result.translate({ord(','): group, ord('.'): decimal}) + return format_number( + number, decimal_places, padding, self.request.locale) @property def view_name(self) -> str | None: diff --git a/src/onegov/core/utils.py b/src/onegov/core/utils.py index 2f3588ef41..55e4c259fb 100644 --- a/src/onegov/core/utils.py +++ b/src/onegov/core/utils.py @@ -1,7 +1,11 @@ from __future__ import annotations +import arrow +import babel.dates +import babel.numbers import base64 import bleach +import pytz from urlextract import URLExtract, CacheFileError from bleach.linkifier import TLDS import errno @@ -13,6 +17,7 @@ import magic import mimetypes import morepath +import numbers import operator import os.path import re @@ -29,11 +34,16 @@ from itertools import groupby, islice from markupsafe import escape from markupsafe import Markup + from onegov.core import log from onegov.core.custom import json from onegov.core.errors import AlreadyLockedError -from phonenumbers import (PhoneNumberFormat, format_number, - NumberParseException, parse) +from phonenumbers import ( + PhoneNumberFormat, + format_number as format_phone_number, + NumberParseException, + parse, +) from purl import URL from threading import Thread from time import perf_counter @@ -42,13 +52,17 @@ from webob import static from yubico_client import Yubico # type:ignore[import-untyped] from yubico_client.yubico_exceptions import ( # type:ignore[import-untyped] - SignatureVerificationError, StatusCodeError) - + SignatureVerificationError, + StatusCodeError, +) from typing import overload, Any, TypeVar, TYPE_CHECKING + if TYPE_CHECKING: from _typeshed import SupportsRichComparison from collections.abc import Callable, Collection, Iterator + from datetime import datetime, date + from decimal import Decimal from fs.base import FS, SubFS from re import Match from sqlalchemy import Column @@ -1250,11 +1264,9 @@ def generate_fts_phonenumbers(numbers: Iterable[str | None]) -> list[str]: result.append(number.replace(' ', '')) continue - result.append(format_number( - parsed, PhoneNumberFormat.E164)) + result.append(format_phone_number(parsed, PhoneNumberFormat.E164)) - national = format_number( - parsed, PhoneNumberFormat.NATIONAL) + national = format_phone_number(parsed, PhoneNumberFormat.NATIONAL) groups = national.split() for idx in range(len(groups)): partial = ''.join(groups[idx:]) @@ -1262,3 +1274,84 @@ def generate_fts_phonenumbers(numbers: Iterable[str | None]) -> list[str]: result.append(partial) return result + + +def format_date( + dt: datetime | date | None, + format: str, + locale: str | None = None, + timezone: pytz.BaseTzInfo | None = None, +) -> str: + """Takes a datetime and formats it according to locale, timezone and + the given format. + + """ + if dt is None: + return '' + + locale = locale or 'de_CH' + timezone = timezone or pytz.timezone('Europe/Zurich') + + if getattr(dt, 'tzinfo', None) is not None: + dt = timezone.normalize( + dt.astimezone(timezone) # type:ignore[attr-defined] + ) + + if format == 'relative': + adt = arrow.get(dt) + + try: + return adt.humanize(locale=locale) + except ValueError: + return adt.humanize(locale=locale.split('_')[0]) + + if format.startswith('skeleton:'): + return babel.dates.format_skeleton( + format.replace('skeleton:', ''), + datetime=dt, + fuzzy=False, + locale=locale, + ) + elif hasattr(dt, 'hour'): + return babel.dates.format_datetime(dt, format=format, locale=locale) + else: + return babel.dates.format_date(dt, format=format, locale=locale) + + +def format_number( + number: numbers.Number | Decimal | float | str | None, + decimal_places: int | None = None, + padding: str = '', + locale: str | None = 'de_CH', +) -> str: + """Takes the given numer and formats it according to locale. + + If the number is an integer, the default decimal places are 0, + otherwise 2. + + """ + if isinstance(number, str): + return number + + if number is None: + return '' + + if decimal_places is None: + if isinstance(number, numbers.Integral): + decimal_places = 0 + else: + decimal_places = 2 + + decimal, group = number_symbols(locale) + result = '{{:{},.{}f}}'.format(padding, decimal_places).format(number) + return result.translate({ord(','): group, ord('.'): decimal}) + + +@lru_cache(maxsize=8) +def number_symbols(locale: str) -> tuple[str, str]: + """Returns the locale specific number symbols.""" + + return ( + babel.numbers.get_decimal_symbol(locale), + babel.numbers.get_group_symbol(locale), + ) diff --git a/src/onegov/org/forms/newsletter.py b/src/onegov/org/forms/newsletter.py index 6ab2d5d657..cffc77cb82 100644 --- a/src/onegov/org/forms/newsletter.py +++ b/src/onegov/org/forms/newsletter.py @@ -5,12 +5,13 @@ import transaction from wtforms.validators import DataRequired from onegov.core.csv import convert_excel_to_csv, CSVFile +from onegov.core.utils import format_date from onegov.form.fields import UploadField from onegov.org.forms.fields import HtmlField +from onegov.org.utils import extract_categories_and_subcategories from onegov.form.validators import FileSizeLimit from onegov.form.validators import WhitelistedMimeType from wtforms.fields import BooleanField -from onegov.core.layout import Layout from onegov.file.utils import name_without_extension from onegov.form import Form from onegov.form.fields import ChosenSelectField @@ -29,8 +30,6 @@ from typing import Any, TYPE_CHECKING -from onegov.org.utils import extract_categories_and_subcategories - if TYPE_CHECKING: from collections.abc import Iterable, Callable from onegov.core.csv import DefaultRow @@ -85,10 +84,6 @@ def with_news( news: Iterable[News] ) -> type[Self]: - # FIXME: using a layout just for format_date seems bad, we should - # probably extract these functions into util modules - layout = Layout(None, request) - choices = tuple( ( str(item.id), @@ -97,8 +92,8 @@ def with_news( '
{}
' ).format( item.title, - layout.format_date(item.created, 'relative') - ) + format_date(item.created, 'relative', request.locale), + ), ) for item in news ) @@ -149,9 +144,6 @@ def with_occurrences( occurrences: Iterable[Occurrence] ) -> type[Self]: - # FIXME: another use of layout for format_date - layout = Layout(None, request) - choices = tuple( ( str(item.id), @@ -160,7 +152,11 @@ def with_occurrences( '
{}
' ).format( item.title, - layout.format_date(item.localized_start, 'datetime') + format_date( + item.localized_start, + 'dd.MM.yyyy HH:mm', + request.locale + ) ) ) for item in occurrences @@ -201,9 +197,6 @@ def with_publications( publications: Iterable[File] ) -> type[Self]: - # FIXME: another use of layout for format_date - layout = Layout(None, request) - choices = tuple( ( str(item.id), @@ -212,7 +205,7 @@ def with_publications( '
{}
' ).format( name_without_extension(item.name), - layout.format_date(item.created, 'date') + format_date(item.created, 'dd.MM.yyyy', request.locale) ) ) for item in publications From c8cf16ee81b135eef8c85e49b21a17cb046cfa4f Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Mon, 2 Jun 2025 13:32:40 +0200 Subject: [PATCH 2/6] Undo unecessary changes --- src/onegov/core/utils.py | 10 +++++----- src/onegov/org/forms/newsletter.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/onegov/core/utils.py b/src/onegov/core/utils.py index 55e4c259fb..132c356081 100644 --- a/src/onegov/core/utils.py +++ b/src/onegov/core/utils.py @@ -52,9 +52,7 @@ from webob import static from yubico_client import Yubico # type:ignore[import-untyped] from yubico_client.yubico_exceptions import ( # type:ignore[import-untyped] - SignatureVerificationError, - StatusCodeError, -) + SignatureVerificationError, StatusCodeError) from typing import overload, Any, TypeVar, TYPE_CHECKING @@ -1264,9 +1262,11 @@ def generate_fts_phonenumbers(numbers: Iterable[str | None]) -> list[str]: result.append(number.replace(' ', '')) continue - result.append(format_phone_number(parsed, PhoneNumberFormat.E164)) + result.append(format_phone_number( + parsed, PhoneNumberFormat.E164)) - national = format_phone_number(parsed, PhoneNumberFormat.NATIONAL) + national = format_phone_number( + parsed, PhoneNumberFormat.NATIONAL) groups = national.split() for idx in range(len(groups)): partial = ''.join(groups[idx:]) diff --git a/src/onegov/org/forms/newsletter.py b/src/onegov/org/forms/newsletter.py index cffc77cb82..22e595ad4e 100644 --- a/src/onegov/org/forms/newsletter.py +++ b/src/onegov/org/forms/newsletter.py @@ -93,7 +93,7 @@ def with_news( ).format( item.title, format_date(item.created, 'relative', request.locale), - ), + ) ) for item in news ) From c68fb61e9f9e54ad09d95fc3001a10a1175277b2 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 3 Jun 2025 08:00:39 +0200 Subject: [PATCH 3/6] Put format utils to CoreRequest --- src/onegov/core/layout.py | 12 +--- src/onegov/core/request.py | 87 ++++++++++++++++++++++++++++- src/onegov/core/utils.py | 88 ------------------------------ src/onegov/org/forms/newsletter.py | 12 ++-- 4 files changed, 91 insertions(+), 108 deletions(-) diff --git a/src/onegov/core/layout.py b/src/onegov/core/layout.py index 18a11d68b7..040a069e5a 100644 --- a/src/onegov/core/layout.py +++ b/src/onegov/core/layout.py @@ -7,10 +7,8 @@ from datetime import datetime from functools import cached_property -from functools import lru_cache from onegov.core import utils from onegov.core.templates import PageTemplate -from onegov.core.utils import format_date, format_number, number_symbols from pytz import timezone from typing import overload, Any, TypeVar, TYPE_CHECKING @@ -154,7 +152,7 @@ def csrf_protected_url(self, url: str) -> str: def format_date(self, dt: datetime | date | None, format: str) -> str: fmt = getattr(self, f'{format}_format', format) - return format_date(dt, fmt, self.request.locale, self.timezone) + return self.request.format_date(dt, fmt) def isodate(self, date: datetime) -> str: """ Returns the given date in the ISO 8601 format. """ @@ -164,19 +162,13 @@ def parse_isodate(self, string: str) -> datetime: """ Returns the given ISO 8601 string as datetime. """ return isodate.parse_datetime(string) - @staticmethod - @lru_cache(maxsize=8) - def number_symbols(locale: str) -> tuple[str, str]: - return number_symbols(locale) - def format_number( self, number: numbers.Number | Decimal | float | str | None, decimal_places: int | None = None, padding: str = '' ) -> str: - return format_number( - number, decimal_places, padding, self.request.locale) + return self.request.format_number(number, decimal_places, padding) @property def view_name(self) -> str | None: diff --git a/src/onegov/core/request.py b/src/onegov/core/request.py index 4f4eaa79fd..083882a6f3 100644 --- a/src/onegov/core/request.py +++ b/src/onegov/core/request.py @@ -1,10 +1,16 @@ from __future__ import annotations +import arrow +import babel.dates +import babel.numbers import morepath +import numbers + +import pytz import ua_parser -from datetime import timedelta -from functools import cached_property +from datetime import timedelta, datetime, date +from functools import cached_property, lru_cache from onegov.core.cache import instance_lru_cache from onegov.core.custom import msgpack from onegov.core.utils import append_query_param @@ -29,6 +35,7 @@ if TYPE_CHECKING: from _typeshed import SupportsItems from collections.abc import Callable, Iterable, Iterator, Sequence + from decimal import Decimal from dectate import Sentinel from gettext import GNUTranslations from markupsafe import Markup @@ -874,3 +881,79 @@ def template_loader(self) -> TemplateLoader: """ Returns the chameleon template loader. """ registry = self.app.config.template_engine_registry return registry._template_loaders['.pt'] + + def format_date( + self, + dt: datetime | date | None, + fmt: str, + timezone: pytz.BaseTzInfo | None = None + ) -> str: + """ Takes a datetime and formats it according to local timezone and + the given format. + """ + if dt is None: + return '' + + if getattr(dt, 'tzinfo', None) is not None: + timezone = timezone or pytz.timezone('Europe/Zurich') + dt = timezone.normalize( + dt.astimezone(timezone) # type:ignore[attr-defined] + ) + + locale = self.locale + assert locale is not None, 'Cannot format date without a locale' + if fmt == 'relative': + adt = arrow.get(dt) + + try: + return adt.humanize(locale=locale) + except ValueError: + return adt.humanize(locale=locale.split('_')[0]) + + if fmt.startswith('skeleton:'): + return babel.dates.format_skeleton( + fmt.replace('skeleton:', ''), + datetime=dt, + fuzzy=False, + locale=locale + ) + elif hasattr(dt, 'hour'): + return babel.dates.format_datetime(dt, format=fmt, locale=locale) + else: + return babel.dates.format_date(dt, format=fmt, locale=locale) + + def format_number( + self, + number: numbers.Number | Decimal | float | str | None, + decimal_places: int | None = None, + padding: str = '' + ) -> str: + """ Takes the given numer and formats it according to locale. + If the number is an integer, the default decimal places are 0, + otherwise 2. + """ + if isinstance(number, str): + return number + + if number is None: + return '' + + if decimal_places is None: + if isinstance(number, numbers.Integral): + decimal_places = 0 + else: + decimal_places = 2 + + decimal, group = self.number_symbols(self.locale) + result = '{{:{},.{}f}}'.format(padding, decimal_places).format(number) + return result.translate({ord(','): group, ord('.'): decimal}) + + @staticmethod + @lru_cache(maxsize=8) + def number_symbols(locale: str) -> tuple[str, str]: + """ Returns the locale specific number symbols. """ + + return ( + babel.numbers.get_decimal_symbol(locale), + babel.numbers.get_group_symbol(locale) + ) diff --git a/src/onegov/core/utils.py b/src/onegov/core/utils.py index 132c356081..e9d7e86374 100644 --- a/src/onegov/core/utils.py +++ b/src/onegov/core/utils.py @@ -1,11 +1,7 @@ from __future__ import annotations -import arrow -import babel.dates -import babel.numbers import base64 import bleach -import pytz from urlextract import URLExtract, CacheFileError from bleach.linkifier import TLDS import errno @@ -17,7 +13,6 @@ import magic import mimetypes import morepath -import numbers import operator import os.path import re @@ -59,8 +54,6 @@ if TYPE_CHECKING: from _typeshed import SupportsRichComparison from collections.abc import Callable, Collection, Iterator - from datetime import datetime, date - from decimal import Decimal from fs.base import FS, SubFS from re import Match from sqlalchemy import Column @@ -1274,84 +1267,3 @@ def generate_fts_phonenumbers(numbers: Iterable[str | None]) -> list[str]: result.append(partial) return result - - -def format_date( - dt: datetime | date | None, - format: str, - locale: str | None = None, - timezone: pytz.BaseTzInfo | None = None, -) -> str: - """Takes a datetime and formats it according to locale, timezone and - the given format. - - """ - if dt is None: - return '' - - locale = locale or 'de_CH' - timezone = timezone or pytz.timezone('Europe/Zurich') - - if getattr(dt, 'tzinfo', None) is not None: - dt = timezone.normalize( - dt.astimezone(timezone) # type:ignore[attr-defined] - ) - - if format == 'relative': - adt = arrow.get(dt) - - try: - return adt.humanize(locale=locale) - except ValueError: - return adt.humanize(locale=locale.split('_')[0]) - - if format.startswith('skeleton:'): - return babel.dates.format_skeleton( - format.replace('skeleton:', ''), - datetime=dt, - fuzzy=False, - locale=locale, - ) - elif hasattr(dt, 'hour'): - return babel.dates.format_datetime(dt, format=format, locale=locale) - else: - return babel.dates.format_date(dt, format=format, locale=locale) - - -def format_number( - number: numbers.Number | Decimal | float | str | None, - decimal_places: int | None = None, - padding: str = '', - locale: str | None = 'de_CH', -) -> str: - """Takes the given numer and formats it according to locale. - - If the number is an integer, the default decimal places are 0, - otherwise 2. - - """ - if isinstance(number, str): - return number - - if number is None: - return '' - - if decimal_places is None: - if isinstance(number, numbers.Integral): - decimal_places = 0 - else: - decimal_places = 2 - - decimal, group = number_symbols(locale) - result = '{{:{},.{}f}}'.format(padding, decimal_places).format(number) - return result.translate({ord(','): group, ord('.'): decimal}) - - -@lru_cache(maxsize=8) -def number_symbols(locale: str) -> tuple[str, str]: - """Returns the locale specific number symbols.""" - - return ( - babel.numbers.get_decimal_symbol(locale), - babel.numbers.get_group_symbol(locale), - ) diff --git a/src/onegov/org/forms/newsletter.py b/src/onegov/org/forms/newsletter.py index 22e595ad4e..569e90fa8e 100644 --- a/src/onegov/org/forms/newsletter.py +++ b/src/onegov/org/forms/newsletter.py @@ -5,7 +5,6 @@ import transaction from wtforms.validators import DataRequired from onegov.core.csv import convert_excel_to_csv, CSVFile -from onegov.core.utils import format_date from onegov.form.fields import UploadField from onegov.org.forms.fields import HtmlField from onegov.org.utils import extract_categories_and_subcategories @@ -92,7 +91,7 @@ def with_news( '
{}
' ).format( item.title, - format_date(item.created, 'relative', request.locale), + request.format_date(item.created, 'relative'), ) ) for item in news @@ -152,11 +151,8 @@ def with_occurrences( '
{}
' ).format( item.title, - format_date( - item.localized_start, - 'dd.MM.yyyy HH:mm', - request.locale - ) + request.format_date( + item.localized_start, 'dd.MM.yyyy HH:mm') ) ) for item in occurrences @@ -205,7 +201,7 @@ def with_publications( '
{}
' ).format( name_without_extension(item.name), - format_date(item.created, 'dd.MM.yyyy', request.locale) + request.format_date(item.created, 'dd.MM.yyyy') ) ) for item in publications From b48607dbda83df1e0c1cec02c91987f86dc00689 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 3 Jun 2025 08:01:10 +0200 Subject: [PATCH 4/6] Adjust swissvote usage --- src/onegov/swissvotes/layouts/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onegov/swissvotes/layouts/default.py b/src/onegov/swissvotes/layouts/default.py index 8dcb87ba8f..f0934bd628 100644 --- a/src/onegov/swissvotes/layouts/default.py +++ b/src/onegov/swissvotes/layouts/default.py @@ -211,6 +211,6 @@ def format_number( # Fixes using "," for french locale instead of "." as for german if locale == 'fr_CH': locale = 'de_CH' - decimal, group = self.number_symbols(locale) + decimal, group = self.request.number_symbols(locale) result = '{{:{},.{}f}}'.format(padding, decimal_places).format(number) return result.translate({ord(','): group, ord('.'): decimal}) From ad9e17a4c60f8abac53b694d3cc8fd013cebdd01 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 3 Jun 2025 08:06:27 +0200 Subject: [PATCH 5/6] Revert unnecessary changes --- src/onegov/core/utils.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/onegov/core/utils.py b/src/onegov/core/utils.py index e9d7e86374..2f3588ef41 100644 --- a/src/onegov/core/utils.py +++ b/src/onegov/core/utils.py @@ -29,16 +29,11 @@ from itertools import groupby, islice from markupsafe import escape from markupsafe import Markup - from onegov.core import log from onegov.core.custom import json from onegov.core.errors import AlreadyLockedError -from phonenumbers import ( - PhoneNumberFormat, - format_number as format_phone_number, - NumberParseException, - parse, -) +from phonenumbers import (PhoneNumberFormat, format_number, + NumberParseException, parse) from purl import URL from threading import Thread from time import perf_counter @@ -49,8 +44,8 @@ from yubico_client.yubico_exceptions import ( # type:ignore[import-untyped] SignatureVerificationError, StatusCodeError) -from typing import overload, Any, TypeVar, TYPE_CHECKING +from typing import overload, Any, TypeVar, TYPE_CHECKING if TYPE_CHECKING: from _typeshed import SupportsRichComparison from collections.abc import Callable, Collection, Iterator @@ -1255,10 +1250,10 @@ def generate_fts_phonenumbers(numbers: Iterable[str | None]) -> list[str]: result.append(number.replace(' ', '')) continue - result.append(format_phone_number( + result.append(format_number( parsed, PhoneNumberFormat.E164)) - national = format_phone_number( + national = format_number( parsed, PhoneNumberFormat.NATIONAL) groups = national.split() for idx in range(len(groups)): From 0c2e1cade67501f2392b6be8088850b1e7b35670 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 3 Jun 2025 14:58:37 +0200 Subject: [PATCH 6/6] Adjust tests --- tests/onegov/core/conftest.py | 16 ++++++++++++++++ tests/onegov/core/test_layout.py | 18 ++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/tests/onegov/core/conftest.py b/tests/onegov/core/conftest.py index 880b924cc4..69b4c224e5 100644 --- a/tests/onegov/core/conftest.py +++ b/tests/onegov/core/conftest.py @@ -3,6 +3,7 @@ import transaction import yaml +from tests.onegov.org.conftest import create_org_app from tests.shared import Client from tests.shared.utils import create_app from onegov.core.cli import command_group @@ -174,3 +175,18 @@ def maildir_smtp_app(temporary_directory, maildir, smtp): app.smtp = smtp return app + + +@pytest.fixture(scope='function') +def org_app(request): + yield create_org_app(request, use_elasticsearch=False) + + +@pytest.fixture(scope='function') +def core_request(org_app): + yield org_app.request_class(environ={ + 'PATH_INFO': '/', + 'SERVER_NAME': '', + 'SERVER_PORT': '', + 'SERVER_PROTOCOL': 'https' + }, app=org_app) diff --git a/tests/onegov/core/test_layout.py b/tests/onegov/core/test_layout.py index ef4aa2f5ba..6dc340ba37 100644 --- a/tests/onegov/core/test_layout.py +++ b/tests/onegov/core/test_layout.py @@ -16,10 +16,10 @@ def test_batched(): ] -def test_format_date(): +def test_format_date(core_request): layout = Layout( model=object(), - request=Bunch(app=Bunch(version='1.0', sentry_dsn=None)) + request=core_request ) dt = replace_timezone(datetime(2015, 6, 17, 15, 0), 'Europe/Zurich') @@ -41,10 +41,10 @@ def test_format_date(): assert layout.format_date(dt, 'day_long') == '17. Juni' -def test_format_number(): +def test_format_number(core_request): layout = Layout( model=object(), - request=Bunch(app=Bunch(version='1.0', sentry_dsn=None)) + request=core_request ) layout.request.locale = 'de_CH' @@ -78,10 +78,16 @@ def test_format_number(): assert layout.format_number(None) == "" -def test_relative_date(): +def test_relative_date(core_request): layout = Layout( model=object(), - request=Bunch(locale='en', app=Bunch(version='1.0', sentry_dsn=None)) + request=core_request ) + + # default locale is de_CH + text = layout.format_date(utcnow() - timedelta(seconds=60 * 5), 'relative') + assert text == 'vor 5 Minuten' + + core_request.locale = 'en_US' text = layout.format_date(utcnow() - timedelta(seconds=60 * 5), 'relative') assert text == '5 minutes ago'